Web XSS-Servlet-Filter 적용하기
XSS 란?
Cross-site Scripting. 링크 타고 들어갔는데 교차사이트 스크립팅 방지한다고 안 되면 진짜 짜증난다.
SQL injection과 함께[1] 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며,[2] 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.
크로스 사이트 스크립팅이란 이름 답게, 자바스크립트를 사용하여 공격하는 경우가 많다. 공격 방법이 단순하고 가장 기초적이지만, 많은 웹사이트들이 XSS에 대한 방어 조치를 해두지 않아 공격을 받는 경우가 많다. 여러 사용자가 접근 가능한 게시판 등에 코드를 삽입하는 경우도 많으며, 경우에 따라서는 메일과 같은 매체를 통해서도 전파된다.
물론, HTML을 사용하는 것이기 때문에, Text-Only 게시판이나, BBCode를 이용하는 위키위키 등에서는 XSS가 발생할 일은 없다. 단, 나무위키의 경우 {{{#!html HTML}}}
을 이용해서 HTML 태그를 사용할 수 있으므로 취약점이 있을 수도 있다. 그래서 나무위키 초반에는 스크립트 태그와 이벤트 속성도 막지 않았다. 물론, 이는 후에 대부분 수정되었다.
주로 CSRF를 하기 위해서 사용되기 때문에 종종 CSRF와 혼동되는 경우가 있으나, XSS는 자바스크립트를 실행시키는 것이고, CSRF는 특정한 행동을 시키는 것이므로 다르다.
3.1. 스크립트 태그[편집]
방법 | 스크립트 태그로 자바스크립트를 실행한다. |
예제 |
|
설명 | 스크립트 태그의 스크립트를 실행시킨다. 안타깝게도, 매우 정직한 방법이라 대부분의 사이트에서 막는 경우가 많다. 브라우저단에서 필터링 해주는 경우도 있다. |
3.2. 자바스크립트 링크[편집]
방법 | 링크 태그로 자바스크립트를 실행한다. |
예제 |
|
설명 | 브라우저에서 |
3.3. 이벤트 속성[편집]
방법 | 이벤트 속성을 사용한다. |
예제 |
|
설명 | 이벤트 속성으로 스크립트를 실행할 수 있다. 주로 |
3.4. 블랙리스트 우회[편집]
방법 | 알려지지 않은 태그와 속성들을 사용한다. |
예제 |
|
설명 | 블랙 리스트 방식으로 막는 사이트에 사용할 수 있다. 이벤트 속성 목록을 참고하자. 위의 방법들보다는 적게 막혔으나, 여전히 최근 웹사이트들에선 화이트리스트 방식 차단이 대부분이라, 막혔을 가능성이 높다. |
3.5. 내용 난독화[편집]
방법 | 따옴표로 감싸는 문자열 사이에 공백 문자들을 넣고, HTML 인코드를 하여 난독화한다. |
예제 |
|
설명 | 일부 브라우저에서 |
-나무 위키 발췌-
위와 같이 공격할 수 있다는 내용이다.
필자는 Spring-Boot를 사용하기 때문에 적용방법을 적어보자 한다.
naver에 lucy-xss-servlet-filter를 적용하고자 한다.
깃헙 주소 : https://github.com/naver/lucy-xss-servlet-filter
먼저 pom.xml에 디펜던시를 추가하거나 jar 파일을 받아 라이브러리 빌드패스에 추가한다.
<dependency> <groupId>com.navercorp.lucy</groupId> <artifactId>lucy-xss-servlet</artifactId> <version>2.0.0</version> </dependency>
%%% 주의 사항 %%%
프로젝트 중 소켓통신이나 json데이터가 있을경우 json데이터 안에 있는 xss필터링 문자열도 걸러내는 경우가 생긴다. 해당 글은 이것을 해결하고 올린 글이다.
먼저 spring-boot의 핵심인 빈을 추가한다. XssServletFilter 클래스는 아래에 나온다.
1. WebMvcConfig.java
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter
...
...
import 관련
org.springframework.boot.web.servlet.FilterRegistrationBean
org.springframework.boot.web.servlet.FilterRegistrationBean.FilterRegistrationBean()
/*
* lucy-xss-filter
*
* */
@Bean
public FilterRegistrationBean getFilterRegistrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new XssServletFilter(acceptUrls));
registrationBean.setOrder(1);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
/*
* lucy-xss-filter
*
* */
@Bean
public FilterRegistrationBean getFilterRegistrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new XssServletFilter(acceptUrls));
registrationBean.setOrder(1);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
...
...
2. XssServletFilter.java
import java.io.IOException;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.navercorp.lucy.security.xss.servletfilter.XssEscapeFilter;
import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilterWrapper;
public class XssServletFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(XssServletFilter.class);
private String[] acceptUrls;
public XssServletFilter(String[] acceptUrls){
this.acceptUrls = acceptUrls;
}
private XssEscapeFilter xssEscapeFilter = XssEscapeFilter.getInstance();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(excludeUrl(request)){
chain.doFilter(request, response); //걸러내는 URI일 경우 요청값 그대로 처리
}else{
chain.doFilter(new XssEscapeServletFilterWrapper(request, xssEscapeFilter), response);
}
}
private boolean excludeUrl(ServletRequest request) {
String uri = ((HttpServletRequest) request).getRequestURI().toString().trim();
logger.info("XssServletFilter Uri : {}", uri);
boolean returnValue = false;
for(String url : this.acceptUrls) {
if(uri.startsWith(url)){
returnValue = true;
}
}
return returnValue;
}
@Override
public void destroy() {
}
}
3. lucy-xss-servlet-filter-rule.xml
src/main/resources 에 넣어야함.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://www.navercorp.com/lucy-xss-servlet">
<defenders>
<!-- XssPreventer 등록 -->
<defender>
<name>xssPreventerDefender</name>
<class>com.navercorp.lucy.security.xss.servletfilter.defender.XssPreventerDefender</class>
</defender>
<!-- XssSaxFilter 등록 -->
<defender>
<name>xssSaxFilterDefender</name>
<class>com.navercorp.lucy.security.xss.servletfilter.defender.XssSaxFilterDefender</class>
<init-param>
<param-value>lucy-xss-sax.xml</param-value> <!-- lucy-xss-filter의 sax용 설정파일 -->
<param-value>false</param-value> <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
</init-param>
</defender>
<!-- XssFilter 등록 -->
<defender>
<name>xssFilterDefender</name>
<class>com.navercorp.lucy.security.xss.servletfilter.defender.XssFilterDefender</class>
<init-param>
<param-value>lucy-xss.xml</param-value> <!-- lucy-xss-filter의 dom용 설정파일 -->
<param-value>false</param-value> <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
</init-param>
</defender>
</defenders>
<!-- default defender 선언, 필터링 시 지정한 defender가 없으면 여기 정의된 default defender를 사용해 필터링 한다. -->
<default>
<defender>xssPreventerDefender</defender>
</default>
<!-- global 필터링 룰 선언 -->
<global>
<!-- 모든 url에서 들어오는 globalParameter 파라메터는 필터링 되지 않으며
또한 globalPrefixParameter1로 시작하는 파라메터도 필터링 되지 않는다.
globalPrefixParameter2는 필터링 되며 globalPrefixParameter3은 필터링 되지 않지만
더 정확한 표현이 가능하므로 globalPrefixParameter2, globalPrefixParameter3과 같은 불분명한 표현은 사용하지 않는 것이 좋다. -->
<params>
<!-- <param name="JSONDS1" useDefender="false" /> -->
</params>
</global>
<!-- url 별 필터링 룰 선언 -->
<url-rule-set>
<!-- url disable이 true이면 지정한 url 내의 모든 파라메터는 필터링 되지 않는다. -->
<!-- <url-rule> -->
<!-- <url disable="true">/eform/Clip.jsp</url> -->
<!-- <url disable="true">/ajax/eformUserData</url> -->
<!-- </url-rule> -->
</url-rule-set>
</config>
4. application.properties
xss필터를 타지 않게 할 url 설정
#xss accept uri
xss.acceptUrls = /test, /admin