복붙노트

[SPRING] URL 매개 변수 / URL로 전달 된 로그인 매개 변수를 확인하지 못하게하려면 어떻게해야합니까?

SPRING

URL 매개 변수 / URL로 전달 된 로그인 매개 변수를 확인하지 못하게하려면 어떻게해야합니까?

응용 프로그램은 요청 된 모든 URL을 기록합니다. 이는 URL 매개 변수를 사용하여 인증하지 않는 것이 중요하다는 것을 의미합니다. 로그가 쌍으로 가득 찬 상황을 유발할 수 있기 때문입니다 (login = abc & password = 123). 이런 이유로 저는 요청 보안 본문에서 매개 변수를 읽을 수 있도록 스프링 보안을 구성했습니다. request-header에 다음 행을 추가하여 완료됩니다.

'Content-Type': 'application/x-www-form-urlencoded'

시체는 :

{'login':'admin', 'password':'password'}

괜찮아요.하지만 품질 보증을 통해 URL 매개 변수를 통해 인증 할 수 없게됩니다. 현재 다음 URL에 대한 POST가 인증됩니다.

https://example.com/foo?login=admin&password=password

누구든지이 옵션을 비활성화하는 트릭을 알고 있습니까? 가급적 주석을 사용하십시오.

의견으로 인해 나는 내 문제에 더 많은 세부 사항을 추가하기로 결정했다. 내 봄 보안은 WebSecurityConfigurerAdapter로 구성됩니다. 나는 가지고있다

http.usernameParameter("login")
    .passwordParameter("password")
(...)

이렇게하면 Spring은 두 매개 변수와 본문에서 로그인 데이터를 검색하게됩니다. URL에서 매개 변수를 검색하지 못하게하고 싶습니다.

해결법

  1. ==============================

    1.나는이 동작이 JavaEE 자체보다는 Spring에 의해 구현되지 않았기 때문에 가능하지 않다고 생각한다.

    나는이 동작이 JavaEE 자체보다는 Spring에 의해 구현되지 않았기 때문에 가능하지 않다고 생각한다.

    HttpServletRequest.getParameter doc 상태 :

    그러나 다음과 같은 필터를 사용하여이 값을 변경하려고 할 수 있습니다.

    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은 새 필터를 도입하는 대신 기존 필터를 사용하는 또 다른 솔루션을 제안했습니다. tryAuthentication () 대신 obtainUsername () 및 obtainPassword ()를 재정의하는 것이 좋습니다.

  2. ==============================

    2.chimmi가 제안한 해결 방안을 봄 보안에 기초한 대안으로 제안하고자합니다.

    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!");
    
            }
        }
    
     }
    

    자신의 구현을 기존의 구현으로 대체해야합니다 (여기 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;
        }
    }
    

    장점 : 스프링 보안을 기반으로하며 변경에 유연합니다. 단점 : 불행히도 Spring Java Config는 설정하고 읽기가 매우 어려웠습니다.

    편집 : chimmi 코멘트를 허용하고 obtainUsername 및 obtainPassword를 재정의했습니다. github에서 소스 코드를 찾을 수 있습니다.

  3. ==============================

    3.jhan이 언급 한 것처럼 제 지식과 직관력을 최대한 살려면 주석은 @RequestMapping (value = "/ login", method = "RequestMethod.POST") 주석을 사용하는 것이 좋습니다. 그런 다음 사용자가 URL과 함께 전달할 수있는 매개 변수에 관계없이 URL과 URI는 항상 / login이 기본값입니다. 이것이 로거가 기록한 것입니다. 사용자 이름과 암호 쌍이 아니라 "http : // localhost : 8080 / login"또는 포트가 무엇이든 상관 없습니다.

    jhan이 언급 한 것처럼 제 지식과 직관력을 최대한 살려면 주석은 @RequestMapping (value = "/ login", method = "RequestMethod.POST") 주석을 사용하는 것이 좋습니다. 그런 다음 사용자가 URL과 함께 전달할 수있는 매개 변수에 관계없이 URL과 URI는 항상 / login이 기본값입니다. 이것이 로거가 기록한 것입니다. 사용자 이름과 암호 쌍이 아니라 "http : // localhost : 8080 / login"또는 포트가 무엇이든 상관 없습니다.

  4. ==============================

    4.UsernamePasswordAuthenticationFilter의 RequestMatcher를 수정하여이 작업을 수행 할 수 있습니다. 예 :

    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에 달려 있습니다.

  5. from https://stackoverflow.com/questions/38716703/how-do-i-disable-resolving-login-parameters-passed-as-url-parameters-from-the by cc-by-sa and MIT license