Servlet - 使用 changeSessionId() 改变SessionId 防止会话固定攻击

发布时间 2023-11-29 19:20:24作者: sunny123456

Servlet - 使用 changeSessionId() 改变SessionId 防止会话固定攻击

会话固定攻击,黑客获取/设置(通过任何方式)另一个人的会话ID。然后,黑客可以冒充他人并获取敏感信息。

Java Servlet 3.1 引入了以下 HttpServletRequest 方法:

String changeSessionId()

此方法将当前会话 id 更改为新的,从而提供针对会话固定攻击的保护。

Servlet 3.1 还引入了HttpSessionIdListener用于接收 HttpSession id 更改通知。

对于 3.1 之前的版本,我们可以使用以下步骤:

  1. HttpSession session = .....
  2. .....
  3. session.invalidate();
  4. session=request.getSession(true);
  5. //now set data from old session here

例子

在以下示例中,我们将学习如何使用 Servlet 3.1 API 对抗会话固定。

登录 servlet

  1. @WebServlet(name = "userLoginServlet", urlPatterns = {"/login"})
  2. public class LogInServlet extends HttpServlet {
  3. @Override
  4. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  5. throws ServletException, IOException {
  6. HttpSession session = req.getSession();
  7. String userKey = "loggedInUser";
  8. User user = (User) session.getAttribute(userKey);
  9. if (user == null) {
  10. user = loadUserForRequest(req);
  11. session.setAttribute(userKey, user);
  12. }
  13. prepareLogginInfoHtml(user, resp);
  14. req.changeSessionId();
  15. }
  16. private void prepareLogginInfoHtml(User user, HttpServletResponse resp)
  17. throws IOException {
  18. resp.setContentType("text/html");
  19. PrintWriter w = resp.getWriter();
  20. w.write("User logged in " + user.getName());
  21. w.write("<br/><a href='/balance'>Show Balance</a>");
  22. }
  23. private User loadUserForRequest(HttpServletRequest req) {
  24. //returning a dummy user
  25. return new User("1", "Mike", "12312",
  26. BigDecimal.valueOf(200000));
  27. }
  28. }
  1. public class User {
  2. private String id;
  3. private String name;
  4. private String accountNumber;
  5. BigDecimal currentBalance;
  6. .............
  7. }

用于登录后处理的另一个 servlet

  1. @WebServlet(name = "userBalanceServlet", urlPatterns = {"/balance"})
  2. public class BalanceServlet extends HttpServlet {
  3. @Override
  4. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  5. throws ServletException, IOException {
  6. HttpSession session = req.getSession();
  7. User user = (User) session.getAttribute("loggedInUser");
  8. if (user == null) {
  9. RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/login");
  10. dispatcher.forward(req, resp);
  11. } else {
  12. prepareBalanceInfoHtml(user, resp);
  13. req.changeSessionId();
  14. }
  15. }
  16. private void prepareBalanceInfoHtml(User user, HttpServletResponse resp) throws IOException {
  17. resp.setContentType("text/html");
  18. PrintWriter w = resp.getWriter();
  19. w.write("User logged in " + user.getName());
  20. w.write("<br/>Balance: " + user.getCurrentBalance());
  21. }
  22. }

实现 AppSessionIdListener

  1. @WebListener
  2. public class AppSessionIdListener implements HttpSessionIdListener {
  3. @Override
  4. public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {
  5. System.out.println("oldSessionId: " + oldSessionId);
  6. System.out.println("newSessionId: " + event.getSession().getId());
  7. }
  8. }

要尝试示例,请运行嵌入式 Jetty(在以下示例项目的 pom.xml 中配置):

mvn jetty:run

在本教程中,我们使用 Jetty 插件而不是 tomcat7-maven-plugin,因为 tomcat 插件不支持 Servlet 3.1(它支持最高 3.0 的版本)。

输出

点击“显示余额”链接:

服务器输出

正如我们的 HttpSessionIdListener 打印的那样:

  1. .....
  2. oldSessionId: node0md9ox8wtj0e91lr5kpuzw7dql0
  3. newSessionId: node01jl2v0v5bvfwgxvhr7ao2f52z1
  4. ....
  5. oldSessionId: node0nyhkl2ej1ret1716bwjwyl9a92
  6. newSessionId: node0mqnz9mrwfg8a1it5hxc7rdg3v3
  7. ....

Chrome 检查 - 网络选项卡

 

示例项目

使用的依赖项和技术:

  • javax.servlet-api 3.1.0 Java Servlet API
  • jetty-maven-plugin 9.4.1.v20170120:Jetty maven 插件。
  • JDK 1.8
  • Maven 3.3.9
原文链接:https://blog.csdn.net/allway2/article/details/126679312