如何禁用解析作为 url 参数 / 从 url 传递的登录参数
Posted
技术标签:
【中文标题】如何禁用解析作为 url 参数 / 从 url 传递的登录参数【英文标题】:How do I disable resolving login parameters passed as url parameters / from the url 【发布时间】:2016-12-07 14:06:33 【问题描述】:应用程序记录所有请求的url
s。这意味着,不要使用 url 参数进行身份验证至关重要,因为这会导致日志中充满对 (login=abc&password=123)
的情况。为此,我将spring-security
配置为从request-body
读取参数。通过将以下行添加到request-header
来完成:
'Content-Type': 'application/x-www-form-urlencoded'
身体会是:
'login':'admin', 'password':'password'
没关系,但是 QA 强制我禁用通过 url 参数进行身份验证的可能性。目前,对以下 URL 的 POST 也将进行身份验证:
https://example.com/foo?login=admin&password=password
有人知道禁用此选项的技巧吗?最好带注释。
由于评论,我决定为我的问题添加更多细节。我的spring-security
配置了WebSecurityConfigurerAdapter
。我有
http.usernameParameter("login")
.passwordParameter("password")
(...)
这使得Spring
在参数和正文中搜索登录数据。我希望禁用在 url 中搜索这些参数。
【问题讨论】:
在你的端点放 RequestMapping put method = RequestMethod.POST @RequestMapping( value="/login", method=RequestMethod.POST ) 【参考方案1】:这使得 Spring 在参数和正文中搜索登录数据。我希望禁用在 url 中搜索这些参数。
我认为这是不可能的,因为这种行为不是由 Spring 实现的,而是由 JavaEE 本身实现的。
HttpServletRequest.getParameter 文档状态:
将请求参数的值作为字符串返回,如果参数不存在,则返回 null。请求参数是随请求发送的额外信息。对于 HTTP servlet,参数包含在查询字符串或发布的表单数据中。
但是您可以尝试使用看起来像这样的过滤器来改变它:
public class DisableGetAuthFiler extends OncePerRequestFilter
...
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
filterChain.doFilter(
new HttpServletRequestWrapper(request)
@Override
public String getParameter(String name)
if (("login".equals(name) && getQueryString().contains("login"))
|| ("password".equals(name) && getQueryString().contains("password")))
return null;
else
return super.getParameter(name);
,
response
);
编辑 Haim Raman 提出了另一个solution 使用现有过滤器而不是引入新过滤器。只有我建议覆盖 obtainUsername()
和 obtainPassword()
而不是 attemptAuthentication()
。
【讨论】:
我已经修改了代码示例(减去 NPE 检查),原来的一个都错了,对不起( 谢谢)别忘了检查 NPE - getQueryString() 可能为空 它解决了我的 QA 问题。总有可能四处走走 - 寻找一个轻松的。 @xenteros 我在 x-www-form-urlencoded 中传递用户凭据,但它仍然在 queryString 中,所以如果用户在 url 中传递了参数,我该如何限制,那么我应该抛出异常? 【参考方案2】:我想建议一个基于 spring-security 评估器的替代方案,然后是 chimmi 建议的解决方法。
此答案也为xenteros 在bres26 答案上提出的问题提供了解决方案
覆盖现有的 UsernamePasswordAuthenticationFilter 实现
public class ImprovedUsernamePasswordAuthenticationFilter
extends UsernamePasswordAuthenticationFilter
@Override
protected String obtainUsername(HttpServletRequest request)
final String usernameParameter = getUsernameParameter();
validateQueryParameter(request, usernameParameter);
return super.obtainUsername(request);
@Override
protected String obtainPassword(HttpServletRequest request)
final String passwordParameter = getPasswordParameter();
validateQueryParameter(request, passwordParameter);
return super.obtainPassword(request);
private void validateQueryParameter(HttpServletRequest request, String parameter)
final String queryString = request.getQueryString();
if (!StringUtils.isEmpty(queryString))
if (queryString.contains(parameter))
throw new AuthenticationServiceException("Query parameters for login are a prohibit, use message body only!");
您需要用现有的实现替换您自己的实现(参见文档here)
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http
.authorizeRequests()
.antMatchers("/", "/home","/login").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.permitAll()
.and()
//Replace FORM_LOGIN_FILTER with your own custom implementation
.addFilterAt(improvedUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.and()
//disable csrf to allow easy testing
.csrf().disable();
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
public UsernamePasswordAuthenticationFilter improvedUsernamePasswordAuthenticationFilter() throws Exception
UsernamePasswordAuthenticationFilter authFilter = new ImprovedUsernamePasswordAuthenticationFilter();
authFilter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/login", "POST")
);
authFilter
.setAuthenticationManager(authenticationManager());
authFilter
.setAuthenticationSuccessHandler(
new SavedRequestAwareAuthenticationSuccessHandler()
);
authFilter
.setAuthenticationFailureHandler(
new SimpleUrlAuthenticationFailureHandler("/login?error")
);
return authFilter;
优点:它基于 Spring 安全性并且可以灵活更改。缺点:不幸的是,我发现 Spring Java Config 很难设置和阅读
编辑:我接受了chimmi 评论并覆盖了获取用户名和获取密码 您可以在github找到源代码。
【讨论】:
【参考方案3】:您可以通过修改UsernamePasswordAuthenticationFilter
的RequestMatcher
来实现。例如:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http
.formLogin()
.withObjectPostProcessor(new ObjectPostProcessor<UsernamePasswordAuthenticationFilter>()
@Override
public <O extends UsernamePasswordAuthenticationFilter> O postProcess(
O filter)
AntPathRequestMatcher pathMatcher = new AntPathRequestMatcher("/login", "POST");
RequestMatcher noQuery = new RequestMatcher()
@Override
public boolean matches(HttpServletRequest request)
return request.getQueryString() == null;
;
AndRequestMatcher matcher = new AndRequestMatcher(Arrays.asList(pathMatcher, noQuery));
filter.setRequiresAuthenticationRequestMatcher(matcher);
return filter;
)
.and()
...
注意:以下要求不会阻止发出 GET 请求(因此会泄露凭据)。确保不会发生这种情况确实取决于 UI。
没关系,但 QA 强制我禁用 通过 url 参数进行身份验证。
【讨论】:
不能依赖 UI。如果有人curl
s 我怎么办?
如果他们卷曲你,那么泄露的是他们的凭据。更重要的是,您不会对用户进行身份验证并不意味着损害已经造成。如果客户端在查询字符串中发送敏感信息,即使您没有对用户进行身份验证,也会造成损坏(可能会暴露凭据)。【参考方案4】:
据我所知和直觉,就像 jhan 提到的那样,适当的解决方案是使用注释 @RequestMapping(value="/login", method="RequestMethod.POST")
。然后,无论用户可以通过 URL 传递什么参数,URL 和 URI 都将始终默认为 /login。这就是记录器将记录的内容。不是用户名和密码对,而是"http://localhost:8080/login"
,或者任何你的端口。
【讨论】:
使用GET
登录看起来像是一种反模式:) 无论如何,感谢重播!
我本来想写“POST”,但我分心了 :D 现在更正。
这不是解决方案。如果用户通过http://localhost:8080/login?login=admin&password=password
,它仍然会被记录下来。
解决该问题的另一种方法是尝试手动重写 URL,以便省略 ?
之后的部分。但是,这可能有点难以自己实施。或者,您可以利用 springSecurityFilterChain here 或实现自定义过滤器。可以在这里找到对后者的快速解释:link。
正如我之前写的那样,这是一种非常危险的方法。这会导致有人在穿透请求中添加一些文本,以便它们匹配模式并且不会被记录。以上是关于如何禁用解析作为 url 参数 / 从 url 传递的登录参数的主要内容,如果未能解决你的问题,请参考以下文章
如何将 JSON 返回数据作为参数传递给 URL 路由(laravel)
如何将 URL 中的数组作为参数传递给 Promise.all