Segurança no Desenvolvimento Web com Java
Apesar dos constantes avanços na segurança digital, o conceito de que "nenhuma segurança é perfeita e nunca será" continua a se provar verdadeiro. Exemplos recentes, como um caso envolvendo o Banco Neon e a falha de segurança CVE-2025-21298, no qual, o próprio invasor do Banco Neon destacou que a brecha explorada era relativamente simples e a nível nacional, demonstrando que mesmo sendo grandes empresas, não são imunes a vulnerabilidades. Em um caso específico da falha reportada na Microsoft, a correção foi disponibilizada rapidamente, mas isso não impediu que o incidente gerasse discussões acaloradas no LinkedIn e em diversas comunidades tech, reforçando a ideia de que até mesmo falhas básicas podem comprometer sistemas supostamente seguros.
Mas é claro, isso não significa que você, Javeiro, pode simplesmente relaxar e esperar que as falhas de segurança apareçam nos seus SaaS ou até mesmo naqueles "projetinhos" que talvez nunca vejam a luz do dia, só porque viu que até mesmo as Big Techs enfrentam vulnerabilidades em seus sistemas. Segurança precisa ser levada a sério desde o início, porque quando as coisas dão errado, geralmente dão errado de verdade.
Para evitar esse tipo de dor de cabeça, vou apresentar algumas boas práticas e bibliotecas – tanto nativas quanto externas – que podem ajudar a mitigar as principais vulnerabilidades nos sistemas atuais do mercado.
1- Spring Security
Quando falamos de Java e segurança, é impossível não lembrar de uma das melhores ferramentas disponíveis para esse propósito: Spring Security. Essa biblioteca é praticamente um padrão no desenvolvimento seguro de aplicações Java, oferecendo suporte robusto para autenticação, autorização, proteção contra ataques comuns e integração com diversos métodos de autenticação modernos, como OAuth2 e JWT.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // Desativa CSRF para testes (não recomendado em produção)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/publico").permitAll() // Permite acesso sem autenticação
.requestMatchers("/admin").hasRole("ADMIN") // Apenas ADMIN pode acessar
.requestMatchers("/user").hasAnyRole("USER", "ADMIN") // USER e ADMIN podem acessar
.anyRequest().authenticated()) // Qualquer outra requisição exige autenticação
.formLogin(login -> login // Configuração do login padrão do Spring Security
.defaultSuccessUrl("/user", true) // Redireciona após login bem-sucedido
.permitAll())
.logout(logout -> logout // Configuração do logout
.logoutUrl("/logout")
.logoutSuccessUrl("/publico")
.permitAll());
return http.build();
}
}
Esse foi um exemplo relativamente básico (verboso) de configuração do Spring Security, utilizando alguns de seus principais métodos, como autenticação de roles (ADMIN e USER) em endpoints. Mas isso é apenas a ponta do iceberg dessa biblioteca.
Se você quiser se aprofundar ainda mais, pode explorar JWT (JSON Web Token) e OAuth2, que permitem a criação de autenticação baseada em tokens, amplamente utilizada em aplicações modernas. No entanto, para realmente dominar essas tecnologias, é essencial entender conceitos como segurança da informação, criptografia e outros tópicos relacionados.
E aqui vai uma recomendação de ouro: mesmo que pareça um clichê no nível daquele seu amigo ou familiar que "entende" de economia e insiste para você ler Pai Rico, Pai Pobre, eu recomendo fortemente que assistam ao vídeo do Fábio Akita: 📺 "Sua Segurança é uma DROGA | Gerenciadores de Senhas, 2FA, Encriptação"
Esse vídeo traz insights valiosos sobre boas práticas de segurança digital e explica de forma direta por que a segurança da maioria das pessoas é fraca – e como melhorar isso de forma prática.
2- Prepared Statement
Chegamos a um tópico que não é útil apenas para aplicações web, mas para qualquer aplicação que interaja com um banco de dados: o PreparedStatement
. Esse método faz parte da biblioteca nativa do Java, dentro do pacote
java.sql
Agora, você deve estar se perguntando:
"Muito boa essa introdução sobre PreparedStatement
, mas afinal, como ele funciona e o que ele impede???"
Bom, o PreparedStatement
simplesmente evita uma das vulnerabilidades mais caras do mundo do Bug Bounty, o temido SQL Injection.
Para quem não está tão familiarizado com o termo, aqui está uma definição direta do site da Microsoft Learn sobre SQL Injection:
"A injeção de SQL é um ataque em que o código mal-intencionado é inserido em cadeias de caracteres, passadas posteriormente para uma instância do mecanismo de banco de dados do SQL Server para análise e execução. Qualquer procedimento que construa instruções SQL deve ser verificado quanto a vulnerabilidades de injeção, porque o mecanismo de banco de dados executa todas as consultas sintaticamente válidas que recebe. Mesmo dados com parâmetros podem ser manipulados por um invasor qualificado e determinado."
Ou seja, sem um controle adequado, um invasor pode modificar a estrutura da consulta SQL, acessando dados confidenciais, alterando informações ou até mesmo apagando tabelas inteiras.
E é aí que o PreparedStatement
entra para salvar o dia — e de forma muito sagaz, se quer saber minha opinião. Agora, vamos aos exemplos práticos: suponha que você tenha um sistema simples de cadastro de pinguins em um zoológico:
String sql = "SELECT * FROM pinguins WHERE name = '" + name + "' AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
E nosso atacante / user malicioso digite no campo de senha:
' OR '1'='1',
E query gerada será:
SELECT * FROM pinguins WHERE name = 'admin' AND password = '' OR '1'='1'
como "1=1" sempre é verdadeiro, isso retornaria todos os pinguins(usuários), permitindo acesso indevido.
Isso acontece pois o Statement simples, faz com que o Java concatene a string SQL com os valores do usuário permitindo que um atacante insira comandos maliciosos. Agora vamos observar o mesmo código só que com o PreparedStatement:
String sql = "SELECT * FROM pinguins WHERE name = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setString(1, password);
ResultSet rs = pstmt.executeQuery(1);
O que está acontecendo internamente?
O SQL é enviado ao banco de dados primeiro, sem os valores do usuário O banco de dados pré-compila a consulta e separa a estrutura do SQL dos dados. Os valores são tratados como meros parâmetros e inseridos na consulta de forma segura. Agora, se um atacante tentar usar "username = admin e password = ' OR '1'='1", o banco de dados não interpretará isso como parte da lógica SQL. Em vez disso, ele tratará o input como um valor literal e buscará um usuário com senha exatamente igual a " ' OR '1'='1" — o que obviamente não existe (espero eu...).
3- Bcrypt
Ainda com o Spring Security, outro ponto muito importante quando falamos sobre segurança web são senhas, os jeitos de armazená-las e transportá-las do cliente ao servidor sem maiores problemas. E é aí que entra o BCRYPT, pois você não vai adicionar diretamente a senha ao banco de dados, de forma pura, quero dizer. Então, teremos que usar uma das features mais importantes, que é o "hashing seguro".
E quando falamos de hashing de senhas em Java, um dos melhores métodos disponíveis é o Bcrypt. Ele é um algoritmo de hash forte e adaptável, utilizado para proteger credenciais de usuários de forma segura.
Agora um exemplo de configuração básica:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordHashExample {
public static void main(String[] args) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String senhaOriginal = "minhaSenhaSuperSecreta";
// Gerando o hash
String senhaHash = encoder.encode(senhaOriginal);
System.out.println("Senha Hashed: " + senhaHash);
// Verificando a senha
boolean matches = encoder.matches("minhaSenhaSuperSecreta", senhaHash);
System.out.println("Senha válida? " + matches);
}
}
Um dos pontos que achei bem clever (inteligente) é a forma como esse algoritmo funciona e como ele se comporta em questões de desempenho. Sendo sincero, seu desempenho é devagar, mas você pode estar pensando:
"Poxa, mas não é só otimizar utilizando outros algoritmos de hashing já vistos que funcionam em Log O(1)? 🤓☝️"
Sim, mas essa demora não é por falta de conhecimento ou falta de vontade, e sim propositalmente desenhada para evitar uma possível invasão por BRUTE FORCE. O Bcrypt funciona devagar de propósito, dificultando qualquer tentativa de invasão.
Conclusão
Segurança não é só sobre usar bibliotecas, mas sobre compreender as vulnerabilidades e saber como mitigá-las da melhor forma possível. Desde a proteção contra SQL Injection, passando pela autenticação com Spring Security, até o hashing de senhas com Bcrypt, cada camada adicionada fortalece a segurança da sua aplicação.
Agora que você tem uma base sólida, que tal continuar se aprofundando? Segurança é um dos tópicos mais valiosos para qualquer Javeiro que quer se destacar no mercado!
Referências e Links Importantes
Aqui estão algumas referências utilizadas e links importantes para aprofundamento: