💡 핵심 요약 (TL;DR)
input태그에 스크립트 방지하는 방법(XSS 방지)의 동작 원리를 이해하고 실제 프로젝트에 적용하기 위한 실무 가이드다.
* web.xml 에 필터설정 추가을 적용할 수 있다.
프로젝트 테스트기간이라 몇가지 수정해달라는 요청받았다. 엑셀로 받았는데 수정사항 내용이 글제목에 태그가 입력됨. 으로 왔길래 무슨 말일까 고민하고있었다. 팀장님이 “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는 보안에 취약할 수 있기에 꼭 삭제해줘야한다.
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("<" , "<" ).replaceAll(">" , ">" ); returnVal = returnVal.replaceAll("\\(" , "(" ).replaceAll("\\)" , ")" ); returnVal = returnVal.replaceAll("'" , "'" ); returnVal = returnVal.replaceAll("eval\\((.*)\\)" , "" ); returnVal = returnVal.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']" , "\"\"" ); returnVal = returnVal.replaceAll("script" , "" ); returnVal = returnVal.replaceAll("iframe" , "" ); returnVal = returnVal.replaceAll("embed" , "" ); return returnVal; } }
그리고 호출은 서비스에서 진행하면된다.
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 방어
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); } }
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("<" , "<" ).replaceAll(">" , ">" ); returnVal = returnVal.replaceAll("\\(" , "(" ).replaceAll("\\)" , ")" ); returnVal = returnVal.replaceAll("'" , "'" ); returnVal = returnVal.replaceAll("eval\\((.*)\\)" , "" ); returnVal = returnVal.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']" , "\"\"" ); returnVal = returnVal.replaceAll("script" , "" ); returnVal = returnVal.replaceAll("iframe" , "" ); returnVal = returnVal.replaceAll("embed" , "" ); return returnVal; } }
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번방식으로 바꿨다!