2021.08.23-개발일지
url 을 통해 filter 처리를 하는 로직에서 문제가 생긴적이 있습니다. 기본적으로 url parameter 라 표현하면 /website/example?param=1
와 같이 ?param=1
형태를 생각할 것이라 생각합니다. 그러나 해당 용어는 정식으로 query string
이라 표현하며, url parameter는 /website/example;param=1
의 형태를 의미합니다.
<scheme>://<username>:<password>@<host>:<port>/<path>;<parameters>?<query>#<fragment>
http url 은 위와 같은 형식으로 작성됩니다.
이때 내용을 구분하기 위한 몇개의 예약문자가 있으며 rfc3986 그것은 다음과 같습니다.
! * ' ( ) ; : @ & = + $ , / ? # [ ]
예약어를 사용시에는 반드시 인코딩해서 요청해야 합니다.
Request mapping
문제는 ;
사용한 url parameter가 http url 스펙이라 의도하지 않은 결과가 생기기도 합니다. 예를 들어 아래와 같이 controller 를 작성하고 url parameter 삽입하여 http 요청을 보내면 정상적으로 해당 함수를 호출하게 된다는 점 입니다.
@GetMapping("/website/example")
public String homeController() {
return LocalDateTime.now().toString();
}
GET /website;/example
GET /website/example
StrictHttpFirewall
문제는 해당 URL을 이용하여 서버에 공격을 할 수 있다는 점입니다. 만약, 서버에서 pattern match 등을 통해 string을 직접적으로 사용할 경우 filter 나 interceptor 에서 걸러지지 않은 URL 들이 controller 까지 진입하여 보안을 무력화 할 수 있습니다. 만약 프로젝트에서 spring security (4.2.4 이상)를 사용한다면 가장 간편한 방법으로는 StrictHttpFirewall
클래스를 등록하여 간편하게 처리 가능합니다.
@Bean
public HttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedDoubleSlash(false);
firewall.setAllowSemicolon(false);
return firewall;
}
기본적으로 spring secuirty 사용시 특별한 설정 없이도 해당 클래스가 빈으로 등록됩니다. 예약어들이 URI(;
, //
, \\
) 포함되었는지 판단하며 포함 시 RequestRejectedException
이 발생됩니다.
filter
만약, security를 사용하기 어려운 환경이라면 filter를 통해 직접처리하는 것도 좋습니다.
public class HttpFireWallFilter implements Filter {
private static final List<String> MALICIOUS_STRINGS = List.of(";", "//", "\\");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
for (String maliciousString : MALICIOUS_STRINGS) {
if (httpServletRequest.getRequestURI().contains(maliciousString)) {
httpServletResponse.setStatus(400);
httpServletResponse.getWriter().print("URL has malicious character");
return;
}
}
chain.doFilter(request, response);
}
}
댓글남기기