【发布时间】:2019-09-13 16:23:13
【问题描述】:
我们有一个 Web 应用程序(5 年以上),它使用 Spring Security (3.1) 功能对用户进行身份验证和授权。
- 作为身份验证过程的一部分,我们创建一个随机的 20 个字符的访问代码 (ac) 和 5 个字符的用户编号 (un)
- 身份验证成功后,我们重定向到另一个应用程序,传递访问代码、用户号作为查询参数
-
APP2根据访问码向各个用户显示信息
下面是spring安全重定向的示例流程和示例格式:
重定向格式:
身份验证的代码流程
@RequestMapping(value="/Authentication.do")
public String doWAuthentication(ModelMap model) {
User user = super.getUser();
String url = app2Baseroot; // instance variable - APP2 Host name
//generate parameters - static methods
String accessCode = Utils.generateString(20);
String userNo = Utils.generateUserNumber(5);
//write authentication details for APP2 to retrieve
userDao.saveAuthenticationDetails(user, accessCode, userNo);
//Add redirection specifics - servlet name & query params
try {
url += java.net.URLEncoder.encode("AccessServlet?ac="+accessCode+"&un="+userNo, "UTF-8");
} catch (UnsupportedEncodingException uee) {
LOG.error("Unable to encode redirect URL: "+uee.toString());
}
//redirect via spring security logout
// AuthBaseroot - Instance variable - APP1 host name
return "redirect:"+AuthBaseroot+"j_spring_security_logout?redirectUrl="+url;
}
我们最近遇到了一个问题,即 USER 1 能够查看 USER 2 的详细信息,因为重定向 URL 混淆了。两个用户同时访问身份验证应用程序,即相同的小时、分钟、秒。
我们定义了自定义表单登录,将 always-use-default-target 属性设置为“true”,并在 spring security XML 中配置了自定义注销处理程序文件:
<sec:logout success-handler-ref="logoutSuccessHandler" invalidate-session="true" delete-cookies="JSESSIONID" />
LogoutSuccessHandler 扩展 SimpleUrlLogoutSuccessHandler 并覆盖 onLogoutSuccess。该方法所做的只是从 HttpServletRequest 获取重定向 url 并将其设置为
AbstractAuthenticationTargetUrlRequestHandler。 setDefaultTargetUrl(URL) 并调用 super 方法(super.onLogoutSuccess)。
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//Set redirection to APP2 url containing security params
super.setDefaultTargetUrl(request.getParameter("redirectUrl"));
super.onLogoutSuccess(request, response, authentication);
}
- 我们的应用程序托管在 AWS 中,并使用经典 ELB,作为安全性的一部分,所有请求流都通过 incapsula imperva 进行
- ELB 已为“应用程序生成的 cookie 粘性”启用粘性
- 我们还在 ELB 层启用了访问日志并检查访问日志,我们可以看到 USER1、USER2 的重定向 URL 混淆了
以下是供 USER1、USER 2 参考的示例访问日志
用户 2:
2019-04-20T09:34:11.328056Z XXX.XXX.XX.XX:XXXXX XX.XXX.XXX.XXX:80 0.000171 0.050725 0.0001 302 302 0 321
"GET https://APP1_HOST:PORT/auth/j_spring_security_logout?redirectUrl=https://APP2_HOST/link/AccessServlet%3Fac%3DWA-FF7WlNowjyu2R-XaU%26un%3D12345
HTTP/1.1" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
2019-04-20T09:34:11.413849Z XXX.XXX.XX.XX:XXXXX XX.XXX.XXX.XXX:80 0.000074 0.003343 0.000073 200 200 0 799
"GET https://APP2_HOST/link/AccessServlet%3Fac%3DWA-FF7WlNowjyu2R-XaU%26un%3D12345
HTTP/1.1" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
USER 2 访问日志证明来自 spring security logout j_spring_security_logout 的重定向 url 与实际重定向匹配。
用户 1
2019-03-26T09:34:11.349198Z XXX.XXX.XX.XX:XXXXX XX.XXX.XXX.XXX:80 0.000137 0.030374 0.00011 302 302 0 321
"GET https://APP1_HOST:PORT/auth/j_spring_security_logout?redirectUrl=https://APP2_HOST/link/AccessServlet%3Fac%3Dn--qcvnq-Z-3R2LOFjy-%26un%3D13267
HTTP/1.1" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/73.0.3683.86 Safari/537.36" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
2019-03-26T09:34:11.408386Z XXX.XXX.XX.XX:XXXXX XX.XXX.XXX.XXX:80 0.000066 0.007958 0.000077 200 200 0 1365
"GET https://APP2_HOST/link/AccessServlet%3Fac%3DWA-FF7WlNowjyu2R-XaU%26un%3D12345
HTTP/1.1" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/73.0.3683.86 Safari/537.36" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
第一个语句显示 j_spring_security_logout 以及 USER 1 的正确重定向 URL,但随后对重定向 URL 的调用似乎与 USER 2 混淆了。由于用户详细信息基于访问代码 USER 1 是能够查看 USER 2 的详细信息。
到目前为止我们注意到的几件事:
- USER1、USER2 同时发送请求,即相同的小时、分钟、秒。从访问日志中,我们看到 USER 2 请求先发送,但就响应而言,USER 1 首先使用 USER 2 重定向 URL 获得响应。
- 访问代码生成没有太大问题,因为我们看到 USER1、USER2 的初始 spring 安全注销重定向似乎具有正确的详细信息
- 因此 ELB 不会修改除 X-Forwarded-* 标头以外的任何 HTTP 标头详细信息,这是我的理解
- USER1、USER2 请求均由同一个 EC2 实例处理。经典 ELB 的默认行为是将每个请求独立路由到负载最小的注册实例。由于我们已经配置了粘性会话功能,这使负载均衡器能够将用户的会话绑定到特定实例。这可以确保会话期间来自用户的所有请求都发送到同一个实例 参考:https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html
以下是Authentication模块中使用的技术
- Java – 1.6
- Spring MVC、Spring Security(3.1 版)
- Servlet API – 2.4
- OC4J 服务器 (10.1.3.5)
- 在 AWS 中部署 - 使用经典 ELB
我想知道问题是否出在 Spring 安全层(可能是注销处理程序),我们从 HttpServletRequest 对象获取重定向 URL 并将其传递给响应,但无法重现问题。
任何意见或建议都会有所帮助。
【问题讨论】:
标签: java spring-mvc spring-security amazon-elb