반응형
XSS(Cross Site Scripting)란?
- 웹페이지에 스크립트 코드를 삽입하여 의도하지 않은 명령을 실행시키거나 쿠키, 세션등을 탈 취 할 수 있는 취약점이다.
웹 보안 취약점 대응 방법은 보통 lucy filter와 같은 라이브러리를 적용하며, 추가로 필터로 대응되지 않는
케이스들에 대한 예외처리를 추가로 진행한다. (lucy filter의 request body, 에디터HTML 데이터)
Lucy XSS Filter 적용
- 웹어플리케이션으로 들어오는 모든 요청 파라미터에 대해 기본적인 XSS 방어 필터링을 수행한다.
- 화이트 리스트 방식의 필터.
maven pom.xml
<dependency>
<groupId>com.navercorp.lucy</groupId>
<artifactId>lucy-xss-servlet</artifactId>
<version>2.0.1</version>
</dependency>
web.xml
<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>
<filter>
<filter-name>XSS</filter-name>
<filter-class>com.diquest.common.web.CrossScriptingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XSS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
CrossScriptingFilter.java
import java.io.IOException;
import java.util.Collection;
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 javax.servlet.http.HttpServletResponse;
public class CrossScriptingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
}
}
RequestWrapper.java
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.nhncorp.lucy.security.xss.XssFilter;
public class RequestWrapper extends HttpServletRequestWrapper {
private byte[] b;
public RequestWrapper(HttpServletRequest servletRequest) throws IOException {
super(servletRequest);
XssFilter filter = XssFilter.getInstance("lucy-xss-sax.xml");
b = new String(filter.doFilter(getBody(servletRequest))).getBytes("UTF-8");
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bis = new ByteArrayInputStream(b);
return new ServletInputStreamImpl(bis);
}
class ServletInputStreamImpl extends ServletInputStream {
private InputStream is;
public ServletInputStreamImpl(InputStream bis) {
is = bis;
}
@Override
public int read() throws IOException {
return is.read();
}
@Override
public int read(byte[] b) throws IOException {
return is.read(b);
}
}
public static String getBody(HttpServletRequest request) throws IOException {
String body = null;
String bodyRes = null;
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = br.read(charBuffer)) > 0) {
sb.append(charBuffer, 0, bytesRead);
}
} else {
sb.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = sb.toString();
return body;
}
}
lucy-xss.xml 설정 파일
- 보통은 resource폴더 하위에 알맞게 위치시키며, 설정 파일은 아래의 첨부파일 참고.
추가 예외케이스
- 에디터 사용 시, 내용은 html형태로 저장된다. 단순히 html entity 변환하는것에 초첨을 맞추다보면,
img태그나 기타 에디터 기능들이 동작을 안하는 경우가 발생한다.
때문에, 스크립트 공격이 있을 수 있는 케이스를 블랙리스트로 추가로 관리한다.
public static String[] eventArray = {"onclick", "ondblclick", "onmouseover", "onmouseout",
"onchange", "onmousedown", "onmouseenter", "onmouseleave", "onmousemove", "onmouseup",
"onfocus", "onfocusin", "onfocusout", "ondrag", "ondragleave", "ondragenter", "ondragover",
"ondragdrop", "ondragstart", "ondragend", "onload", "onsubmit", "onunload", "onmousewheel",
"onkeyup", "onkeypress", "onkeydown", "onpointercancel", "onpointerdown", "onpointerenter",
"onpointerleave", "onpointermove", "onpointerout", "onpointerover", "onpointerup",
"onpointerup", "onscroll", "onresize", "onselect", "onwheel", "oninput", "onblur",
"onauxclick", "onreset", "confirm", "alert", "console.log", "onerror", "prompt",
"oncontextmenu", "innerHTML", "eval", "onactive", "ondataavailable", "oncut", "onafterupdate",
"onbeforeactivate", "onbeforedeactivate", "onbeforecut", "onbounce", "onbeforecopy",
"ondblclick", "ondeactivate", "ondatasetchanged", "onbeforeprint", "onbeforepaste",
"onbeforeupload", "onselectsatrt", "onpaste", "onpropertychange", "ondrop",
"ondatasetcomplete", "onmove", "oncellchange", "onfinish", "onstop", "onlayoutcomplete",
"onrowexit", "onbefore", "onstart", "onrowinserted", "onrowdelete", "onfilterchange",
"oncontrolselected", "onlosecapture", "onrowenter", "onhelp", "onreadystatechange",
"onrepeat", "onerrorupdate", "onselectionchange"};
public static String checkScriptEvent(String s){
for(String eventStr : eventArray){
if(s.contains(eventStr)) {
s = s.replaceAll(eventStr + "=\".*?\"|" + eventStr + "='.*?'|" + eventStr + "=".*?"|" + eventStr + "=.*?|" + eventStr, "");
for (String dtEventStr : eventArray) {
if (s.contains(dtEventStr) && eventStr.equals(dtEventStr)) {
s = checkScriptEvent(s);
}
}
}
}
return s;
}
checkScriptEvent에서 재귀형태로 호출 하는 이유는 블랙리스트함수를 반복해서 사용할때도 제외하기 위함.
ex) ononerrorerror, onononscrollscrollscroll
반응형
'Back > Spring Java' 카테고리의 다른 글
Spring ssl cors설정 403오류 (0) | 2022.04.08 |
---|---|
Spring class 동적 로딩 (Singleton) (1) | 2021.04.05 |
PreparedStatement 대량 insert(addBatch, executeBatch) (0) | 2021.03.10 |
특정일로 부터 날짜 계산 (Java) (0) | 2020.07.28 |
압축파일 생성 (0) | 2020.07.03 |