【问题标题】:Adding Spring Session JDBC to my Spring Boot project breaks authentication in an iframe将 Spring Session JDBC 添加到我的 Spring Boot 项目会破坏 iframe 中的身份验证
【发布时间】:2020-10-15 05:14:30
【问题描述】:

我目前正在构建一个受 Spring Security 保护的 Spring Boot Web 应用程序(2.3.1,但在 2.1.7 和 2.1.5 版本中也观察到了以下问题)。我主要使用默认设置(例如嵌入式 Tomcat、嵌入式 H2 数据库、Spring Web-MVC)。我使用以下代码对许可的POST 映射进行了一些自定义身份验证:

UsernamePasswordAuthenticationToken authentication = ...;
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);

这很好用。由于我想将其用于学生考试,因此我希望将会话与身份验证保持一致,这样以防服务器软件因某种原因崩溃或停机,服务器再次启动时无需重新进行身份验证。

我为此使用了 Spring Session JDBC。我在pom.xml 中添加了以下依赖项:

<dependency>
   <groupId>org.springframework.session</groupId>
   <artifactId>spring-session-jdbc</artifactId>
</dependency>

下面一行到我的application.properties

spring.session.store-type=jdbc

当我在本地测试时,这一切都很好。我看到会话出现在我的数据库中,一切正常。然而,在实践中,应用程序是从iframe 启动的:学习管理系统执行 LTI 启动,这是一个 POST 请求,其响应以iframe 结束。当我尝试进行测试部署时,我收到了 403 错误,表明我的请求未经过身份验证。经过一天令人沮丧的调试后,我最终将这个问题归结为将spring-session-jdbc 添加到我的项目会导致Web 应用程序在iframe 的每个请求上发送一个新的会话ID cookie。如果我在没有iframe 的情况下重复相同的请求,则相同的应用程序可以正常工作。如果我删除 spring-session-jdbc 依赖项,应用程序在 iframe 内也能正常工作。

如果我在 iframe 中执行身份验证,我会在 Spring Security 调试日志中看到以下内容(exec-1 是成功进行身份验证的地方,exec-2 是 iframe 在被 exec-1 重定向后执行的请求请求):

2020-06-24 22:14:02.015 DEBUG 7474 --- [nio-8031-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@671fa7ec
2020-06-24 22:14:02.015 DEBUG 7474 --- [nio-8031-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-06-24 22:14:02.063 DEBUG 7474 --- [nio-8031-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-06-24 22:14:02.113 DEBUG 7474 --- [nio-8031-exec-2] o.s.security.web.FilterChainProxy        : /launch/development at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-06-24 22:14:02.113 DEBUG 7474 --- [nio-8031-exec-2] o.s.security.web.FilterChainProxy        : /launch/development at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-06-24 22:14:02.114 DEBUG 7474 --- [nio-8031-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-06-24 22:14:02.114 DEBUG 7474 --- [nio-8031-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.

当我在常规浏览器窗口中使用相同的正在运行的应用程序执行相同的请求时,Spring Security 调试日志显示如下:

2020-06-24 22:18:10.518 DEBUG 7474 --- [nio-8031-exec-5] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@671fa7ec
2020-06-24 22:18:10.518 DEBUG 7474 --- [nio-8031-exec-5] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@e1239cdc: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@e1239cdc: Principal: testuserid; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_INSTRUCTOR' stored to HttpSession: 'org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@66670854
2020-06-24 22:18:10.536 DEBUG 7474 --- [nio-8031-exec-5] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2020-06-24 22:18:10.536 DEBUG 7474 --- [nio-8031-exec-5] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-06-24 22:18:10.590 DEBUG 7474 --- [nio-8031-exec-6] o.s.security.web.FilterChainProxy        : /home at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-06-24 22:18:10.591 DEBUG 7474 --- [nio-8031-exec-6] o.s.security.web.FilterChainProxy        : /home at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-06-24 22:18:10.592 DEBUG 7474 --- [nio-8031-exec-6] w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@82a4fa0f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@82a4fa0f: Principal: testuserid; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_INSTRUCTOR'

对我来说这似乎很奇怪:如果这纯粹是一个浏览器问题,我是否使用spring-session-jdbc 并不重要。如果spring-session-jdbc 存储我的身份验证的方式有问题,那么它是否发生在iframe 中并不重要。我在这里缺少什么吗?我是不是偶然发现了一个错误?

我可以尝试一种解决方法,在 iframe 中使用一些 javascript,让 POST 在空白选项卡中进行,但这感觉相当难看。

【问题讨论】:

    标签: java spring spring-boot spring-security spring-session


    【解决方案1】:

    一般来说,我建议您不要在 iframe 中使用您的应用程序。
    这会带来安全风险,您可以在this answer 中了解更多信息。

    现在解释您所看到的行为。
    Spring Security 使用Session cookie 来存储用户的会话。
    Cookie 与域相关联,因此,例如,如果有一个 cookie 与域 stackoverflow.com 相关联,那么该 cookie 将包含在对 stackoverlow.com 的任何请求中。

    为了控制这种行为,cookie 还有一个名为SameSite 的属性。
    SameSite attribute 可以有 3 个值,NoneLaxStrict
    当值为None 时,它的行为如上所述(包含在所有请求中)。
    当值为Lax 时,cookie 将只包含在顶级导航GET 请求中。

    包含 Spring Session 依赖项时,Session cookie SameSite 属性默认设置为 Lax

    由于在 iframe 中呈现应用程序不是顶级导航,因此 Session cookie 不包含在对 iframe 的请求中,并且应用程序无法知道用户已登录。

    您可以使用 Spring Session 将SameSite 属性显式设置为None
    我再次提醒您不要这样做,因为它会使您的应用程序容易受到 CSRF 和点击劫持攻击。
    如果在考虑安全隐患后,您认为有必要将 SameSite 属性设置为 None,则可以通过在依赖项中包含 Spring Session 并创建 custom CookieSerializer 来实现。

    【讨论】:

    • 非常感谢您的明确回答,我现在很清楚发生了什么!我在调试时注意到cookie中的SameSite属性,但没有给予足够的重视。使用自定义 CookieSerializer 的选项确实提供了我可以使用的折衷方案。不幸的是,iframe 不是我可以在短期内摆脱的东西,但值得研究未来的替代方案。
    猜你喜欢
    • 2018-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-08
    • 2018-07-13
    • 2020-11-01
    • 2018-04-13
    相关资源
    最近更新 更多