如何禁用从url中作为url parameters /传递的解析登录参数

xen*_*ros 37 java spring spring-security httprequest spring-boot

应用程序记录所有请求的urls.这意味着,使用url参数进行身份验证至关重要,因为这会导致日志中充满对的情况(login=abc&password=123).出于这个原因,我已配置spring-security为从中读取参数request-body.这是通过将以下行添加到request-header:

'Content-Type': 'application/x-www-form-urlencoded'
Run Code Online (Sandbox Code Playgroud)

身体将是:

{'login':'admin', 'password':'password'}
Run Code Online (Sandbox Code Playgroud)

没关系,但QA强迫我通过url参数禁用身份验证的可能性.目前,对以下URL的POST也将进行身份验证:

https://example.com/foo?login=admin&password=password
Run Code Online (Sandbox Code Playgroud)

有没有人知道禁用此选项的技巧?最好带注释.

由于评论,我决定在我的问题中添加更多细节.我的配置spring-security.我有

http.usernameParameter("login")
    .passwordParameter("password")
(...)
Run Code Online (Sandbox Code Playgroud)

这使得WebSecurityConfigurerAdapter在参数和正文中搜索登录数据.我希望禁用在网址中搜索这些参数.

chi*_*mmi 15

这使得Spring在参数和正文中搜索登录数据.我希望禁用在网址中搜索这些参数.

我相信这是不可能的,因为Spring没有实现这种行为而不是JavaEE本身.

HttpServletRequest.getParameter doc说明:

以String形式返回请求参数的值,如果参数不存在,则返回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
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑 Haim Raman提出了另一种 使用现有过滤器而不是引入新过滤器的解决方案.只有我建议覆盖obtainUsername()obtainPassword()不是attemptAuthentication().


Hai*_*man 7

我想提出一个基于Spring-security rater的替代方案,然后根据chimmi建议的解决方法.

这个答案为xenterosbres26答案中提出的问题提供了解决方案

覆盖现有的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!");

        }
    }

 }
Run Code Online (Sandbox Code Playgroud)

您需要将现有实现替换为现有实现(请参阅此处的 doc )

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

优点:它基于弹簧安全性和灵活变化.
缺点:不幸的是我发现Spring Java Config很难设置和阅读

编辑:我接受了chimmi评论并覆盖了gainUsername和obtainPassword
你可以在github中找到源代码.


Pur*_*bro 6

就像我所提到的那样,尽我所知和直觉,适当的解决方案是使用注释@RequestMapping(value="/login", method="RequestMethod.POST").然后,无论用户使用URL传递什么参数,URL和URI都将始终默认为/ login.这就是记录器将记录的内容.不是用户名和密码对,而是 "http://localhost:8080/login"或者您的端口是什么.

  • 这不是一个解决方案.如果用户通过`http:// localhost:8080/login?login = admin&password = password`,它仍会按原样记录. (2认同)

Rob*_*nch 6

你可以通过修改UsernamePasswordAuthenticationFilter's 来实现这一点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()
                ...
        }
}
Run Code Online (Sandbox Code Playgroud)

注意:以下要求不会阻止发出GET请求(从而泄露凭据).用户界面确实不会发生这种情况.

没关系,但QA强迫我通过url参数禁用身份验证的可能性.

  • 如果他们卷曲你,那么他们的凭据就会被泄露。更重要的是,仅仅因为您不验证用户身份并不意味着损害已经造成。如果客户端在查询字符串中发送敏感信息,即使您不对用户进行身份验证,也会造成损害(凭据可能会暴露)。 (2认同)