티스토리 뷰

반응형

서블릿 필터를 이용해서 XSS나 SqlInjection을 어떻게 처리하는지 잘 나와있는 블로그들이 있어 정리해본다. 

 

Filter란?

Servlet 2.3 Specifacation(해당 링크 문서의 챕터 6에 나와있음)에 추가된 대표적인 기능 중 하나가 필터이다.

스펙에는 아래와 같이 설명되어 있다.

A filter is a reusable piece of code that transforms either the content of an HTTP request or response and can also modify header information. 

HTTP Request 또는 HTTP Response 내용을 수정할 수 있고, HTTP 헤더 정보 또한 수정할 수 있는 재사용 가능한 코드 조각

 

그림으로 보면 다음과 같다.

위의 그림에서 보여지듯이, 필터는 HTTP 요청이 서블릿으로 가기 전에 먼저 맞닥뜨리는 놈이다. 한 가지 짚고 넘어가면, Spring MVC의 인터셉터는 HTTP 요청이 DispatcherServlet으로 들어온 후에 처리가 되는 것이고, 필터는 아에 서블릿으로 들어가기 전에 처리되는 것이다.

 

필터는 아래의 그림과 같이 여러 개를 둘 수 있는데, 이를 필터 체인이라고 부른다.

위와 같이 필터 체인을 구성할 경우, 첫 번째 필터는 클라이언트의 HTTP 요청을 입력 값으로 받지만, 두 번째 필터 이후 부터는 그 전 필터로부터 필터링된 요청 값을 입력 값으로 받는다.

 

과거에는 이러한 필터를 이용해서 인증(Authentication)과 권한(Authorization)을 체크하는 용도로 많이 사용하였다.

그 뒤로는 필터 대신 Spring 프레임워크의 인터셉터를 이용하는 방식이 많이 사용되었고, 최근에는 OAuth를 이용한 방식을 많이 사용한다.

 

인증/권한 방식의 변화

 

필터는 인증/권한 체크 외에도 XSS 방어와 같이  클라이언트의 요청에 대해 필터링이 필요하다면 사용될 수 있다.

 

필터를 통한 HTTP 요청/응답 값 변환

서블릿으로 요청 값을 전달하기 위해서는 ServletRequest 인터페이스의 구현체를 이용한다. 대개 서블릿으로의 요청은 HTTP 프로토콜을 사용하기 때문에 ServletRequest 인터페이스의 구현체 중 하나인 HttpServletRequest 클래스를 이용한다.

 

필터는 요청/응답 값을 변경하는게 목적이므로, 변환된 요청/응답 값을 담기 위한 HttpServletRequestWrapper 클래스를 제공한다.


출처: https://pangtrue.tistory.com/249


구현

출처: https://offbyone.tistory.com/32

1. web.xml 에서 필터를 등록합니다. /WEB-INF/web.xml 파일 입니다.

<filter> 
	<filter-name>firstFilter</filter-name> 
    <filter-class>com.tistory.pentode.filter.FirstFilter</filter-class> 
    <init-param> 
    	<param-name>encoding</param-name> 
        <param-value>UTF-8</param-value> 
    </init-param> 
</filter> 
<filter-mapping> 
	<filter-name>firstFilter</filter-name> 
	<url-pattern>/*</url-pattern> 
</filter-mapping>

필터를 /* 에 맵핑 했습니다.

이것은 필터가 servlet, jsp 뿐만 아니라 모든 자원의 요청에도 호출 된다는 것을 의미합니다.

 

2. 필터를 정의합니다.

src/main/java/com/tistory/pentode/filter/FirstFilter.java 파일 입니다. 이 필터에서는 하는 작업은 다음과 같습니다. 

- 필터 등록시 설정한 초기 파라미터로 POST 요청 데이터의 인코딩을 지정합니다.
 - 요청 정보에 대한 추가 처리를 위해 HttpServletRequestWrapper 를 사용하여 HttpServletRequest에서 필요한 메소드를 오버라이딩 합니다.
 - 출력되는 응답 정보를 변형하기 위해서 HttpServletResponseWrapper 를 이용하여 출력을 모으는 작업을 합니다.
 - (핵심) TestRequestWrapper에 입력파라미터에서 <와>를 제거하는 메소드가 정의되어있습니다.

public class FirstFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(FirstFilter.class);
    private String encoding;

    /**
     * init 함수는 컨텍스트 로드시 호출됩니다.
     */
    @Override
    public void init(FilterConfig config) throws ServletException {
        logger.info("init call");

        // web.xml 에서 필터 등록시 정의했던 파라미터를 가져옵니다.
        this.encoding = config.getInitParameter("encoding");
    }

    /**
     * 필터 실행 부분 입니다.
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        // Controller 처리 전에 처리를 수행 하는곳 입니다.
        // 파라미터 값으로 POST 데이터의 인코딩을 지정합니다.
        request.setCharacterEncoding(this.encoding);

        // 요청과 응답에 필요한 처리를 수행할 Wrapper를 생성합니다.
        ServletRequest requestWrapper = new TestRequestWrapper((HttpServletRequest) request);
        ServletResponse responseWrapper = new TestResponseWrapper((HttpServletResponse) response);

        logger.info("before doFilter");

        // 다음 필터의 호출, 실제 자원의 처리를 합니다.
        chain.doFilter(requestWrapper, responseWrapper);

        // 응답에 대한 처리를 하는곳 입니다.
        logger.info("after doFilter");

        // 응답 래퍼를 이용하여 출력을 모두 대문자로 변형합니다.
        if(response.isCommitted() == false) {
            response.getWriter().write(responseWrapper.toString().toUpperCase());
        }
    }

    /**
     * destroy 는 컨텍스트가 종료될 때 호출됩니다.
     */
    @Override
    public void destroy() {
        logger.info("destroy call");
    }

    /**
     * 요청 래퍼 입니다.
     */
    class TestRequestWrapper extends HttpServletRequestWrapper {

        public TestRequestWrapper(HttpServletRequest request) {
            super(request);
        }

        /**
         * 입력 파라미터에서 <, > 를 제거 합니다.
         */
        @Override
        public String getParameter(String name) {
            String value = super.getParameter(name);
            if(value == null) value = "";
            return value.replaceAll("[<>]", "");
        }
    }

    /**
     * 응답 래퍼 입니다.
     */
    class TestResponseWrapper extends HttpServletResponseWrapper {

        protected CharArrayWriter charWriter;

        public TestResponseWrapper(HttpServletResponse response) {
            super(response);
            charWriter = new CharArrayWriter();
        }

        /**
         * 출력을 나중에 수정할 수 있도록 모아둡니다.
         */
        @Override
        public PrintWriter getWriter() throws IOException {
            return new PrintWriter(charWriter);
        }

        @Override
        public String toString() {
            return charWriter.toString();
        }
    }
}

- http://localhost:8080/pentode/filter.do?param=<test> 로 요청시, 위의 필터 요청처리-> 자원의처리(컨트롤러)-> 필터 응답처리 순으로 됩니다. 결과적으로 <test>가 test로 변환됩니다.

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함