프로젝트 댓글에, 누군가 XSS 공격을 해놓았다.. 껄껄껄
이론으로만 알던걸 직접 당해보니 기분이 새롭고 상쾌하다
해결하자!!
1.XSS 란
2. Spring에서 Xss 해결하기
1. XSS 란
Crooss-site Scripting으로, 클라이언트가 입력값으로 악의적인 스크립트 문을 삽입하여, 개발자의 의도대로 동작하지 않게하는 공격을 말합니다.
바로 아래처럼,,..!! ㅠㅠ
2. Spring에서 Xss 해결하기
XSS 공격을 예방하는 방법으로는,
- 특정문자 치환하기
- 특정문자 입력시 검사 후, 입력받지 않게하기
- 모든 body태그에 C:out 입력하기.. 등등
하지만! 네이버에서 이 기능등을, lucy xss servlet filter라는 정의해둔 라이브러리가 존재한다, 이걸 프로젝트에 적용해서 사용해보자
나는 Spring boot Gradle 프로젝트에 Lucy Xss servlet filter를 적용해보았다.
1) 라이브러리 설치
먼저 라이브러리를 설치해보자
build.gradle
//xss예방 - 네이버 filter
implementation 'com.navercorp.lucy:lucy-xss-servlet:2.0.0'
implementation 'com.navercorp.lucy:lucy-xss:1.6.3'
//StringEscapeUtils 를 사용하기 위해 commons-text 의존성 추가
implementation 'org.apache.commons:commons-text:1.8'
2) 그 다음, src/main/resource 폴더 바로 아래 xml 파일을 만들어줘야한다.
lucy-xss-servlet-filter-rule.xml
<?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>
</defenders>
<!-- default defender 선언, 별다른 defender 선언이 없으면 default defender를 사용해 필터링 한다. -->
<default>
<defender>xssPreventerDefender</defender>
</default>
<!-- url 별 필터링 룰 선언 -->
<url-rule-set>
<!-- url disable이 true이면 지정한 url 내의 모든 파라메터는 필터링 되지 않는다. -->
<url-rule>
<url disable="true">/</url>
</url-rule>
<!-- <url-rule>
<url disable="true">/app</url>
</url-rule> -->
<!-- 설정된 param은 필터링에서 제외된다. -->
<url-rule>
<url>/app/admin</url>
<params>
<param name="title" useDefender="false" />
</params>
</url-rule>
</url-rule-set>
</config>
3) 그 다음, 어플리케이션에, 필터를 등록해준다, (Lucy filter가 작동하도록)
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- xss servlet filter -->
<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>
</web-app>
4) 인자, 스프링에서 동작할 필터를 만들어준다,
WebConfig.java
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final ObjectMapper objectMapper;
//Lucy Xss filter 적용
@Bean
public FilterRegistrationBean xssFilterBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new XssEscapeServletFilter());
registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
registrationBean.addUrlPatterns("*.do", "*.jsp");
return registrationBean;
}
}
이렇게 하면 루시 필터 적용이 끝난다!
하지만,,
lucy filter 적용 안될경우, Json 형식으로 받는건 아닌지 의심해 보아야한다.
lucy xss servlet filter는 @requestBody를 필터링 하지 못한다.
나 같은 경우, 컨트롤러에서 RequsetBody로 DTO를 받아오기때문에, Json 형식이라 루시필터가 적용이 되지않았다.
아래의 블로그글을 참고하여, json 형식의 Xss 공격을 해결해보았다.
https://jojoldu.tistory.com/470
5) XSS Json 공격 막기 (Requestbody)
WebConfig.java
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final ObjectMapper objectMapper;
//Lucy Xss filter 적용
@Bean
public FilterRegistrationBean xssFilterBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new XssEscapeServletFilter());
registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
registrationBean.addUrlPatterns("*.do", "*.jsp");
return registrationBean;
}
//requestBody xss 필터 적용(json/api)
@Bean
public MappingJackson2HttpMessageConverter jsonEscapeConverter() {
ObjectMapper copy = objectMapper.copy();
copy.getFactory().setCharacterEscapes(new HtmlCharacterEscapes());
return new MappingJackson2HttpMessageConverter(copy);
}
}
HtmlCharacterEscapes.java
public class HtmlCharacterEscapes extends CharacterEscapes {
private final int[] asciiEscapes;
public HtmlCharacterEscapes() {
// 1. XSS 방지 처리할 특수 문자 지정
asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
asciiEscapes['<'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['>'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['\"'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['('] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes[')'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['#'] = CharacterEscapes.ESCAPE_CUSTOM;
asciiEscapes['\''] = CharacterEscapes.ESCAPE_CUSTOM;
}
@Override
public int[] getEscapeCodesForAscii() {
return asciiEscapes;
}
@Override
public SerializableString getEscapeSequence(int ch) {
return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch)));
}
}
위처럼하면, JSON 문자열 입력값을 Response를 클라이언트로 내보내는 단계에서 escape 할수 있다.
하지만, LocalDate 데이터타입같은 경우는, 거르지 못하기 때문에 추가 설정이 필요하다
AppConfig.java
@Configuration
public class AppConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
return new Jackson2ObjectMapperBuilder()
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.modules(new JavaTimeModule())
.timeZone("Asia/Seoul")
.build();
}
}
Spring boot XSS JSON 해결!
네이버 깃헙: https://github.com/naver/lucy-xss-servlet-filter
lucy 사용법 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=jomer&logNo=221475344347
'Spring > Spring Boot' 카테고리의 다른 글
[JPA] Fetch join 과 Join 차이점 (0) | 2022.04.20 |
---|---|
[Spring] 스프링 부트 JPA 페이징 성능 개선 - querydsl 페이지네이션(오프셋 페이징, 커서 페이징, querydsl 정렬) (0) | 2022.04.10 |
[Spring] JPA N+1 문제 해결방법(지연로딩 N+1, OneToMany, ManyToOne 연관관계, fetch join, 페이지네이션) (0) | 2022.03.30 |
[Spring] JPA 즉시 로딩과 지연 로딩 (0) | 2022.03.23 |
getter setter를 사용하는 이유 (0) | 2022.02.24 |