Java Cookie安全案例详解
不安全的Cookie设置案例
❌ 错误示例1:未设置安全标志
// 不安全的做法 - 生产环境中应避免
Cookie cookie = new Cookie("sessionId", "abc123");
cookie.setMaxAge(3600 * 24 * 30); // 30天过期
response.addCookie(cookie);
问题分析:

- 未设置Secure标志,允许通过HTTP传输
- 未设置HttpOnly标志,允许JavaScript访问
- 过期时间过长
- 未设置SameSite属性
❌ 错误示例2:敏感信息明文存储
// 极不安全 - 切勿使用
Cookie cookie = new Cookie("userInfo",
"username=admin;password=123456;role=admin");
cookie.setPath("/");
response.addCookie(cookie);
问题分析:
- 明文存储敏感信息
- 任何人都可以读取和篡改
- 不设置过期时间,成为会话Cookie
安全的Cookie配置案例
✅ 正确示例1:Session ID的安全配置
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
public class SecureCookieUtil {
public static void setSecureSessionCookie(HttpServletResponse response,
String sessionId) {
Cookie sessionCookie = new Cookie("SESSIONID", sessionId);
// 安全配置
sessionCookie.setSecure(true); // 仅HTTPS传输
sessionCookie.setHttpOnly(true); // 禁止JavaScript访问
sessionCookie.setPath("/"); // 设置作用域
// SameSite属性设置 (Servlet 4.0+)
// sessionCookie.setAttribute("SameSite", "Strict");
// 会话Cookie(不设置MaxAge,浏览器关闭即失效)
// 或者设置合理的过期时间
sessionCookie.setMaxAge(1800); // 30分钟
response.addCookie(sessionCookie);
}
}
✅ 正确示例2:使用加密的Cookie值
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class EncryptedCookieUtil {
private static final String AES_KEY = "YourSecretKey123"; // 必须16位
public static String encryptValue(String value) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(
AES_KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(value.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decryptValue(String encryptedValue) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(
AES_KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = Base64.getDecoder().decode(encryptedValue);
return new String(cipher.doFinal(decoded), "UTF-8");
}
public static void setEncryptedCookie(HttpServletResponse response,
String name,
String value,
int maxAge) throws Exception {
String encryptedValue = encryptValue(value);
Cookie cookie = new Cookie(name, encryptedValue);
cookie.setSecure(true);
cookie.setHttpOnly(true);
cookie.setMaxAge(maxAge);
cookie.setPath("/");
response.addCookie(cookie);
}
}
✅ 正确示例3:Spring Boot安全Cookie配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Configuration
public class SessionCookieConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
// 安全配置
serializer.setCookieName("SESSIONID");
serializer.setCookiePath("/");
serializer.setUseSecureCookie(true); // 仅HTTPS
serializer.setUseHttpOnlyCookie(true); // 禁止JS访问
serializer.setCookieMaxAge(1800); // 30分钟过期
serializer.setSameSite("Strict"); // SameSite策略
// 域名限制
// serializer.setDomainName("example.com");
return serializer;
}
}
完整的企业级安全实现
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.UUID;
public class EnterpriseCookieManager {
private static final int SESSION_TIMEOUT = 1800; // 30分钟
private static final String COOKIE_NAME = "X-Secure-Token";
private static final SecureRandom RANDOM = new SecureRandom();
/**
* 创建安全的会话Cookie
*/
public static Cookie createSecureCookie(String userId) {
// 生成唯一的会话标识
String sessionToken = generateSessionToken();
// 创建签名的令牌
String signedToken = signToken(userId + ":" + sessionToken);
Cookie cookie = new Cookie(COOKIE_NAME, signedToken);
cookie.setSecure(true); // 仅HTTPS
cookie.setHttpOnly(true); // 禁止JS访问
cookie.setPath("/"); // 全站生效
cookie.setMaxAge(SESSION_TIMEOUT);
cookie.setDomain(".example.com"); // 设置域名范围
return cookie;
}
/**
* 验证Cookie的完整性和有效性
*/
public static boolean validateCookie(Cookie cookie) {
if (cookie == null) return false;
try {
String token = cookie.getValue();
// 验证签名
if (!verifySignature(token)) {
return false;
}
// 检查是否过期(从令牌中提取时间戳)
long timestamp = extractTimestamp(token);
if (System.currentTimeMillis() - timestamp > SESSION_TIMEOUT * 1000) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 获取安全的Cookie值
*/
public static String getSecureValue(HttpServletRequest request,
String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
// 只处理通过HTTPS传输的Cookie
if (request.isSecure()) {
return cookie.getValue();
}
}
}
}
return null;
}
/**
* 销毁Cookie(登出时使用)
*/
public static void invalidateCookie(HttpServletResponse response) {
Cookie cookie = new Cookie(COOKIE_NAME, null);
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setSecure(true);
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
private static String generateSessionToken() {
byte[] randomBytes = new byte[32];
RANDOM.nextBytes(randomBytes);
return DatatypeConverter.printHexBinary(randomBytes);
}
private static String signToken(String token) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest((token + "secret_salt").getBytes("UTF-8"));
return token + ":" + DatatypeConverter.printHexBinary(hash);
} catch (Exception e) {
throw new RuntimeException("Token signing failed", e);
}
}
private static boolean verifySignature(String signedToken) {
try {
String[] parts = signedToken.split(":");
if (parts.length != 3) return false;
String originalToken = parts[0] + ":" + parts[1];
String expectedSignature = parts[2];
MessageDigest md = MessageDigest.getInstance("SHA-256");
String computedHash = DatatypeConverter.printHexBinary(
md.digest((originalToken + "secret_salt").getBytes("UTF-8")));
return expectedSignature.equals(computedHash);
} catch (Exception e) {
return false;
}
}
private static long extractTimestamp(String signedToken) {
// 假设令牌格式: userId:sessionToken:timestamp:signature
String[] parts = signedToken.split(":");
return Long.parseLong(parts[2]);
}
}
Web安全头配置
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/api/*")
public class SecurityHeadersFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
// HSTS(HTTP严格传输安全)
httpResponse.setHeader("Strict-Transport-Security",
"max-age=31536000; includeSubDomains");
// 防止XSS攻击
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
httpResponse.setHeader("X-Frame-Options", "DENY");
httpResponse.setHeader("X-XSS-Protection",
"1; mode=block");
// 控制资源加载策略
httpResponse.setHeader("Content-Security-Policy",
"default-src 'self'; script-src 'self'");
// 控制Referer信息
httpResponse.setHeader("Referrer-Policy",
"strict-origin-when-cross-origin");
// 禁止通过JavaScript访问Cookie
// 使用Set-Cookie头设置SameSite
// response.setHeader("Set-Cookie",
// "SESSIONID=xxx; Secure; HttpOnly; SameSite=Strict");
chain.doFilter(request, response);
}
}
安全最佳实践总结
| 安全措施 | 说明 | 配置方式 |
|---|---|---|
| Secure | 仅HTTPS传输 | cookie.setSecure(true) |
| HttpOnly | 禁止JavaScript访问 | cookie.setHttpOnly(true) |
| SameSite | 防止CSRF攻击 | SameSite=Strict/Lax |
| 过期时间 | 限制Cookie生命周期 | setMaxAge(1800) |
| 签名验证 | 防止篡改 | HMAC或数字签名 |
| 加密 | 保护敏感数据 | AES加密存储 |
| 路径限制 | 限制作用域 | setPath("/app") |
| 域名限制 | 限制访问域名 | setDomain(".example.com") |
特别注意:
- 绝不将密码、信用卡等敏感信息存储在Cookie中
- Session ID应该在服务器端进行绑定验证
- 定期轮换密钥和会话ID
- 对Cookie值进行完整性校验
- 使用最新的Servlet规范和浏览器安全特性
案例涵盖了从基础到高级的Java Cookie安全实现,可根据实际需求选择合适的安全级别。