Como impedir hackers de invadirem seu código Java: injeções SQL e XSS sob o microscópio
\n\nImagine: você escreveu a aplicação web perfeita em Java. Interface bonita, lógica rápida, arquitetura limpa. Você a publica no servidor, prepara um chá e... uma hora depois recebe uma mensagem de que o banco de dados foi deletado e na página principal há um ousado ASCII-art de alguém. História familiar? Infelizmente, para muitos desenvolvedores — sim.
\n\nAs duas vulnerabilidades mais comuns e perigosas de aplicações web — injeções SQL e cross-site scripting (XSS) — tiram o sono de administradores de milhares de sites todos os anos. E Java, apesar de sua rigidez, não é exceção. Mas há uma boa notícia: proteger-se contra elas é possível. E nem é tão difícil.
\n\nNeste artigo, vamos analisar como os invasores atacam aplicações Java e, principalmente, como montar um bloqueio confiável usando ferramentas comprovadas e hábitos de codificação corretos. Apertem os cintos: terá muito código, dicas e o mínimo de enrolação.
\n\nInjeções SQL: quando o usuário se torna programador
\n\nO que é e por que funciona?
\n\nInjeção SQL é uma técnica de ataque onde o invasor "injeta" código SQL malicioso em uma consulta através da entrada do usuário. Se a aplicação concatena strings sem pensar, o atacante pode acessar dados alheios, deletar tabelas ou até assumir o controle do servidor de banco de dados.
\n\nExemplo clássico de código ruim em Java:
\n\nString userInput = request.getParameter("username");\nString query = "SELECT * FROM users WHERE username = '" + userInput + "'";\nStatement stmt = connection.createStatement();\nResultSet rs = stmt.executeQuery(query);\n\nO que acontece se o usuário digitar: admin' OR '1'='1? A consulta se torna:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'\n\nA condição '1'='1' é sempre verdadeira. Parabéns: o invasor acabou de entrar no sistema como administrador, sem nem saber a senha. E se ele digitar '; DROP TABLE users; -- — pode dar adeus aos dados.
Como se proteger: PreparedStatement — seu escudo
\n\nRegra número um em segurança de aplicações Java: nunca concatene dados do usuário com consultas SQL. Use consultas parametrizadas através de PreparedStatement.
Veja como deve ser o código seguro:
\n\nString userInput = request.getParameter("username");\nString query = "SELECT * FROM users WHERE username = ?";\nPreparedStatement pstmt = connection.prepareStatement(query);\npstmt.setString(1, userInput);\nResultSet rs = pstmt.executeQuery();\n\nO que aconteceu aqui? O banco de dados recebe a consulta e os dados separadamente. Mesmo que userInput contenha código SQL malicioso, ele será tratado como uma string comum, não como um comando. O PreparedStatement escapa automaticamente os caracteres especiais. Esta é a primeira e mais importante linha de defesa.
Medidas adicionais: procedimentos armazenados e validação
\n\nAlém do PreparedStatement, vale usar:
- \n
- Procedimentos armazenados — chamar um procedimento via
CallableStatementtambém separa o código dos dados. \n - Listas brancas — se precisar inserir o nome de uma tabela ou coluna (o que é raro), nunca o obtenha da entrada do usuário. Use enum ou constantes estritamente definidas. \n
- Validação de entrada — verifique se o email realmente se parece com um email e se um número é realmente um número. Mas lembre-se: validação é uma camada adicional, não substitui o
PreparedStatement. \n
Exemplo de trabalho seguro com nomes de tabelas dinâmicas:
\n\npublic enum TableName {\n USERS, PRODUCTS, ORDERS\n}\n\npublic void queryTable(TableName table) {\n String que