Segurança da aplicação web

SQL injection, XSS, CSRF, autorização e palavras-passe, com exemplos de defesa por contexto.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

O email recebido no login é texto controlado pelo cliente. Se a aplicação o concatenar numa consulta, esse texto pode passar a ser sintaxe SQL. A defesa depende de onde a entrada é interpretada. Nos pedidos sobre bilhetes, é preciso verificar também quem pede e se pode operar sobre aquele objeto. A validação no navegador pode ser contornada.

SQL injection: separar código de valores

Construir SQL por concatenação permite que um valor passe a ser sintaxe:

// Vulnerável: a entrada faz parte do texto da instrução.
$sql = "SELECT id FROM utilizadores WHERE email = '" . $email . "'";

Usa parâmetros preparados:

$stmt = $pdo->prepare('SELECT id FROM utilizadores WHERE email = :email');
$stmt->execute(['email' => $email]);

O valor é tratado como dado. Um apóstrofo deixa de fechar uma string do SQL. Parametrizar um valor não parametriza identificadores como nomes de colunas ou palavras-chave. Se a pessoa escolher a ordenação, mapeia a opção para uma lista de colunas autorizadas, sem concatenar o texto recebido.

O ORM ajuda quando usa parâmetros corretamente. Uma expressão raw construída com entrada continua vulnerável. Limita também os privilégios da conta da aplicação: uma leitura não precisa de permissões para alterar o esquema.

XSS: o contexto decide a codificação

Cross-site scripting faz o navegador interpretar dados controlados pelo atacante como código da página. Pode ser refletido na resposta, guardado e mostrado mais tarde, ou criado no cliente ao inserir dados de forma insegura no DOM.

Para texto dentro de HTML, em PHP:

echo htmlspecialchars($comentario, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

No Blade, {{ $comentario }} escapa HTML e {!! $comentario !!} imprime sem essa proteção. No DOM, prefere textContent para texto, porque innerHTML interpreta HTML e só deve receber conteúdo tratado para esse propósito.

A codificação depende do destino. Escapar texto HTML não torna segura uma interpolação dentro de JavaScript, CSS ou um URL. Num URL também tens de verificar o esquema, para não aceitar javascript:. Quando a funcionalidade permite HTML formatado, usa uma biblioteca de sanitização mantida com uma lista restrita de elementos e atributos, em vez de remover apenas <script>.

Uma política CSP pode reduzir certos ataques, mas não substitui codificação por contexto e sanitização. HttpOnly protege a leitura direta do cookie; um script injetado pode ainda agir com a sessão da vítima.

CSRF: a sessão é enviada automaticamente

Em cross-site request forgery, uma página de outra pessoa induz um pedido ao sistema usando credenciais que o navegador envia, como cookies. O atacante não precisa de conhecer o identificador da sessão nem de ler a resposta para causar uma alteração.

Uma compra baseada em sessão deve usar um token imprevisível associado ao contexto autorizado e validado pelo servidor. No Laravel, formulários protegidos pelo middleware adequado incluem @csrf. O token só é útil se for verificado; não deve ser colocado num URL que aparece em históricos e registos.

SameSite e verificação de origem são defesas complementares, não substitutos do token. O valor Lax continua a enviar o cookie em navegações de topo com método seguro: se alguma operação de escrita estiver acessível por GET, essa proteção não a impede, por isso nenhuma alteração de estado pode depender só do método do pedido. Além disso, a comparação é feita ao nível do domínio registável, não da origem completa: dois subdomínios diferentes do mesmo domínio contam como mesmo site. Um método POST sozinho não protege, e CORS não impede todos os envios de formulários. XSS pode contornar muitas defesas CSRF porque executa código na origem legítima.

Autenticado não significa autorizado

O comprador 7 pode estar autenticado e pedir GET /bilhetes/900, pertencente ao comprador 8. A aplicação tem de testar a relação entre a identidade e o recurso. Esconder o link ou usar um identificador difícil de adivinhar não cria essa permissão.

// Excerto Laravel com Illuminate\Support\Facades\Gate importado.
$bilhete = Bilhete::findOrFail($id);
Gate::authorize('view', $bilhete);

Aplica o controlo em todos os caminhos, incluindo API, downloads e operações em lote. Recusa por defeito e concede apenas as permissões necessárias. O papel «comprador» pode dar acesso à funcionalidade, mas a pertença do bilhete decide que objeto pode consultar.

Receber utilizador_id=8 no formulário não autoriza criar uma compra para 8. Usa a identidade autenticada quando a operação é «comprar para mim». $fillable controla preenchimento em massa de atributos, não verifica quem pode operar sobre cada objeto.

Palavras-passe e fixação de sessão

Guarda palavras-passe com um algoritmo de hash próprio para palavras-passe, como Argon2id ou bcrypt, através de uma API mantida. SHA-256 simples é demasiado rápido para este objetivo. Hash não tem uma chave de desencriptação; cifragem permite recuperar o original com uma chave. Uma palavra-passe não precisa de ser recuperada para verificar o login.

Em PHP, password_hash gera o hash com os parâmetros e salt necessários; password_verify verifica-o. O hash guardado já contém a informação que a API precisa. Não compares um novo hash gerado ao texto do anterior, porque os salts podem diferir.

Após autenticar, regenera o identificador da sessão para impedir session fixation. Uma mensagem de login genérica evita revelar qual parte das credenciais existe. Limitação de tentativas, recuperação com tokens de uso único e expiração completam a proteção.

Texto HTML e verificação de uma palavra-passe
<?php
$texto = '<img src=x onerror="alert(1)">';
echo htmlspecialchars($texto, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . "\n";
$hash = password_hash('exemplo_que_nao_e_segredo', PASSWORD_DEFAULT);
echo password_verify('exemplo_que_nao_e_segredo', $hash) ? "certa\n" : "recusada\n";
echo password_verify('outra', $hash) ? "certa\n" : "recusada\n";
Resultado

Executa o código para ver a saída e os gráficos.

O bloco isola duas APIs: codificação de texto para HTML e verificação de palavra-passe. Não implementa login, CSRF ou políticas de acesso, por isso não introduzas palavras-passe pessoais nestes exemplos.

Identificar a defesa numa pergunta

SituaçãoDefesa principal
Valor torna-se parte da sintaxe SQL.Consulta parametrizada.
Comentário torna-se HTML ativo.Codificação ou sanitização apropriada ao contexto.
Cookie da vítima acompanha uma operação induzida.Proteção CSRF validada no servidor.
Pessoa consulta o bilhete de outra.Autorização sobre o recurso.
Identificador de sessão anterior ao login é reutilizado.Regeneração da sessão.
Base de dados de palavras-passe é exposta.Hash resistente com parâmetros adequados, além de proteção do armazenamento.

São mecanismos diferentes: uma consulta parametrizada não impede alguém de consultar um objeto para o qual não tem autorização, e um token CSRF não impede XSS.

Um pedido ao bilhete alheio, camada a camada

O comprador 7 pede GET /bilhetes/900, mas o bilhete pertence ao comprador 8. Encontrar a linha com SQL seguro não concede permissão para a devolver.

Codificar o título impediria HTML ativo, mas não tornaria lícito mostrar os dados ao comprador 7.

A autorização testa a identidade 7 contra o proprietário 8 e recusa a leitura antes de gerar a representação. Para este pedido de leitura, um token CSRF não substitui a decisão sobre o objeto. Numa escrita, um token válido também não concede permissões; pode ser obtido e enviado por uma pessoa autenticada que tenta operar sobre um bilhete alheio.

Exercícios

Atacar sem ler a resposta

Um atacante precisa de ler a resposta da compra para realizar CSRF?

A tua resposta

Pista

O objetivo pode ser alterar, não obter informação.

Mais uma pista

Um formulário pode enviar uma operação com cookies.

Solução

Não precisa de ler. Usa proteção CSRF validada e métodos apropriados, além das defesas complementares.

  • Não. Basta induzir uma alteração com as credenciais automáticas da vítima.. A operação pode ter efeito mesmo sem expor o corpo ao atacante.
  • Sim, tem de ler o cookie.. O navegador pode enviá-lo sem o atacante o conhecer.
  • Sim, CORS impede a alteração.. CORS não bloqueia todos os pedidos que causam efeitos.

Erros frequentes

Não confundas bloquear leitura com bloquear efeito.

Voltar à explicação
Verificar sem recuperar a palavra-passe

Por que não se compara um password_hash novo diretamente com o hash guardado?

Pista

Dois hashes da mesma palavra-passe podem ser diferentes.

Mais uma pista

O formato guarda parâmetros e salt para a verificação.

Solução

A API gera salts próprios; dois textos de hash podem diferir. password_verify usa o formato guardado para testar a palavra-passe.

Confere a tua resposta:

  • Reconheço salts diferentes.
  • Uso password_verify.
  • Não preciso de recuperar o original.

Erros frequentes

Não uses SHA-256 rápido nem cifragem recuperável como substituto automático de um hash de palavras-passe.

Voltar à explicação
Uma defesa depende do destino

Escapar texto HTML torna segura qualquer string interpolada num bloco JavaScript?

A tua resposta

Pista

Há dois interpretadores com regras diferentes.

Mais uma pista

É preferível evitar a interpolação em código ativo.

Solução

Não. Escolhe uma API e serialização próprias para o destino, ou mantém os dados fora de código ativo. CSP acrescenta defesa, não corrige a interpolação.

  • Não. O contexto JavaScript exige tratamento próprio.. Codificação de texto HTML não é uma defesa universal.
  • Sim, todas as linguagens interpretam os caracteres da mesma forma.. Os contextos e parsers diferem.
  • Sim, se o site tiver HTTPS.. HTTPS protege o transporte, não essa interpretação.

Erros frequentes

Não chames «sanitização» a qualquer função sem indicar contexto.

Voltar à explicação
Distinguir injeção e acesso alheio

O login constrói a consulta abaixo por concatenação. Separadamente, GET /bilhetes/900 carrega e mostra o bilhete 900 a qualquer comprador autenticado, embora pertença ao comprador 8. O atacante é o comprador 7. Explica entrada, efeito e correção de cada falha. HTTPS e um token CSRF válido resolvem alguma delas?

$sql = "SELECT id FROM utilizadores WHERE email = '" . $email . "'";

Pista

Identifica qual interpretador ou regra é contornado em cada caminho.

Solução

Com email ' OR '1'='1, a consulta passa a terminar em WHERE email = '' OR '1'='1', uma condição verdadeira para todas as linhas. Usa uma consulta preparada com um parâmetro :email, para o valor nunca se tornar sintaxe.

O pedido de 7 ao bilhete de 8 pode ter SQL perfeitamente parametrizado e continuar a expor dados. Verifica no servidor uma policy sobre a operação view e o bilhete carregado, recusando o acesso de 7.

HTTPS protege o transporte. O token CSRF trata pedidos induzidos com credenciais automáticas. Nenhum substitui parâmetros SQL ou autorização sobre o objeto.

Confere a tua resposta:

  • Mostro como a entrada altera a sintaxe SQL.
  • Uso parâmetros para valores.
  • Recuso bilhete alheio por autorização.
Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.