blog

バックエンドのXSS攻撃対策

もしパラメータを変更したいのであれば、クラスをデコレーションすることで、 必要に応じてデコレーション・クラスのメソッドを書き換えるだけです。...

Mar 30, 2020 · 5 min. read
Share this

フィルタでのリクエストパラメータのフィルタリング

Filterコンフィギュレーションは次のとおりです

@WebFilter(urlPatterns = {"/web/*"},filterName = "xssFilter") //urlPatternsインターセプトされるアドレスに対して
public class XssFilter implements Filter {
 //アドレスをインターセプトする必要はない。一般に、支払いコールバックは除外する。
 private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("/pay/notifyWX"
 ,"/pay/notifyALI"
 ,"/express/notify/callback")));
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 // TODO Auto-generated method stub
 }
 @Override
 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
 throws IOException, ServletException {
 HttpServletRequest request = (HttpServletRequest)req;
 HttpServletResponse response = (HttpServletResponse)resp;
 //リクエスト・アドレスを取得し、フィルターする必要のないアドレスを除外する。
 String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
 boolean allowedPath = ALLOWED_PATHS.contains(path);
 if(allowedPath) {
 chain.doFilter(request, response);
 }else {
 chain.doFilter(new XSSRequestWrapper(request), response);
 }
 }
 @Override
 public void destroy() {
 // TODO Auto-generated method stub
 }
}

RequestWrapperコンフィギュレーションは次のとおりです

public class XSSRequestWrapper extends HttpServletRequestWrapper {
 public XSSRequestWrapper(HttpServletRequest request) {
 super(request);
 }
 /**
 * 配列パラメーターの特殊文字フィルタリング
 */
 @Override
 public String[] getParameterValues(String name) {
 String[] values = super.getParameterValues(name);
 if (values == null) {
 return null;
 }
 int count = values.length;
 String[] encodedValues = new String[count];
 for (int i = 0; i < count; i++) {
 encodedValues[i] = clearXss(values[i]);
 }
 return encodedValues;
 }
 /**
 * パラメーター中の特殊文字のフィルタリング
 */
 @Override
 public String getParameter(String name) {
 String value = super.getParameter(name);
 if (value == null) {
 return null;
 }
 return clearXss(value);
 }
 /**
 * 属性の取得、特殊文字のフィルタリング
 */
 @Override
 public Object getAttribute(String name) {
 Object value = super.getAttribute(name);
 if (value instanceof String) {
 clearXss((String) value);
 }
 return value;
 }
 /**
 * リクエストヘッダ中の特殊文字をフィルターする
 */
 @Override
 public String getHeader(String name) {
 String value = super.getHeader(name);
 if (value == null) {
 return null;
 }
 return clearXss(value);
 }
 /**
 * 特殊文字のためにボディをフィルターする
 * @return
 * @throws IOException
 */
 @Override
 public ServletInputStream getInputStream() throws IOException {
 StringBuilder responseStrBuilder = new StringBuilder();
 BufferedReader streamReader = new BufferedReader(new InputStreamReader(super.getInputStream(), StandardCharsets.UTF_8));
 String inputStr;
 while ((inputStr = streamReader.readLine()) != null)
 responseStrBuilder.append(inputStr);
 if (SystemUtils.isNotEmpty(responseStrBuilder.toString())) {
 String clearXss = clearXss(responseStrBuilder.toString());
 final ByteArrayInputStream bais = new ByteArrayInputStream(clearXss.getBytes(StandardCharsets.UTF_8));
 return new ServletInputStream() {
 @Override
 public boolean isFinished() {
 return false;
 }
 @Override
 public boolean isReady() {
 return false;
 }
 @Override
 public void setReadListener(ReadListener readListener) {
 }
 @Override
 public int read() throws IOException {
 return bais.read();
 }
 };
 }
 return super.getInputStream();
 }
 /**
 * @throws
 * @Title: clearXss
 * @Description: xss攻撃ハンドリング
 * @param: @param value
 * @param: @return
 * @return: String
 */
 private String clearXss(String value) {
 if (SystemUtils.isEmpty(value)) {
 return value;
 }
 // 
 value = new String(value.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
 return XssFilterUtil.stripXss(value);
 }
}

Xssツール・クラスは、次のように設定します

@Slf4j
public class XssFilterUtil {
 private static List<Pattern> patterns = null;
 
 private static List<Object[]> getXssPatternList() {
 List<Object[]> ret = new ArrayList<Object[]>();
 ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE});
 ret.add(new Object[]{"</script>", Pattern.CASE_INSENSITIVE});
 ret.add(new Object[]{"<script(.*?)>",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
 ret.add(new Object[]{"eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
 ret.add(new Object[]{"expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
 ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE});
 ret.add(new Object[]{"<("[^"]*"|\'[^\']*\'|[^\'">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
 ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
 ret.add(new Object[]{"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
 return ret;
 }
 private static List<Pattern> getPatterns() {
 if (patterns == null) {
 List<Pattern> list = new ArrayList<Pattern>();
 String regex = null;
 Integer flag = null;
 int arrLength = 0;
 for(Object[] arr : getXssPatternList()) {
 arrLength = arr.length;
 for(int i = 0; i < arrLength; i++) {
 regex = (String)arr[0];
 flag = (Integer)arr[1];
 list.add(Pattern.compile(regex, flag));
 }
 }
 patterns = list;
 }
 return patterns;
 }
 public static String stripXss(String value) {
 System.out.println(value);
 if(SystemUtils.isNotEmpty(value)) {
 Matcher matcher = null;
 for(Pattern pattern : getPatterns()) {
 matcher = pattern.matcher(value);
 //  
 if(matcher.find()) {
 // 関連する文字列を削除する
 value = matcher.replaceAll("");
 }
 }
 value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
 //特別なシンボルを削除する
 String specialStr = "%20|=|!=|-|--|;|'|"|%|#|+|//|/| |\\|<|>|(|)|{|}";
 for (String str : specialStr.split("\\|")) {
 if(value.indexOf(str) > -1) {
 value = value.replaceAll(str, "");
 }
 }
 }
 if (log.isDebugEnabled())
 log.debug("strip value: " + value);
 return value;
 }
}
Read next

REPプロジェクトとの付き合い方

WBTCとTBTCの競合として、RENは仕掛ける価値があるため、REPは当面考慮せず、MLN、GNO、REPも同様。 ZRXについては、0x Realyerを使用した総取引量は1日平均300万ドルから400万ドルに達する可能性があります。同時に、ZRX...

Mar 30, 2020 · 1 min read