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

카테고리:

업데이트:

댓글남기기