如何禁用解析作为 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 【问题描述】:

应用程序记录所有请求的urls。这意味着,不要使用 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】:

您可以通过修改UsernamePasswordAuthenticationFilterRequestMatcher 来实现。例如:

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。如果有人curls 我怎么办? 如果他们卷曲你,那么泄露的是他们的凭据。更重要的是,您不会对用户进行身份验证并不意味着损害已经造成。如果客户端在查询字符串中发送敏感信息,即使您没有对用户进行身份验证,也会造成损坏(可能会暴露凭据)。【参考方案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&amp;password=password,它仍然会被记录下来。 解决该问题的另一种方法是尝试手动重写 URL,以便省略 ? 之后的部分。但是,这可能有点难以自己实施。或者,您可以利用 springSecurityFilterChain here 或实现自定义过滤器。可以在这里找到对后者的快速解释:link。 正如我之前写的那样,这是一种非常危险的方法。这会导致有人在穿透请求中添加一些文本,以便它们匹配模式并且不会被记录。

以上是关于如何禁用解析作为 url 参数 / 从 url 传递的登录参数的主要内容,如果未能解决你的问题,请参考以下文章

如何将 JSON 返回数据作为参数传递给 URL 路由(laravel)

如何将 URL 中的数组作为参数传递给 Promise.all

在 Azure 函数中解析 URL

在解析 JSON 数据时,我从相同的 url 得到不同的结果

前端后端联调解决URL作为参数发生跨域问题

如何使用react将多个参数作为url中的对象传递