input태그에 스크립트 방지하는 방법(XSS 방지)

프로젝트 테스트기간이라 몇가지 수정해달라는 요청받았다.
엑셀로 받았는데 수정사항 내용이 글제목에 태그가 입력됨. 으로 왔길래 무슨 말일까 고민하고있었다.
팀장님이 “XSS방어하면 돼요~”라고 말씀해준 뒤에야 이해할 수 있었다.

아 html태그를 말하는 거였구나.
태그가 입력된다는 말이 진짜 html태그가 들어간다는 말이었구나.

XSS란?

XSS란 Cross-site scriptiong에 약어로 input태그에 자바스크립트 함수등의 실행코드를 고의로 작동시켜 해당 사이트를 마음대로 컨트롤하며 Data등을 빼내갈 수 있는 공격법이다.
예를 들어 게시글을 수정할때 input태그에 <script>alert('XSS테스트');</script> 를 입력한 뒤 글 수정버튼을 클릭하면 서버사이드랜더링으로 출력되는 값을 그대로 실행하기 때문에 XSS되어 alert창이 실행된다.
간단한 alert창이어서 망정이지 DB를 접근하거나 아예 사이트를 제어하는 스크립트를 실행시키는 경우 보안상 큰 문제가 발생할 수 있다.




1 Service에서 XSS 방어하기

XSS를 방어하기 위해서 서버측에서 필터링을 걸어야한다.
구글링을 통해 다양한 방법을 찾을 수 있다.
보통 script, iframe, embed는 보안에 취약할 수 있기에 꼭 삭제해줘야한다.

  • XssUtil.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class XssUtil {

    public static String cleanXSS(String value) {
    String returnVal = value;
    returnVal = returnVal.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    returnVal = returnVal.replaceAll("\\(", "&#40;").replaceAll("\\)", "&#41;");
    returnVal = returnVal.replaceAll("'", "&#39;");
    returnVal = returnVal.replaceAll("eval\\((.*)\\)", "");
    returnVal = returnVal.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
    returnVal = returnVal.replaceAll("script", "");
    returnVal = returnVal.replaceAll("iframe", "");
    returnVal = returnVal.replaceAll("embed", "");
    return returnVal;
    }
    }

그리고 호출은 서비스에서 진행하면된다.

  • BoardServiceImpl.java
    1
    2
    3
    4
    5
    6
    7
    @Override
    @Transactional
    public void updateBoard(BoardInfo info) {
    info.setSubject(XssUtil.cleanXSS(info.getSubject()));
    // 중략
    boardDao.updateBoard(info);
    }




2 JAVA fILTER를 통해 XSS 방어

  • XSSFilter.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class XSSFilter implements Filter {

    public FilterConfig filterConfig;

    public void init(FilterConfig filterConfig) throws ServletException {
    this.filterConfig = filterConfig;
    }

    public void destroy() {
    this.filterConfig = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {

    chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);

    }
    }
  • 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
    public final class RequestWrapper extends HttpServletRequestWrapper {

    public RequestWrapper(HttpServletRequest servletRequest) {
    super(servletRequest);
    }

    public String[] getParameterValues(String parameter) {

    String[] values = super.getParameterValues(parameter);
    if (values==null) {
    return null;
    }
    int count = values.length;
    String[] encodedValues = new String[count];
    for (int i = 0; i < count; i++) {
    encodedValues[i] = cleanXSS(values[i]);
    }
    return encodedValues;
    }

    public String getParameter(String parameter) {
    String value = super.getParameter(parameter);
    if (value == null) {
    return null;
    }
    return cleanXSS(value);
    }

    public String getHeader(String name) {
    String value = super.getHeader(name);
    if (value == null)
    return null;
    return cleanXSS(value);

    }

    private String cleanXSS(String value) {
    String returnVal = value;
    returnVal = returnVal.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    returnVal = returnVal.replaceAll("\\(", "&#40;").replaceAll("\\)", "&#41;");
    returnVal = returnVal.replaceAll("'", "&#39;");
    returnVal = returnVal.replaceAll("eval\\((.*)\\)", "");
    returnVal = returnVal.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
    returnVal = returnVal.replaceAll("script", "");
    returnVal = returnVal.replaceAll("iframe", "");
    returnVal = returnVal.replaceAll("embed", "");
    return returnVal;
    }
    }
  • web.xml 에 필터설정 추가
    1
    2
    3
    4
    5
    6
    7
    8
    <filter>
    <filter-name>XSS</filter-name>
    <filter-class>com.test.api.XSSFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>XSS</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>




3 lucy 라이브러리 사용

네이버에서 만든 lucy-xss-filter(링크) 라이브러리가 있다.
링크에 접속하면 한국어로 친절히 설명되어있다.
pom.xml파일과 web.xml파일, lucy-xss-servlet-filter-rule.xml파일을 설정해주면 된다!
가장 사용하기 좋은 방법이 아닐까한다. 실제로 1번 방식으로 했다가 3번방식으로 바꿨다!