Mecanismo de Funcionamento
O protocolo HTTP opera de maneira sem estado, o que significa que cada requisição é tratada independentemente. Para superar essa limitação e manter o contexto do usuário, os desenvolvedores utilizam cookies. Quando o lado do servidor define uma propriedade de resposta adicionando um cabeçalho Set-Cookie, o navegadro armazena esses dados localmente. Em solicitações subsequentes para o mesmo domínio, o cliente transmite automaticamente esses valores através do cabeçalho de requisição Cookie.
Embora os cookies sejam um padrão da indústria, existem restrições práticas, como o limite aproximado de 20 cookies por site dependendo das configurações do agente do usuário.
Gestão do Ciclo de Vida
Por padrão, um cookie é volátil e expira assim que a sessão do navegador termina. Para persistir os dados além disso, o método setMaxAge(int segundos) deve ser configurado explicitamente no objeto Cookie. Um valor positivo indica quantos segnudos o dado permanece válido no armazenamento local.
No ambiente server-side, a leitura dos dados enviados pelo cliente é realizada acessando o objeto HttpServletRequest. O método getCookies() retorna um array de objetos Cookie. É necessário percorrer esse array manualmente para localizar o identificador dseejado, pois não existe um método nativo de busca direta por nome.
A exclusão efetiva ocorre criando-se um novo cookie com o mesmo nome e definindo seu tempo máximo de vida (maxAge) para zero antes de enviar ao cliente.
Segurança e Restrições de Acesso
Na especificação Servlet 3.0, foi introduzido o método setHttpOnly(true). Ao ativar esta flag, o atributo HttpOnly é anexado ao cabeçalho de resposta. Isso impede que scripts executados no lado do cliente, como JavaScript, acessem o conteúdo do cookie, mitigando riscos associados a ataques XSS (Cross-Site Scripting). O status dessa configuração pode ser verificado posteriormente usando isHttpOnly().
Vale ressaltar que a principal vulnerabilidade reside na possibilidade de o usuário desabilitar o suporte a cookies nas configurações do seu navegador, tornando a dependência exclusiva dessa tecnologia arriscada para sessões críticas.
Exemplo Prático de Implementação
A seguir, apresenta-se uma implementação refinada de dois servlets para a gestão de tokens de acesso, alterando a estrutura lógica e nomenclatura em relação ao modelo básico.
package com.example.security;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "TokenGenerator", value = "/generar-token")
public class TokenGenerator extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
definirCookiePermanente(response);
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("Token gerado com sucesso.");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
private void definirCookiePermanente(HttpServletResponse resp) {
// Criação de cookie com nome específico e valor seguro
Cookie authKey = new Cookie("access_token", "xyz-987-zzz");
// Configuração de validade para 1 dia (86400 segundos)
authKey.setMaxAge(86400);
// Aplica restrições de segurança
authKey.setHttpOnly(true);
authKey.setPath("/");
resp.addCookie(authKey);
}
}
package com.example.security;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
@WebServlet(name = "TokenValidator", value = "/verificar-token")
public class TokenValidator extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Optional<Cookie> tokenOpt = buscarTokenEspecifico(req);
PrintWriter out = resp.getWriter();
if (tokenOpt.isPresent()) {
Cookie tokenAtual = tokenOpt.get();
out.println("<strong>Status:</strong> Autenticado");
out.println("<strong>ID:</strong> " + tokenAtual.getName());
// Lógica opcional para remover cookie após leitura
removerCookie(resp, tokenAtual.getName());
} else {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
out.println("<strong>Status:</strong> Nenhum token encontrado.");
}
}
private Optional<Cookie> buscarTokenEspecifico(HttpServletRequest request) {
Cookie[] todosTokens = request.getCookies();
if (todosTokens != null) {
for (Cookie c : todosTokens) {
if ("access_token".equals(c.getName())) {
return Optional.of(c);
}
}
}
return Optional.empty();
}
private void removerCookie(HttpServletResponse response, String nomeCookie) {
Cookie deleteCookie = new Cookie(nomeCookie, "");
deleteCookie.setMaxAge(0);
deleteCookie.setPath("/");
response.addCookie(deleteCookie);
}
}