Segurança em aplicações PHP
Esta aula aborda segurança em aplicações PHP, cobrindo as principais ameaças segundo a OWASP, técnicas de validação de dados, o conceito de defesa em profundidade e a mentalidade necessária para desenvolver código seguro. São apresentados exemplos práticos e exercícios para fixação.
Segurança é um dos aspectos mais críticos no desenvolvimento de aplicações web, e com PHP não é diferente. Nesta aula, vamos explorar os fundamentos da segurança em aplicações PHP, desde as ameaças mais comuns até as estratégias de mitigação. Você aprenderá a identificar vulnerabilidades, implementar validações robustas, adotar uma abordagem de defesa em camadas e desenvolver uma mentalidade orientada à segurança. Ao final, terá um conjunto de boas práticas e exercícios para aplicar imediatamente.
O PHP é uma linguagem amplamente utilizada, e sua flexibilidade pode ser uma faca de dois gumes: se não tomarmos cuidado, podemos introduzir falhas graves. Por isso, é essencial entender como os atacantes pensam e como proteger cada camada da aplicação. Vamos começar pelas ameaças mais relevantes, seguindo as diretrizes do OWASP.
Principais ameaças (OWASP, visão geral)
O OWASP (Open Web Application Security Project) mantém uma lista das vulnerabilidades mais críticas em aplicações web, conhecida como OWASP Top 10. Essa lista é atualizada periodicamente e serve como referência para desenvolvedores e profissionais de segurança. Em aplicações PHP, as ameaças mais comuns incluem:
- Injeção (SQL, XSS, etc.): quando dados fornecidos pelo usuário são interpretados como código, permitindo manipulação de consultas ou execução de scripts maliciosos.
- Quebra de autenticação e gerenciamento de sessão: falhas que permitem a um atacante assumir a identidade de outro usuário ou comprometer tokens de sessão.
- Exposição de dados sensíveis: falta de criptografia ou controle de acesso, expondo informações como senhas e dados pessoais.
- Configurações incorretas de segurança: servidores mal configurados, erros de permissão, headers inseguros, entre outros.
- Cross-Site Request Forgery (CSRF): forçar o usuário a executar ações indesejadas em uma aplicação autenticada.
No PHP, a injeção de SQL é particularmente perigosa se não utilizarmos prepared statements. O XSS (Cross-Site Scripting) ocorre quando não escapamos a saída de dados. Vamos ver exemplos práticos de como essas ameaças se manifestam e como evitá-las.
// Exemplo vulnerável a SQL Injection
$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $id";
$result = mysqli_query($conn, $query);
// Exemplo seguro com prepared statement
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();Além disso, a exposição de dados sensíveis pode ser mitigada com criptografia e políticas de acesso. Sempre utilize HTTPS, armazene senhas com hash (usando password_hash e password_verify) e evite exibir informações de erro detalhadas em produção.
Validação
A validação de dados é a primeira linha de defesa contra entradas maliciosas. Ela consiste em verificar se os dados fornecidos pelo usuário atendem aos critérios esperados, antes de processá-los. No PHP, temos funções nativas que facilitam esse trabalho, como filter_var, preg_match e as funções de validação de tipos.
É importante distinguir entre validação e sanitização. A validação verifica se os dados são válidos (por exemplo, um e-mail tem formato correto), enquanto a sanitização remove ou modifica caracteres perigosos. Ambas são essenciais, mas a validação é mais segura, pois rejeita dados inválidos em vez de tentar corrigi-los.
$email = $_POST['email'];
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
// dados inválidos, tratar erro
}
$idade = $_POST['idade'];
if (filter_var($idade, FILTER_VALIDATE_INT, ['options' => ['min_range' => 18, 'max_range' => 120]])) {
// idade válida
}
// Para strings, use expressões regulares
$username = $_POST['username'];
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
// username inválido
}Além de validar entradas, é crucial validar dados em todas as camadas: no cliente (JavaScript), no servidor (PHP) e no banco de dados (restrições de integridade). Nunca confie apenas na validação do lado do cliente, pois ela pode ser facilmente contornada. No PHP, também devemos validar arquivos enviados, verificando o tipo, tamanho e conteúdo real, nunca confiando na extensão ou no MIME informado.
Defesa em profundidade
Defesa em profundidade é uma estratégia que consiste em aplicar múltiplas camadas de segurança, de modo que, se uma falha for explorada, outras barreiras ainda protejam o sistema. No contexto de aplicações PHP, isso significa combinar várias técnicas, como validação, sanitização, prepared statements, controle de acesso, criptografia, logging e monitoramento.
Por exemplo, para proteger contra SQL Injection, utilizamos prepared statements (camada de banco de dados), mas também devemos validar e sanitizar as entradas (camada de aplicação). Para proteger contra XSS, escapamos a saída (camada de apresentação) e também definimos headers de segurança (camada de resposta HTTP).
// Exemplo de defesa em profundidade para XSS
// 1. Validar entrada (server-side)
$comment = $_POST['comment'];
if (mb_strlen($comment) > 500) {
// rejeitar
}
// 2. Sanitizar a entrada (remover tags, se aplicável)
$comment = strip_tags($comment);
// 3. Escapar a saída ao exibir
?>
<div><?php echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8'); ?></div>
<?php
// 4. Definir headers de segurança
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('Content-Security-Policy: default-src \'self\'');Além disso, é fundamental manter o PHP e as bibliotecas atualizadas, pois muitas vulnerabilidades são corrigidas em novas versões. Configure também as opções de segurança do PHP, como session.cookie_httponly, session.cookie_secure e expose_php.
Mindset
Desenvolver uma mentalidade de segurança é tão importante quanto conhecer as técnicas. Isso envolve pensar como um atacante, questionar cada entrada e cada saída, e nunca assumir que o usuário é benigno. Adotar uma postura proativa, realizando revisões de código e testes de segurança, ajuda a identificar vulnerabilidades antes que sejam exploradas.
Algumas práticas que fazem parte desse mindset incluem: princípio do menor privilégio (dar apenas as permissões necessárias), nunca confiar em dados externos, manter a simplicidade (quanto mais complexo, mais chances de erro), e documentar decisões de segurança. Além disso, manter-se atualizado sobre novas ameaças e ferramentas de segurança é essencial.
// Exemplo de mindset: princípio do menor privilégio
// Em vez de usar um usuário com permissões totais no banco, crie um usuário com permissões mínimas
$db = new PDO('mysql:host=localhost;dbname=app', 'app_user', 'senha_forte');
// Configurar o PDO para lançar exceções e usar prepared statements
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);Também é importante ter um plano de resposta a incidentes: saber o que fazer se uma vulnerabilidade for descoberta ou explorada. Isso inclui ter backups, logs detalhados e procedimentos de comunicação. Por fim, a educação contínua é fundamental: participe de comunidades, leia sobre segurança e pratique em ambientes controlados.
Boas práticas e observações finais
Ao longo desta aula, vimos várias técnicas e conceitos. Aqui estão algumas boas práticas consolidadas:
- Sempre use prepared statements ou PDO com prepared statements para consultas ao banco.
- Escape toda saída de dados com
htmlspecialcharsou equivalentes. - Valide e sanitize todas as entradas, no servidor.
- Use HTTPS em toda a aplicação.
- Armazene senhas com
password_hashe verifique compassword_verify. - Defina headers de segurança (CSP, X-Frame-Options, etc.).
- Configure corretamente as sessões: use cookies seguros, HttpOnly, e timeout adequado.
- Mantenha o PHP e as dependências atualizadas.
- Não exiba erros em produção; registre-os em logs.
- Teste sua aplicação com ferramentas de análise de segurança (como OWASP ZAP).
Lembre-se: segurança é um processo contínuo, não um estado final. Sempre revise, teste e melhore. Com o mindset certo e as técnicas adequadas, você pode construir aplicações PHP muito mais seguras.
Referências
- OWASP Top 10
- PHP Manual - Segurança
- PHP Filter - Exemplos de Validação
- PHP PDO - Prepared Statements
- MDN - Content Security Policy
- OWASP Input Validation Cheat Sheet
- OWASP Password Storage Cheat Sheet
- PHP password_hash
Exercícios
- Explique a diferença entre validação e sanitização, e dê um exemplo de cada em PHP.
- Escreva um código PHP que receba um ID via GET e execute uma consulta segura no banco usando prepared statements, evitando SQL Injection.
- Cite três headers de segurança importantes e explique para que servem.
Content-Security-Policy: controla quais recursos podem ser carregados, mitigando XSS.X-Frame-Options: impede que a página seja exibida em iframes, prevenindo clickjacking.Strict-Transport-Security: força conexões HTTPS, protegendo contra downgrade.- Como você armazenaria senhas de usuários de forma segura no PHP? Mostre o código.
- Descreva uma situação onde a defesa em profundidade ajudaria a proteger uma aplicação PHP, e explique as camadas envolvidas.
filter_var($email, FILTER_VALIDATE_EMAIL)), enquanto sanitização modifica os dados para remover ou neutralizar caracteres perigosos (ex.: strip_tags($string)). A validação rejeita dados inválidos, a sanitização os limpa.$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();$hash = password_hash($password, PASSWORD_DEFAULT);
// para verificar:
if (password_verify($password, $hash)) {
// senha correta
}htmlspecialchars, (3) header CSP que restringe scripts, (4) cookie HttpOnly para proteger sessões. Se uma camada falhar, as outras ainda protegem.