본문 바로가기
Programming/Java_Spring

XSS(Cross Site Script) Filter.

by 곰네Zip 2022. 11. 9.

웹에서 텍스트를 입력받다보면 다음과 같이 입력 필드 (여기서는 설명이라고 적은)에 저렇게 script코드를 넣으면 스크립트 실행이 가능하다.

설명 : [ <script>alert('hello');</script> ]

이걸 막아야지.. 막기위해 가장 좋은 방법은..

&나 <, >등의 특수 기호를 &amp;, &lt;, &gt;등으로 치환하면 된다.

브라우저에서는 그 문자열들을 보고 기호로 다시 복원해주니까.

 

그러면 이러한 입력이 오는곳 모든 곳 마다 다 문자열 치환하는 코드를 구현한다면? 해결될거같긴 하다.

근데 엄청 비효율적이다. 유틸리티처럼 뽑아낸다 해도, 매번 입력받는 곳 마다 저걸 호출해야할테니까.

다행히, Spring에서는 Filter를 적용할 수 있다. Interceptor도 있지만 우선 URL이 호출되어 먼저 만나는 녀석이 Filter니까.. Filter를 하나 추가하여, 해당 필터에서 문자 치환하면 된다.

public class MyTagFilter implements Filter{
	private FilterConfig config;
    
    @Override
    public void destroy(){
    	...
    }
    
    @Override
    public void init(FilterConfig config) throws ServletException {
    	this.config = config;
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    							throws IOException, ServletException{
        chain.doFilter(new MyTagFilterWrapper((HttpServletRequest)request), response);                        
    }
}

이렇게 필터를 만들고...

근데 무언가 할 일을 하는 애가 안보인다? 치환은 wrapper에서..

그럼 이제 wrapper를 만들자.

public class MyTagFilterWrapper extends HttpServletRequestWrapper{
	public MyTagFilterWrapper(HttpServletRequest request){
    	super(request);
    }
    
    @Override
    public String getParameter(String parameter){
    	return this.convertEscape(super.getParameter(parameter));
    }
    
    @Override
    public String[] getParameterValues(String parameter){
    	String[] values = super.getParameterValues(parameter);
        for( int i = 0 ; i < values.length ; i++){
        	if( values[i] != null){
            	values[i] = this.convertEscape(values[i]);
            }
        }
        return values;
    }
    
    private String convertEscape(String value){
    	if( value != null){
        	StringBuffer sb = new StringBuffer();
            for( int i = 0 ; i < value.length() ; i++){
            	char c = value.charAt(i);
                switch(c){
                    case '<':
                        sb.append("&lt;");
                        break;
                    case '>':
                        sb.append("&gt;");
                        break;
                        ...
                }
            }   
            value = sb.toString();
        }
        return value;
    }
}

 이렇게 치환할 wrapper를 다 만들었으면.. 그 다음은 필터 사용을 등록하자.

filter사용은 web.xml에 추가하면 된다. (다른 필터도 적용할거면 순서를 잘 정의해서 기입하자.)

 <filter>
    <filter-name>multipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
    <filter-name>MyTagFilter</filter-name>
    <filter-class>내패키지이름.MyTagFilter</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>MyTagFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 위에 multipart는 뭐냐고?

 해당 multipart가 없으면.. form으로 전송되는 데이터에 대해서는 filter가 적용되지 않는다.

그래서 그걸 위해 저 필터를 먼저 추가한다.

그리고.. multipart를 추가하면 톰캣의 Context.xml (또는 Server.xml)의 Context에 다음 한줄을 추가해주어야한다.

<Context allowCasualMultipartParsing="true">
	...
</Context>

allowCasualMultipartParsing="true"를 꼭 해줄것.

반응형

댓글