会话固定攻击(session fixation attack)及解决办法

1 Cookie 的工作过程

Cookie的传递用到了两个字段: 请求头字段Cookie和响应头字段Set-Cookie

当用户浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份识别数据,格式是"key=value", 然后放进Set-Cookie字段里。随着响应报文一同发给浏览器。

浏览器收到响应报文,看到里面的Set-Cookie,知道这时服务器给的身份标识,于是就保存起来,下次再请求的时候 就自动把这个值放进Cookie字段里发给服务器。

因为第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。

服务器有时会在响应头里添加多个 Set-Cookie,存储多个**“key=value**”。但浏览器这边发送时不需要用多个 Cookie 字段,只要在一行里用“;”隔开就行。

示意图如下:
在这里插入图片描述
从上图得知,Cookie是由浏览器负责存储的,而不是操作系统。

2.session 介绍

session 是服务端的一个概念,服务端生成session都会生成一个对应的SessionID,这个SessionId 会通过Cookie传递给前端,前端以后发送请求的时候,都会带上sessionID的参数,服务端看到请求中带了sessionId, 就会根据这个sessionID 取出对应的session信息。

浏览器的关闭不会导致服务器的session失效,服务端session失效只有两种情况:一是session过期,二
是session的invalidate方法,还有就是重启客户端。

为什么我们平时觉得浏览器关闭了,session就失效了吗?这是因为浏览器里面的sessionID丢了,所以当浏览器再次访问服务器端的时候,服务器会重新给浏览器分配一个sessionId ,这个sessionId和 之前的那么session对应不上,所以用户就会感觉session失效。

session的认证的流程如下图:
在这里插入图片描述
1.浏览器发送登录请求 /user/login

2.登录成功后,服务端将session存在内存中。

扫描二维码关注公众号,回复: 14273042 查看本文章

3.然后服务端返回Set-Cookie 携带 sessionId 字段

4.登录后,浏览器每发送都会在请求字段Cookie上自动带上sessionId

5.浏览器拿到sessionID 后,去内存session中回去用户信息,如果匹配不上,说明没有登录。

3.会话固定攻击

会话固定(Session fixation)是一种诱骗受害者使用攻击者指定的会话标识(SessionID)的攻击手段。这是攻击者获取合法会话标识的最简单的方法。(让合法用户使用黑客预先设置的sessionID进行登录,从而是Web不再进行生成新的sessionID,从而导致黑客设置的sessionId变成了合法桥梁。)

Servlet容器允许URL地址后面增加;JSESSIONID=...的方式携带session信息。

攻击步骤
在这里插入图片描述
1.攻击者打开一个登录页面

2.服务端会返回攻击者一个sessionId = 123xyz

3.攻击者发送一个伪装连接并带上sessionId

4.然后正常用户接到后,点击进行登录

5.这时候服务端sessionID=123xyz的session已经存在

6.这时,有了这个sessionID ,攻击者就可以正大光明的访问漏洞系统了。

攻击原理: 登录后的sessionId和登录前的SessionId 没有变化

4. 解决方案

解决办法是在登录的时候,将原来的session作废,生成新的session。这里要注意的是,使用logout不能使session作废,而要用session的stop方法。代码如下:

在 Spring Security中,防御会话固定攻击很简单,只需要下面配置即可:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.formLogin()
                .loginPage("/signIn")
                .and()
                .cors()
                .and().sessionManagement().sessionFixation().migrateSession()
                .sessionRegistry(sessionRegistry);
    }
}

springSecurity 默认已经开启了该配置,主要利用SessionManager这个配置其来完成的。
在这里插入图片描述

  • none: 该策略对会话不做任何变动,登录之后会沿用旧的session;

  • newSession: 用户登录后会创建一个新的session;

  • migrateSession: 默认策略,用户登录后创建一个新的session,并将旧session中的数据复制过来;

  • changeSessionId: 表示 session 不变,不会创建新的session,但是会修改 sessionid,内部使用由Servlet容器提供的会话固定保护。

默认的防御策略是 migrateSession ,在用户匿名访问的时候是一个 sessionid,当用户成功登录之后,
又是另外一个 sessionid,这样就可以有效避免会话固定攻击。

Spring Security 之所以可以实现上述防御效果,主要是从以下三个方面来完成:

  1. 利用 StrictHttpFirewall 防火墙,如果发现请求地址中带有 “;”,则该请求会被直接拒绝;这样可以阻止请求URL带上SessionID

  2. 响应的 Set-Cookie 字段中有 HttpOnly 属性,这种方式会避免通过 XSS 攻击来获取 Cookie 中的会话信息,进而达成会话固定攻击。

  3. 登录成功后,改变sessionId 。既然问题是由于 sessionid 不变导致的,那我们就让 sessionid 变一下,利用Spring Security提供的防御会话固定攻击的策略即可实现。

我们看下session策略的源代码。

ChangeSessionIdAuthenticationStrategy中的onAuthentication方法其实是由父类AbstractSessionFixationProtectionStrategy实现的。这个方法就是防止固定会话攻击,会一直不停的修改你的sessionId。

public void onAuthentication(Authentication authentication,
                        HttpServletRequest request, HttpServletResponse response) {
    
    
                boolean hadSessionAlready = request.getSession(false) != null;
                //1.session不存在直接返回
                if (!hadSessionAlready && !alwaysCreateSession) {
    
    
                        return;
                }
                HttpSession session = request.getSession();
                //2.如果session存在的话则创建一个新的session覆盖老的session,sessionId也会改变
                if (hadSessionAlready && request.isRequestedSessionIdValid()) {
    
    
                        String originalSessionId;
                        String newSessionId;
                        Object mutex = WebUtils.getSessionMutex(session);
                        synchronized (mutex) {
    
    
                                // We need to migrate to a new session
                                originalSessionId = session.getId();
 
                                session = applySessionFixation(request);
                                newSessionId = session.getId();
                        }
                        if (originalSessionId.equals(newSessionId)) {
    
    
                                logger.warn("Your servlet container did not change the session ID when a new session was created. You will"
                                                + " not be adequately protected against session-fixation attacks");
                        }
                        onSessionChange(originalSessionId, session, authentication);
                }
        }

猜你喜欢

转载自blog.csdn.net/fd2025/article/details/124490876#comments_21631443