itsource

거부된 요청을 가로채는 방법봄에는 예외?

mycopycode 2023. 7. 26. 22:05
반응형

거부된 요청을 가로채는 방법봄에는 예외?

나는 많은 것을 보고 있습니다.RequestRejectedExceptionTomcat 로그의 항목(아래에 샘플을 붙여넣음).몇 달 전 마이너 버전 업그레이드(Spring Security 4.2.4, IIRC) 후에 로그 파일에 표시되기 시작했기 때문에 이 기능은 기본적으로 활성화된 봄의 새로운 보안 기능입니다.비슷한 문제가 여기서 보고되지만, 제 질문은 특히 컨트롤러에서 이러한 예외를 가로채는 방법과 관련이 있습니다.이 문제에 대해 문서화된 Spring Security 버그가 있습니다(RequestRejected를 처리하는 방법을 제공예외).하지만, 그들은 5.1 봄까지 이 문제에 대한 해결책을 찾고 있지 않습니다.

이러한 예외가 발생하는 이유를 이해하며 이 보안 기능을 사용하지 않도록 설정하지 않습니다.

다음과 같이 이 기능을 어느 정도 제어하고 싶습니다.

  1. 내 사이트에서 합법적인 사용자를 차단하지 않습니다.
  2. 어떤 요청이 이를 트리거하는지 알 수 있습니다(SQL Injection 공격입니까?).
  3. 저는 서버 응답을 조정할 수 있습니다. 스택 추적 노출)과 Spring Security를 웹.500 Internal Server Error(틀린 입니다, 은 (그완부다니합확정히은, 이것전은것▁be)이어야 합니다400 Bad Request).

요청된 URL을 기록할 수 있는 방법을 찾지만, 이러한 예외에 대해 스택 추적을 억제하고 싶습니다. 이 예외는 유용한 정보를 제공하지 않고 내 로그 파일을 오염시키고 있기 때문입니다.이러한 예외를 Tomcat 로그에 보고하는 대신 애플리케이션 계층에서 처리하는 것이 가장 좋습니다.

예를 들어, 이것은 매일 나타나는 수천 개의 로그 항목 중 하나입니다.catalina.out:

Aug 10, 2018 2:01:36 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [] threw exception
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
        at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:265)
        at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:245)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:193)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
        at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:486)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

저는 이틀 동안 3,200개 이상의 이것들을 보고 있습니다. 그리고 그것은 빠르게 저의 가장 큰 기여자가 되었습니다.catalina.out로그 파일은 합법적인 다른 문제를 볼 수 없도록 합니다.기본적으로, 이 새로운 스프링 보안 기능은 기본 제공 서비스 거부의 한 형태이며, 4월 이후로 제 시간의 몇 시간을 낭비했습니다.이 기능이 중요한 기능이 아니라고 말하는 것은 아닙니다. 단순히 기본 구현이 완전히 잘못되었다는 것입니다. 개발자로서도 시스템 관리자로서도 어느 정도 제어할 수 있는 방법을 찾고 싶습니다.

는 사용자 하여 다른 유형: 사용지 오다을컨사를러다여용유많예포하다른있형니습함외은고로가채롤자트정음류다)을 .IOException 에. 봄에.하지만,RequestRejectedException무슨 이유에서인지 실패하고 있는 것 같습니다.

은 저의 이것나관부다니입분련의 입니다.ErrorController.java내가 성취하고자 하는 것에 대한 아이디어를 주기 위해:

@ControllerAdvice
public final class ErrorController
{
    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(ErrorController.class.getName());

    /**
     * Generates an Error page by intercepting exceptions generated from HttpFirewall.
     *
     * @param ex A RequestRejectedException exception.
     * @return The tile definition name for the page.
     */
    @ExceptionHandler(RequestRejectedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handleRequestRejectedException(final HttpServletRequest request, final RequestRejectedException ex)
    {
        if (LOGGER.isLoggable(Level.INFO))
        {
            LOGGER.log(Level.INFO, "Request Rejected", ex);
        }

        LOGGER.log(Level.WARNING, "Rejected request for [" + request.getRequestURL().toString() + "]. Reason: " + ex.getMessage());
        return "errorPage";
    }

    /**
     * Generates a Server Error page.
     *
     * @param ex An exception.
     * @return The tile definition name for the page.
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String handleException(final Exception ex)
    {
        if (LOGGER.isLoggable(Level.SEVERE))
        {
            LOGGER.log(Level.SEVERE, "Server Error", ex);
        }

        return "errorPage";
    }
}

이 오류 컨트롤러는 여러 예외에 대해 작동합니다.예를 들어, 이것을 성공적으로 가로챘습니다.IllegalStateException:

Aug 05, 2018 7:50:30 AM com.mycompany.spring.controller.ErrorController handleException
SEVERE: Server Error
java.lang.IllegalStateException: Cannot create a session after the response has been committed
        at org.apache.catalina.connector.Request.doGetSession(Request.java:2999)
...

그러나 이것은 가로채는 것이 아닙니다.RequestRejectedException(위의 첫 번째 로그 샘플에 "서버 오류"가 없는 것으로 표시됨).)

인터셉트를 할 수 ?RequestRejectedException오류 제어 장치에서?

간단한 필터로도 처리할 수 있으며, 이는 404개의 오류 응답으로 이어질 수 있습니다.

@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LogAndSuppressRequestRejectedExceptionFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(req, res);
        } catch (RequestRejectedException e) {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;

            log
                .warn(
                        "request_rejected: remote={}, user_agent={}, request_url={}",
                        request.getRemoteHost(),  
                        request.getHeader(HttpHeaders.USER_AGENT),
                        request.getRequestURL(), 
                        e
                );

            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }
}

버전의 Spring ▁for▁spring안▁versions보▁spring▁security5.4 콩 종류의 콩을 만들 수 있습니다.RequestRejectedHandler 필터 체인 Spring에 입니다.

import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;

@Bean
RequestRejectedHandler requestRejectedHandler() {
   // sends an error response with a configurable status code (default is 400 BAD_REQUEST)
   // we can pass a different value in the constructor
   return new HttpStatusRequestRejectedHandler();
}

는 다의하위클구다니습현했래의 했습니다.StrictHttpFirewall요청 정보를 콘솔에 기록하고 억제된 스택 추적으로 새 예외를 던집니다.이것은 제 문제를 부분적으로 해결합니다(적어도 지금은 나쁜 요청을 볼 수 있습니다).

스택 추적 없이 거부된 요청만 보려면 이 방법을 선택하십시오.

컨트롤러에서 이러한 예외를 처리하려면 승인된 답변을 참조하여 완전한(그러나 약간 더 복잡한) 솔루션을 확인하십시오.


Http Firewall.java 로깅

이 클래스는 캡처할 StrictHttp Firewall을 확장합니다.RequestRejectedException요청의 메타데이터와 억제된 스택 추적을 사용하여 새 예외를 던집니다.

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.security.web.firewall.StrictHttpFirewall;

/**
 * Overrides the StrictHttpFirewall to log some useful information about blocked requests.
 */
public final class LoggingHttpFirewall extends StrictHttpFirewall
{
    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(LoggingHttpFirewall.class.getName());

    /**
     * Default constructor.
     */
    public LoggingHttpFirewall()
    {
        super();
        return;
    }

    /**
     * Provides the request object which will be passed through the filter chain.
     *
     * @returns A FirewalledRequest (required by the HttpFirewall interface) which
     *          inconveniently breaks the general contract of ServletFilter because
     *          we can't upcast this to an HttpServletRequest. This prevents us
     *          from re-wrapping this using an HttpServletRequestWrapper.
     * @throws RequestRejectedException if the request should be rejected immediately.
     */
    @Override
    public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) throws RequestRejectedException
    {
        try
        {
            return super.getFirewalledRequest(request);
        } catch (RequestRejectedException ex) {
            if (LOGGER.isLoggable(Level.WARNING))
            {
                LOGGER.log(Level.WARNING, "Intercepted RequestBlockedException: Remote Host: " + request.getRemoteHost() + " User Agent: " + request.getHeader("User-Agent") + " Request URL: " + request.getRequestURL().toString());
            }

            // Wrap in a new RequestRejectedException with request metadata and a shallower stack trace.
            throw new RequestRejectedException(ex.getMessage() + ".\n Remote Host: " + request.getRemoteHost() + "\n User Agent: " + request.getHeader("User-Agent") + "\n Request URL: " + request.getRequestURL().toString())
            {
                private static final long serialVersionUID = 1L;

                @Override
                public synchronized Throwable fillInStackTrace()
                {
                    return this; // suppress the stack trace.
                }
            };
        }
    }

    /**
     * Provides the response which will be passed through the filter chain.
     * This method isn't extensible because the request may already be committed.
     * Furthermore, this is only invoked for requests that were not blocked, so we can't
     * control the status or response for blocked requests here.
     *
     * @param response The original HttpServletResponse.
     * @return the original response or a replacement/wrapper.
     */
    @Override
    public HttpServletResponse getFirewalledResponse(final HttpServletResponse response)
    {
        // Note: The FirewalledResponse class is not accessible outside the package.
        return super.getFirewalledResponse(response);
    }
}

WebSecurityConfig.java

WebSecurityConfig 방화벽으로 설정합니다.LoggingHttpFirewall.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    /**
     * Default constructor.
     */
    public WebSecurityConfig()
    {
        super();
        return;
    }

    @Override
    public final void configure(final WebSecurity web) throws Exception
    {
        super.configure(web);
        web.httpFirewall(new LoggingHttpFirewall()); // Set the custom firewall.
        return;
    }
}

결과.

운영 환경에 이 솔루션을 구축한 후에 신속하게 다음과 같은 기본 동작을 확인할 수 있었습니다.StrictHttpFirewallGoogle이 내 사이트를 인덱싱하는 것을 차단했습니다!

Aug 13, 2018 1:48:56 PM com.mycompany.spring.security.AnnotatingHttpFirewall getFirewalledRequest
WARNING: Intercepted RequestBlockedException: Remote Host: 66.249.64.223 User Agent: Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) Request URL: https://www.mycompany.com/10.1601/tx.3784;jsessionid=692804549F9AB55F45DBD0AFE2A97FFD

이것을 발견하자마자, 저는 다음을 찾는 새로운 버전(다른 답변에 포함됨)을 신속하게 배포했습니다.;jsessionid=이러한 요청을 통과할 수 있도록 허용합니다.다른 요청도 통과해야 할 수 있습니다. 이제 이러한 요청을 탐지할 수 있습니다.

알고 보니 비록HttpFirewall그리고.StrictHttpFirewall몇 가지 설계 오류(아래 코드에 문서화되어 있음)가 포함되어 있습니다. Spring Security의 One True Firewall을 탈출하여 터널링하는 것은 거의 불가능합니다.HttpFirewall에 대한 요청 HandlerInterceptor이러한 플래그 지정된 요청을 처음부터 플래그 지정된 원래 비즈니스 로직을 그대로 유지하면서 실제(영구) 방화벽에 전달할 수 있습니다.여기에 문서화된 방법은 미래에 대비해야 합니다. 그것은 그것이 다음의 단순한 계약에 부합하기 때문입니다.HttpFirewallSpring Framework Java Servlet API를 사용합니다.

이것은 본질적으로 제가 이전에 대답했던 보다 더 복잡하지만 더 완벽한 대안입니다.이 답변에서, 저는 다음의 새로운 하위 클래스를 구현했습니다.StrictHttpFirewall이는 특정 로깅 수준에서 거부된 요청을 가로채고 기록하지만 처리할 다운스트림 필터(또는 컨트롤러)에 플래그를 지정하는 속성을 HTTP 요청에 추가합니다.그리고 이것도.AnnotatingHttpFirewall는 을(를) 제공합니다.inspect()하위 클래스가 요청 차단을 위한 사용자 지정 규칙을 추가할 수 있는 메서드입니다.

이 솔루션은 (1) Spring Security와 (2) Spring Framework(Core)의 두 부분으로 나뉩니다. 왜냐하면 이것이 애초에 이 문제를 일으킨 분열이기 때문입니다. 그리고 이것이 어떻게 이 문제를 해결해야 하는지를 보여주기 때문입니다.

참고로 이 테스트는 Spring 4.3.17 및 Spring Security 4.2.6에서 테스트되었습니다.Spring 5.1이 출시되면 상당한 변화가 있을 수 있습니다.


1부: 스프링 보안

이것은 Spring Security 내에서 로깅 및 플래그 지정을 수행하는 솔루션의 절반입니다.


Http Firewall.java 주석 달기

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.security.web.firewall.StrictHttpFirewall;

/**
 * Overrides the StrictHttpFirewall to log some useful information about blocked requests.
 */
public class AnnotatingHttpFirewall extends StrictHttpFirewall
{
    /**
     * The name of the HTTP header representing a request that has been rejected by this firewall.
     */
    public static final String HTTP_HEADER_REQUEST_REJECTED_FLAG = "X-HttpFirewall-RequestRejectedFlag";

    /**
     * The name of the HTTP header representing the reason a request has been rejected by this firewall.
     */
    public static final String HTTP_HEADER_REQUEST_REJECTED_REASON = "X-HttpFirewall-RequestRejectedReason";

    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(AnnotatingHttpFirewall.class.getName());

    /**
     * Default constructor.
     */
    public AnnotatingHttpFirewall()
    {
        super();
        return;
    }

    /**
     * Provides the request object which will be passed through the filter chain.
     *
     * @param request The original HttpServletRequest.
     * @returns A FirewalledRequest (required by the HttpFirewall interface) which
     *          inconveniently breaks the general contract of ServletFilter because
     *          we can't upcast this to an HttpServletRequest. This prevents us
     *          from re-wrapping this using an HttpServletRequestWrapper.
     */
    @Override
    public FirewalledRequest getFirewalledRequest(final HttpServletRequest request)
    {
        try
        {
            this.inspect(request); // Perform any additional checks that the naive "StrictHttpFirewall" misses.
            return super.getFirewalledRequest(request);
        } catch (RequestRejectedException ex) {
            final String requestUrl = request.getRequestURL().toString();

            // Override some of the default behavior because some requests are
            // legitimate.
            if (requestUrl.contains(";jsessionid="))
            {
                // Do not block non-cookie serialized sessions. Google's crawler does this often.
            } else {
                // Log anything that is blocked so we can find these in the catalina.out log.
                // This will give us any information we need to make
                // adjustments to these special cases and see potentially
                // malicious activity.
                if (LOGGER.isLoggable(Level.WARNING))
                {
                    LOGGER.log(Level.WARNING, "Intercepted RequestBlockedException: Remote Host: " + request.getRemoteHost() + " User Agent: " + request.getHeader("User-Agent") + " Request URL: " + request.getRequestURL().toString());
                }

                // Mark this request as rejected.
                request.setAttribute(HTTP_HEADER_REQUEST_REJECTED, Boolean.TRUE);
                request.setAttribute(HTTP_HEADER_REQUEST_REJECTED_REASON, ex.getMessage());
            }

            // Suppress the RequestBlockedException and pass the request through
            // with the additional attribute.
            return new FirewalledRequest(request)
            {
                @Override
                public void reset()
                {
                    return;
                }
            };
        }
    }

    /**
     * Provides the response which will be passed through the filter chain.
     * This method isn't extensible because the request may already be committed.
     * Furthermore, this is only invoked for requests that were not blocked, so we can't
     * control the status or response for blocked requests here.
     *
     * @param response The original HttpServletResponse.
     * @return the original response or a replacement/wrapper.
     */
    @Override
    public HttpServletResponse getFirewalledResponse(final HttpServletResponse response)
    {
        // Note: The FirewalledResponse class is not accessible outside the package.
        return super.getFirewalledResponse(response);
    }

    /**
     * Perform any custom checks on the request.
     * This method may be overridden by a subclass in order to supplement or replace these tests.
     *
     * @param request The original HttpServletRequest.
     * @throws RequestRejectedException if the request should be rejected immediately.
     */
    public void inspect(final HttpServletRequest request) throws RequestRejectedException
    {
        final String requestUri = request.getRequestURI(); // path without parameters
//        final String requestUrl = request.getRequestURL().toString(); // full path with parameters

        if (requestUri.endsWith("/wp-login.php"))
        {
            throw new RequestRejectedException("The request was rejected because it is a vulnerability scan.");
        }

        if (requestUri.endsWith(".php"))
        {
            throw new RequestRejectedException("The request was rejected because it is a likely vulnerability scan.");
        }

        return; // The request passed all custom tests.
    }
}

WebSecurityConfig.java

WebSecurityConfig 방화벽으로 설정합니다.AnnotatingHttpFirewall.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    /**
     * Default constructor.
     */
    public WebSecurityConfig()
    {
        super();
        return;
    }

    @Override
    public final void configure(final WebSecurity web) throws Exception
    {
        super.configure(web);
        web.httpFirewall(new AnnotatingHttpFirewall()); // Set the custom firewall.
        return;
    }
}

2부: 스프링 프레임워크

이 솔루션의 두 번째 부분은 다음과 같이 구현될 수 있습니다.ServletFilter또는HandlerInterceptor▁a▁path의 길을 가고 있습니다.HandlerInterceptorSpring Framework 내에서 가장 유연하고 직접적으로 작동하기 때문입니다.


요청 차단됨예외.java

이 사용자 지정 예외는 오류 컨트롤러에서 처리할 수 있습니다.이는 애플리케이션 비즈니스 로직(예: 영구 방화벽)과 관련이 있을 수 있는 원시 요청(전체 요청 자체)에서 사용 가능한 요청 헤더, 매개 변수 또는 속성을 추가하도록 확장될 수 있습니다.

/**
 * A custom exception for situations where a request is blocked or rejected.
 */
public class RequestBlockedException extends RuntimeException
{
    private static final long serialVersionUID = 1L;

    /**
     * The requested URL.
     */
    private String requestUrl;

    /**
     * The remote address of the client making the request.
     */
    private String remoteAddress;

    /**
     * A message or reason for blocking the request.
     */
    private String reason;

    /**
     * The user agent supplied by the client the request.
     */
    private String userAgent;

    /**
     * Creates a new Request Blocked Exception.
     *
     * @param reqUrl The requested URL.
     * @param remoteAddr The remote address of the client making the request.
     * @param userAgent The user agent supplied by the client making the request.
     * @param message A message or reason for blocking the request.
     */
    public RequestBlockedException(final String reqUrl, final String remoteAddr, final String userAgent, final String message)
    {
        this.requestUrl = reqUrl;
        this.remoteAddress = remoteAddr;
        this.userAgent = userAgent;
        this.reason = message;
        return;
    }

    /**
     * Gets the requested URL.
     *
     * @return A URL.
     */
    public String getRequestUrl()
    {
        return this.requestUrl;
    }

    /**
     * Gets the remote address of the client making the request.
     *
     * @return A remote address.
     */
    public String getRemoteAddress()
    {
        return this.remoteAddress;
    }

    /**
     * Gets the user agent supplied by the client making the request.
     *
     * @return  A user agent string.
     */
    public String getUserAgent()
    {
        return this.userAgent;
    }

    /**
     * Gets the reason for blocking the request.
     *
     * @return  A message or reason for blocking the request.
     */
    public String getReason()
    {
        return this.reason;
    }
}

FirewallInterceptor.java

Security 필터가 이인셉트즉스는보실필후다행호니에된됩출이터, 후다음가안링프터즉▁this▁have이().AnnotatingHttpFirewall거부해야 하는 요청에 플래그를 지정했습니다.이 인터셉터는 요청에서 이러한 플래그(속성)를 탐지하고 오류 컨트롤러에서 처리할 수 있는 사용자 지정 예외를 발생시킵니다.

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * Intercepts requests that were flagged as rejected by the firewall.
 */
public final class FirewallInterceptor implements HandlerInterceptor
{
    /**
     * Default constructor.
     */
    public FirewallInterceptor()
    {
        return;
    }

    @Override
    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception
    {
        if (Boolean.TRUE.equals(request.getAttribute(AnnotatingHttpFirewall.HTTP_HEADER_REQUEST_REJECTED)))
        {
            // Throw a custom exception that can be handled by a custom error controller.
            final String reason = (String) request.getAttribute(AnnotatingHttpFirewall.HTTP_HEADER_REQUEST_REJECTED_REASON);
            throw new RequestRejectedByFirewallException(request.getRequestURL().toString(), request.getRemoteAddr(), request.getHeader(HttpHeaders.USER_AGENT), reason);
        }

        return true; // Allow the request to proceed normally.
    }

    @Override
    public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView modelAndView) throws Exception
    {
        return;
    }

    @Override
    public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception
    {
        return;
    }
}

WebConfig.java

WebConfig을가를 FirewallInterceptor등기부에.

@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{
    /**
     * Among your other methods in this class, make sure you register
     * your Interceptor.
     */
    @Override
    public void addInterceptors(final InterceptorRegistry registry)
    {
        // Register firewall interceptor for all URLs in webapp.
        registry.addInterceptor(new FirewallInterceptor()).addPathPatterns("/**");
        return;
    }
}

오류 Controller.java

이는 특히 위의 사용자 지정 예외를 처리하며, 모든 관련 정보를 기록하고 사용자 지정 응용 프로그램 방화벽에 대한 특수 비즈니스 논리를 호출하는 동안 클라이언트에 대한 새로 고침 오류 페이지를 생성합니다.

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.web.servlet.NoHandlerFoundException;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import RequestBlockedException;

@ControllerAdvice
public final class ErrorController
{
    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(ErrorController.class.getName());

    /**
     * Generates an Error page by intercepting exceptions generated from AnnotatingHttpFirewall.
     *
     * @param request The original HTTP request.
     * @param ex A RequestBlockedException exception.
     * @return The tile definition name for the page.
     */
    @ExceptionHandler(RequestBlockedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handleRequestBlockedException(final RequestBlockedException ex)
    {
        if (LOGGER.isLoggable(Level.WARNING))
        {
            LOGGER.log(Level.WARNING, "Rejected request from " + ex.getRemoteAddress() + " for [" + ex.getRequestUrl() + "]. Reason: " + ex.getReason());
        }

        // Note: Perform any additional business logic or logging here.

        return "errorPage"; // Returns a nice error page with the specified status code.
    }

    /**
     * Generates a Page Not Found page.
     *
     * @param ex A NoHandlerFound exception.
     * @return The tile definition name for the page.
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String handleException(final NoHandlerFoundException ex)
    {
        return "notFoundPage";
    }
}

방화벽 컨트롤러.java

로, "" "" " " " " " " " " " " " " " " " " " " " " " " " " 을 사용합니다.NoHandlerFoundException이는 Dispatcher Servlet.noHandlerFound의 닭과 달걀 전략을 우회하여 해당 방법이 항상 매핑을 찾을 수 있도록 합니다.FirewallInterceptor.preHandle항상 호출됩니다.은 이이주는을 줍니다.RequestRejectedByFirewallException에 대한 우선권.NoHandlerFoundException.

이것이 필요한 이유:

여기서 언급한 바와 같이, 다음과 같은 경우.NoHandlerFoundException에서 .DispatcherServlet() "URL", "URL")에서 이 없습니다.NoHandlerFoundExceptionpreHandle()을 호출하기 전에 던져집니다. 따라서 이러한 요청은 404 보기에 포함됩니다(이 경우는 원하는 동작이 아닙니다. "No mapping found for HTTP request for URI..." 메시지가 많이 표시됩니다.이 문제는 특수 헤더에 대한 검사를 다음으로 이동하여 해결할 수 있습니다.noHandlerFoundㅠㅠ. 는 이 할 수 . 하는 것이 .유감스럽게도, 새 디스패치 서블릿을 처음부터 작성하지 않고는 이 작업을 수행할 수 없습니다. 그러면 전체 Spring Framework를 폐기하는 것이 좋습니다.확이불니다합능을 연장하는 합니다.DispatcherServlet보호된 방법, 개인 방법 및 최종 방법이 혼합되어 있고 해당 속성에 액세스할 수 없기 때문입니다(게터 또는 세터 없음).또한 구현할 수 있는 공통 인터페이스가 없기 때문에 클래스를 마무리하는 것이 불가능합니다.이 클래스의 기본 매핑은 이러한 모든 논리를 우회하는 우아한 방법을 제공합니다.

중요한 주의 사항:아래의 요청 매핑은 등록된 모든 리소스 처리기보다 우선하므로 정적 리소스를 확인할 수 없습니다.아직 해결 방법을 찾고 있지만, 이 답변에서 제안한 정적 리소스를 처리하는 방법 중 하나를 사용해 보는 것이 좋습니다.

import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.NoHandlerFoundException;

@Controller
public final class FirewallController
{
    /**
     * The name of the model attribute (or request parameter for advertisement click tracking) that contains the request URL.
     */
    protected static final String REQUEST_URL = "requestUrl";

    /**
     * The name of the model attribute that contains the request method.
     */
    protected static final String REQUEST_METHOD = "requestMethod";

    /**
     * The name of the model attribute that contains all HTTP headers.
     */
    protected static final String REQUEST_HEADERS = "requestHeaders";

    /**
     * Default constructor.
     */
    public FirewallController()
    {
        return;
    }

    /**
     * Populates the request URL model attribute from the HTTP request.
     *
     * @param request The HTTP request.
     * @return The request URL.
     */
    @ModelAttribute(REQUEST_URL)
    public final String getRequestURL(final HttpServletRequest request)
    {
        return request.getRequestURL().toString();
    }

    /**
     * Populates the request method from the HTTP request.
     *
     * @param request The HTTP request.
     * @return The request method (GET, POST, HEAD, etc.).
     */
    @ModelAttribute(REQUEST_METHOD)
    public final String getRequestMethod(final HttpServletRequest request)
    {
        return request.getMethod();
    }

    /**
     * Gets all headers from the HTTP request.
     *
     * @param request The HTTP request.
     * @return The request headers.
     */
    @ModelAttribute(REQUEST_HEADERS)
    public final HttpHeaders getRequestHeaders(final HttpServletRequest request)
    {
        return FirewallController.headers(request);
    }

    /**
     * A catch-all default mapping that throws a NoHandlerFoundException.
     * This will be intercepted by the ErrorController, which allows preHandle to work normally.
     *
     * @param requestMethod The request method.
     * @param requestUrl The request URL.
     * @param requestHeaders The request headers.
     * @throws NoHandlerFoundException every time this method is invoked.
     */
    @RequestMapping(value = "/**") // NOTE: This prevents resolution of static resources. Still looking for a workaround for this.
    public void getNotFoundPage(@ModelAttribute(REQUEST_METHOD) final String requestMethod, @ModelAttribute(REQUEST_URL) final String requestUrl, @ModelAttribute(REQUEST_HEADERS) final HttpHeaders requestHeaders) throws NoHandlerFoundException
    {
        throw new NoHandlerFoundException(requestMethod, requestUrl, requestHeaders);
    }

    /**
     * Gets all headers from a HTTP request.
     *
     * @param request The HTTP request.
     * @return The request headers.
     */
    public static HttpHeaders headers(final HttpServletRequest request)
    {
        final HttpHeaders headers = new HttpHeaders();

        for (Enumeration<?> names = request.getHeaderNames(); names.hasMoreElements();)
        {
            final String headerName = (String) names.nextElement();

            for (Enumeration<?> headerValues = request.getHeaders(headerName); headerValues.hasMoreElements();)
            {
                headers.add(headerName, (String) headerValues.nextElement());
            }
        }

        return headers;
    }
}

결과.

두 두 가 기록되는수 첫 번째는 Spring Security, 두 번째 경고는 Framework입니다).ErrorController이제 로깅을 완전히 제어할 수 있으며 필요에 따라 조정할 수 있는 확장 가능한 응용프로그램 방화벽이 있습니다.

Sep 12, 2018 10:24:37 AM com.mycompany.spring.security.AnnotatingHttpFirewall getFirewalledRequest
WARNING: Intercepted org.springframework.security.web.firewall.RequestRejectedException: Remote Host: 0:0:0:0:0:0:0:1 User Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0 Request URL: http://localhost:8080/webapp-www-mycompany-com/login.php
Sep 12, 2018 10:24:37 AM com.mycompany.spring.controller.ErrorController handleException
WARNING: Rejected request from 0:0:0:0:0:0:0:1 for [http://localhost:8080/webapp-www-mycompany-com/login.php]. Reason: The request was rejected because it is a likely vulnerability scan.

이를 처리하는 또 다른 방법은 Spring AOP를 사용하는 것입니다.RequestRejected를 포착하는 FilterChainProxy.doFilter() 메서드에 대한 조언을 만들 수 있습니다.Http Firewall에서 던져진 예외를 400 BAD_로 변환합니다.부탁한다

@Aspect
@Component
public class FilterChainProxyAdvice {

    @Around("execution(public void org.springframework.security.web.FilterChainProxy.doFilter(..))")
    public void handleRequestRejectedException (ProceedingJoinPoint pjp) throws Throwable {
        try {
            pjp.proceed();
        } catch (RequestRejectedException exception) {
            HttpServletResponse response = (HttpServletResponse) pjp.getArgs()[1]);
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
        }
    }
}

최근 이 커밋의 github 변경에서 몇 가지 작동하는 해결책을 봅니다.

의 콩을 하면 됩니다.RequestRejectedHandler아니면 제가 보기에, 다음을 통한 통합도 있을 것입니다.WebSecurityWebSecurityConfigurerAdapter안타깝게도 이 변경 사항은 2.3.3에는 포함되지 않았을 것입니다.종속성 관리를 사용하여 릴리스합니다. 5. 2Spring Security Config 5.4.0-M1입니다. 종속성 관리의 경우 버전 2.4.0-M1입니다.

조만간 이 답변을 접한 사람들은 표준 릴리스에서 이러한 변화를 보게 될 것입니다.

봄 보안 5.7.6 이 코드를 사용하여 오류를 기록하고 404 페이지로 리디렉션할 수 있었습니다.

@Bean
public RequestRejectedHandler requestRejectedHandler() {
    HttpStatusRequestRejectedHandler rejectedHandler = new HttpStatusRequestRejectedHandler() {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, RequestRejectedException requestRejectedException) throws IOException {
            log.info(requestRejectedException.getMessage() + " for " + request.getRequestURL());
            response.sendRedirect("/404");
        }
    };
    return rejectedHandler;
}

매우 간단한 방법은 web.xml을 사용하여 해당 파일에 오류 페이지를 지정하는 것입니다.

<error-page>
  <exception-type>org.springframework.security.web.firewall.RequestRejectedException</exception-type>
  <location>/request-rejected</location>
</error-page>

된 경로에 대한 을 " " " (으)로 합니다.@Controller classsublish :

@RequestMapping(value = "/request-rejected")
@ResponseStatus(HttpStatus.BAD_REQUEST)
public @ResponseBody String handleRequestRejected(
        @RequestAttribute(RequestDispatcher.ERROR_EXCEPTION) RequestRejectedException ex,
        @RequestAttribute(RequestDispatcher.ERROR_REQUEST_URI) String uri) {

    String msg = ex.getMessage();

    // optionally log the message and requested URI (slf4j)
    logger.warn("Request with URI [{}] rejected. {}", uri, msg);

    return msg;
}

우리의 것은 스프링-웹mvc (4.3.25)였습니다.릴리스) 스프링 보안 코어(4.2.13)를 사용하는 GUI.RELEASE이 문제는 RELEASE가 문자열 때문에 했습니다.";jsessionid=D3A0470674704B75756AA10F50AA2CFC"세미콜론을 매개 변수 중 하나로 사용합니다.

»org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"웹 페이지가 처음 로드될 때만 발생합니다. 이미지가 로드되지 않고 색상과 글꼴이 제대로 설정되지 않는 등 모든 종류의 CSS 형식 문제가 발생합니다.그러나 동일한 페이지를 새로 고친 후 또는 탐색 링크를 클릭하면 다음 페이지를 사용하여 모든 CSS가 올바르게 구현되어 정상적으로 로드됩니다.이 는 한, 이오입니다.RequestRejectedException통나무를 심하게 오염시켰습니다.

우리는 새로운 세션이 생성될 때 생성된 쿠키가 두 번째 이후와 같은 쿼리 문자열이 아닌 jsessionid로 세션을 처리하도록 이 문제를 처리하고 싶었습니다.

제 솔루션은 https://stackoverflow.com/a/52635656/2915705 에 설명된 대로 필터를 구현하는 것에서 파생되었지만 오류 페이지로 보내는 대신 세션을 확인하고 https://stackoverflow.com/a/4019476/2915705 에 설명된 대로 인코딩된 URL로 리디렉션했습니다.이 해결책 이후 우리는 결코 받지 못했습니다.RequestRejectedException또는 새로운 세션이나 첫 페이지 로드에 대해서도 CSS 문제가 발생합니다.

된 업트됨데.LogAndSuppressRequestRejectedExceptionFilter아래에서 확인할 수 있습니다.

@Component("logAndSuppressRequestRejectedExceptionFilter")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LogAndSuppressRequestRejectedExceptionFilter extends GenericFilterBean {
    private static final Logger logger = LoggerFactory.getLogger(LogAndSuppressRequestRejectedExceptionFilter.class);

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        try {
            HttpServletRequest httpRequest = (HttpServletRequest) req;
            HttpServletResponse httpResponse = (HttpServletResponse) res;
            HttpSession session = httpRequest.getSession();

            if (session.isNew()) {
                // New session? OK, redirect to encoded URL with jsessionid in it (and
                // implicitly also set cookie).
                logger.debug("New session - redirect to encoded url");
                httpResponse.sendRedirect(httpResponse.encodeRedirectURL(httpRequest.getRequestURI()));
                return;
            } else if (session.getAttribute("verified") == null) {
                // Session has not been verified yet? OK, mark it verified so that we don't need
                // to repeat this.
                logger.debug("Setting session to verified");
                session.setAttribute("verified", true);
                if (httpRequest.isRequestedSessionIdFromCookie()) {
                    // Supports cookies? OK, redirect to unencoded URL to get rid of jsessionid in
                    // URL.
                    logger.debug("redirect to unencoded URL to get rid of jsessionid in url");
                    httpResponse.sendRedirect(httpRequest.getRequestURI().split(";")[0]);
                    return;
                }
            }

            chain.doFilter(req, res);

        } catch (RequestRejectedException ex) {
            HttpServletRequest request = (HttpServletRequest) req;
            logger.warn("request_rejected: remote={}, user_agent={}, request_url={}", request.getRemoteHost(),
                    request.getHeader(HttpHeaders.USER_AGENT), request.getRequestURL(), ex.getMessage());
            return;
        }
    }
}

언급URL : https://stackoverflow.com/questions/51788764/how-to-intercept-a-requestrejectedexception-in-spring

반응형