在会话固定攻击,黑客获取/设置(通过任何方式)另一个人的会话ID。然后,黑客可以冒充他人并获取敏感信息。
Java Servlet 3.1 引入了以下 HttpServletRequest 方法:
String changeSessionId()
此方法将当前会话 id 更改为新的,从而提供针对会话固定攻击的保护。
Servlet 3.1 还引入了HttpSessionIdListener用于接收 HttpSession id 更改通知。
对于 3.1 之前的版本,我们可以使用以下步骤:
- HttpSession session = .....
- .....
- session.invalidate();
- session=request.getSession(true);
- //now set data from old session here
在以下示例中,我们将学习如何使用 Servlet 3.1 API 对抗会话固定。
- @WebServlet(name = "userLoginServlet", urlPatterns = {"/login"})
- public class LogInServlet extends HttpServlet {
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
-
- HttpSession session = req.getSession();
- String userKey = "loggedInUser";
- User user = (User) session.getAttribute(userKey);
- if (user == null) {
- user = loadUserForRequest(req);
- session.setAttribute(userKey, user);
- }
- prepareLogginInfoHtml(user, resp);
- req.changeSessionId();
- }
-
- private void prepareLogginInfoHtml(User user, HttpServletResponse resp)
- throws IOException {
- resp.setContentType("text/html");
- PrintWriter w = resp.getWriter();
- w.write("User logged in " + user.getName());
- w.write("
Show Balance"); - }
-
- private User loadUserForRequest(HttpServletRequest req) {
- //returning a dummy user
- return new User("1", "Mike", "12312",
- BigDecimal.valueOf(200000));
- }
- }
- public class User {
- private String id;
- private String name;
- private String accountNumber;
- BigDecimal currentBalance;
- .............
- }
- @WebServlet(name = "userBalanceServlet", urlPatterns = {"/balance"})
- public class BalanceServlet extends HttpServlet {
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
-
- HttpSession session = req.getSession();
- User user = (User) session.getAttribute("loggedInUser");
- if (user == null) {
- RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/login");
- dispatcher.forward(req, resp);
- } else {
- prepareBalanceInfoHtml(user, resp);
- req.changeSessionId();
- }
- }
-
- private void prepareBalanceInfoHtml(User user, HttpServletResponse resp) throws IOException {
- resp.setContentType("text/html");
- PrintWriter w = resp.getWriter();
- w.write("User logged in " + user.getName());
- w.write("
Balance: " + user.getCurrentBalance()); - }
- }
- @WebListener
- public class AppSessionIdListener implements HttpSessionIdListener {
-
- @Override
- public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {
- System.out.println("oldSessionId: " + oldSessionId);
- System.out.println("newSessionId: " + event.getSession().getId());
-
- }
- }
要尝试示例,请运行嵌入式 Jetty(在以下示例项目的 pom.xml 中配置):
mvn jetty:run
在本教程中,我们使用 Jetty 插件而不是 tomcat7-maven-plugin,因为 tomcat 插件不支持 Servlet 3.1(它支持最高 3.0 的版本)。

点击“显示余额”链接:

正如我们的 HttpSessionIdListener 打印的那样:
- .....
- oldSessionId: node0md9ox8wtj0e91lr5kpuzw7dql0
- newSessionId: node01jl2v0v5bvfwgxvhr7ao2f52z1
- ....
- oldSessionId: node0nyhkl2ej1ret1716bwjwyl9a92
- newSessionId: node0mqnz9mrwfg8a1it5hxc7rdg3v3
- ....

使用的依赖项和技术: