XSS란?

XSS(cross-site scripting)는 웹페이지에 악의적인 스크립트 코드를 주입할 수 있는 취약점이다.

 

XSS(Cross Site Scripting) 방지를 위해 널리 쓰이는 훌륭한 lucy-xss-servlet-filter는 Servlet Filter 단에서 < 등의 특수 문자를 &lt; 등으로 변환해주며, 여러 가지 관련 설정을 편리하게 지정할 수 있어 정말 좋다.

그런데 그 처리가 form-data에 대해서만 적용되고 Request Raw Body로 넘어가는 JSON에 대해서는 처리해주지 않는다는 단점이 있다. 그래서 JSON을 주고 받는 API 서버의 경우에는 직접 처리를 해줘야 한다.

 

즉, Lucy는 RequestParameter관련한 지원만 해준다.

 

 

그래서 이를 보완하기 위해서 여러가지 방법이 있겠지만, 가장 많이 사용되는 방법인 Request를 Wrapping 하여 적용하도록 한다. (기존에 Lucy XSS Filter를 사용하고 있다는 가정하에 작성)

 

준비물 : 2개의 Java Class, web.xml, Lucy XSS Filter

 

# Lucy XSS Filter는 Maven Central Repository 에 배포되어 있다. pom.xml 에 아래와 같이 의존성을 선언한다.

1
2
3
4
5
<dependency>
    <groupId>com.navercorp.lucy</groupId>
    <artifactId>lucy-xss</artifactId>
    <version>X.X.X</version>
</dependency>
cs

 

 

RequestWrapper.java

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
 
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
 
import com.nhncorp.lucy.security.xss.XssFilter;
 
public class RequestWrapper extends HttpServletRequestWrapper {
    private byte[] b;
 
    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        XssFilter filter = XssFilter.getInstance("lucy-xss-sax.xml");
        b = new String(filter.doFilter(getBody(request))).getBytes("UTF-8");
    }
 
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bis = new ByteArrayInputStream(b);
        return new ServletInputStreamImpl(bis);
    }
 
    class ServletInputStreamImpl extends ServletInputStream {
        private InputStream is;
 
        public ServletInputStreamImpl(InputStream bis) {
            is = bis;
        }
 
        public int read() throws IOException {
            return is.read();
        }
 
        public int read(byte[] b) throws IOException {
            return is.read(b);
        }
    }
 
    public static String getBody(HttpServletRequest request) throws IOException {
        String body = null;
       BufferedReader br= null;
       StringBuilder sb= new StringBuilder();
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
               br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = br.read(charBuffer)) > 0) {
                   sb.append(charBuffer, 0, bytesRead);
                }
              
            } else {
               sb.append("");
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (br!= null) {
                try {
                   br.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        body = sb.toString();
        return body;
    }
}
 
cs

 

실제 필터링 기능을 수행하는 Java class이다. 

주의할 부분은 해당 작업을 수행할 경우 한글이 깨져서 chain.doFilter(requestWrapper, response);가 수행될 때 Json 오류가 날 수 있다.

그러므로 br= new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); 항목에 인코딩 설정을 명시해서 해당 오류를 피해 가도록 한다.

 

 

 

RequestBodyXSSFIleter.java

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class RequestBodyXSSFIleter implements Filter {
    private List<String> extUrl;
 
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
       RequestWrapper reqWrapper = null;
        String path = ((HttpServletRequest) req).getServletPath();
 
        try {
            if (!extUrl.contains(path)) {
              
               reqWrapper = new RequestWrapper(request);
               chain.doFilter(reqWrapper , response);
            } else {
                chain.doFilter(request, response);
            }
 
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String excludePattern = filterConfig.getInitParameter("extUrls");
       extUrl = Arrays.asList(excludePattern.split(","));
    }
 
    @Override
    public void destroy() {
    }
}
 
cs

 

requestBody에 XSS Filter를 적용할 경우 Multipart(일반적으로 파일 업로드) 일 때 파일이 손상되는 일이 발생한다.

이를 방지하기위해 파일을 송신하는 URL의 경우 web.xml의 init-param을 이용하여 필터링을 제외할 수 있다.

 

RequestBodyXSSFIleter의 init시 filterConfig에서 해당 파라미터에 접근하여 제외할 URL목록을 콤마(,)로 구분하여 작성 후 필터를 적용하지 않는 방법으로 우회할 수 있다.

 

 

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    <!-- Xss Filter Start -->
    <filter>
        <filter-name>xssEscapeServletFilter</filter-name>
        <filter-class>com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>xssEscapeServletFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <filter>
        <filter-name>RequestBodyXSSFilter</filter-name>
        <filter-class>com.your.pkg.path.filter.RequestBodyXSSFIleter</filter-class>
        <init-param>
            <param-name>extUrls</param-name>
            <param-value>/linkUrl1,/linkUrl2,/linkUrl2</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>RequestBodyXSSFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- Xss Filter End -->
cs

 

jQuery.ajax() 사용시 중복호출 방지하는 방법

jQuery.ajax() 함수를 버튼 이벤트 같은 곳에 걸어두었을 경우 버튼을 여러번 클릭시 request 요청이 여러번 날아가서 오동작을 일으키는 경우가 있다.

이 때 아래와 같은 방법들로 해결할 수 있다.

1. jQuery.ajax() 세팅 옵션에 async: false 추가하는 방법

jQuery.ajax() 는 default 세팅 값이 true 로 되어 있어 요청을 비동기로 처리하게 되는데 async: false 옵션을 추가하면 동기로 처리하게 된다.

동기로 처리하게되면 아까 위와 같은 상황에서 버튼을 눌러 request 요청을 날렸을 때 response 요청이 올 때까지 다른 request 요청은 받지 않게 되어 중복호출을 방지한다.

 

 

 

2. 버튼에 클릭 이벤트를 jQuery.bind(), jQuery.unbind() 로 처리하는 방법

아래와 같이 jQuery.ready() 에 클릭 이벤트를 bind 시켜놓고, 한 번 클릭시 클릭 이벤트를 unbind 시켜 ajax 요청이 끝나면 다시 bind 시키는 방법이다.

 
     $(document).ready(function() {
           $('#foo').bind('click', function() {
                 doSomething();  
           });
           var doSomething = function(){
           $('#foo').unbind('click');
           $.ajax({ 
                         type: "POST",
                         url: "some.do"
            }).done(function() {
                         $('#foo').bind('click', function() {
                                 doSomething();
                     });  
             });
}
 
 
3. jQuery Block UI Plugin을 사용하는 방법

플러그인을 사용하는 방법으로 위 플러그인을 사용하게 되면 요청이 들어왔을 때 Please wait… 와 같은 Progress 메세지를 띄우면서 페이지에서 다른 이벤트를 막아버린다.

띄울 메세지를 css를 사용하여 커스텀으로 작성할 수도 있다.

 

 

 

'Web > JQuery' 카테고리의 다른 글

checkbox checked 설정  (0) 2014.10.17
Datepicker 대만 번체 zh-TW 번역자료  (0) 2014.07.28
JavaScript Class 상속  (0) 2014.05.21
Javascript override 방법  (0) 2014.05.21
[IE9 짜증남] IE 버전 체크  (0) 2014.05.21

+ Recent posts