에디터 XSS 보안 취약점 조치
🎈 개요
보안 취약점 점검으로 현재 이용중인 에디터 Summernote 사용시 XSS 공격에 취약하다는 결과를 받았다
이 XSS(크로스사이트 스크립팅)가 어떻게 발생하는지 예를 들자면
위지윅 에디터를 기반으로 글을 작성할때 태그를 포함한 Text 정보가 DB에 저장되는데
이 때 Text 정보를 변조해 <script> alert("XSS"); </script> 이런 정보를 끼워 넣어
강제적으로 스크립팅을 실행시킬 수 있는 취약점이 생길 수 있다
즉, CKEditor, SmartEditor 등 HTML기반의 에디터를 쓴다면 꼭 고려해야하는 보안 이슈라는 것
각 에디터에서 자체적으로 XSS를 방지하는 기능을 제공하기도 하지만
서버로 넘어가는 패킷을 가로채 데이터를 변조할 수 있으니 서버단에서 확실히 잡아줘야했다
웹 프록시 툴 Burp Suite 을 사용해서 서버로 넘어가는 데이터들을 확인하고 변조하며 테스트를 진행했다
(프록시 툴 다운로드 링크 : https://portswigger.net/burp/communitydownload)
☔ 문제가 발생하고 있는 부분들
1. 에디터 사용할 때 - 글 작성, 글 수정
2. 조회 기능 - 게시글 제목으로 조회 등
기타. input type이 text인 value를 서버로 넘기는 액션이 있는 곳
위의 부분이 포함되지 않은 홈페이지는 아마 없을 것이다
프로젝트는 스프링 전자정부 프레임워크로 개발했다
🎈 lucy 시도
처음 테스트한 라이브러리는 lucy-filter인데 테스트했을 때 에디터를 제외하곤 모든 XSS 필터링이 잘 되었다
설정도 간편하고 믿고 쓸만한 라이브러리라 부담감도 없었지만...
json을 주고받는 화면단이 원치않는 필터링으로 인해 기능에 에러가 생겨서 결국 뺐다
(구글링 해보니 비슷한 에러를 겪는 사람들이 많아 보였다)
🎈 Jsoup 시도
두번째 테스트는 Jsoup을 이용했다
먼저 메이븐 의존성 추가해주고
<!-- xss 방지 jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.3</version>
</dependency>
사용법은 아래와 같다 (컨트롤러든 서비스쪽이든 상관없다)
// 클라이언트에서 넘어온 게시글 내용 - @RequestParam String boardContent
String safeContent = Jsoup.clean(boardContent, Whitelist.relaxed());
// safeContent는 Jsoup으로 필터링 된 게시글 내용
// 아래 insert문 실행
// .....
Jsoup의 clean이라는 메소드를 사용해서 필터링을 해주면 되는데
파라미터는 필터링할 String, 화이트리스트 정보 2개이며
두번째 파라미터가 담고 있는 정보 (허용하는 태그)를 이용하여 나머지 태그들을 제거해준다
위에서는 relaxed() 리스트로 필터링했는데 다양한 리스트가 존재한다.
Whitelist.basic() 은 미리 만들어져있는 기본형 화이트 리스트이고
Whitelist.basicWithImages() 는 위의 basic리스트에서 img와 관련된 태그들을 추가로 허용한 것이다
Whitelist.relaxed() 도 미리 만들어져 있는 화이트 리스트인데 HTML에디터에서 주로 활용되는 태그들이 포함되어있어 가장 완화된 필터링 리스트라고 보면 된다
Whitelist.none() 은 텍스트 외 모든 태그를 허용하지 않는 리스트라 제목, 댓글, 검색조건 에 활용하기 좋다
각자 허용하는 태그들의 상세 리스트는 아래 소스로 첨부하였다
public static Whitelist none() {
return new Whitelist();
}
public static Whitelist basic() {
return new Whitelist()
.addTags(
"a", "b", "blockquote", "br", "cite", "code", "dd", "dl", "dt", "em",
"i", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong", "sub",
"sup", "u", "ul")
.addAttributes("a", "href")
.addAttributes("blockquote", "cite")
.addAttributes("q", "cite")
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
.addProtocols("blockquote", "cite", "http", "https")
.addProtocols("cite", "cite", "http", "https")
.addEnforcedAttribute("a", "rel", "nofollow")
;
}
public static Whitelist basicWithImages() {
return basic()
.addTags("img")
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
.addProtocols("img", "src", "http", "https")
;
}
public static Whitelist relaxed() {
return new Whitelist()
.addTags(
"a", "b", "blockquote", "br", "caption", "cite", "code", "col",
"colgroup", "dd", "div", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6",
"i", "img", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong",
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u",
"ul")
.addAttributes("a", "href", "title")
.addAttributes("blockquote", "cite")
.addAttributes("col", "span", "width")
.addAttributes("colgroup", "span", "width")
.addAttributes("img", "align", "alt", "height", "src", "title", "width")
.addAttributes("ol", "start", "type")
.addAttributes("q", "cite")
.addAttributes("table", "summary", "width")
.addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width")
.addAttributes(
"th", "abbr", "axis", "colspan", "rowspan", "scope",
"width")
.addAttributes("ul", "type")
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
.addProtocols("blockquote", "cite", "http", "https")
.addProtocols("cite", "cite", "http", "https")
.addProtocols("img", "src", "http", "https")
.addProtocols("q", "cite", "http", "https")
;
}
만약 커스텀해서 추가하고 싶다면
String safeContent = Jsoup.clean(content, //필터링할 내용
Whitelist.relaxed() //white list 설정
.addTags("iframe") //허용할 태그 추가
.addAttributes("iframe", "frameborder", "src", "width", "height", "class") //허용할 태그의 속성 추가
.addAttributes("p", "style")
.addAttributes("span", "style")
.addAttributes("font", "face")
.addProtocols("iframe", "src", "https", "http")); //허용할 태그의 프로토콜 추가
이런식으로 whitelist 뒤에 addTags, addAttributes, addProtocols를 이용하여 추가해주면 된다
예시)
Tags는 허용할 태그 ex) <p> </p> - addTags("p")
Attributes는 태그의 허용할 속성 ex) <p style="color:blue"> </p> - addAttributes("p", "style")
Protocols는 태그의 속성중 허용할 프로토콜 ex) iframe 예시는 티스토리에서 텍스트로 못적는구나..
끘!