"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

오늘이군

@RequestBody Json 출력필터 적용 - xss 본문

삶../프로그래밍

@RequestBody Json 출력필터 적용 - xss

오늘이군 2020. 5. 19. 10:35
반응형

이전 글에서 Request 를 Wrapping 하여 악의적인 공격 구문을 필터링하여 입력하기도 하고
Mapper가 JSON 문자열을 생성할 때 char 를 변환하여 출력하기도 하였습니다. 

막상 배포를 해보니 char 를 변환하여 출력하는 것에는 일정한 규칙(예외) 를 적용 하지 못하여
Request 를 Wrapping 한 것 처럼 Response 를 Wrapping 할 수 있지 않을까 하여 찾아본 내용을 공유 합니다. 

stackoverflow 에서 가져 왔고요 아래 filter 를 web.xml, WebConfig, WebMvcConfig 같은 곳에 적용해주시면 됩니다.

import com.navercorp.lucy.security.xss.servletfilter.XssEscapeFilter;
import org.springframework.http.MediaType;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;

public class CustomJsonFilter implements Filter {

    private XssEscapeFilter xssEscapeFilter = XssEscapeFilter.getInstance();
    private FilterConfig filterConfig = null;

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
        chain.doFilter(request, wrappedResponse);

        byte[] bytes = wrappedResponse.getByteArray();

        if (wrappedResponse.isJsonResponse()) {
            String out = filterString(new String(bytes));
            response.getOutputStream().write(out.getBytes());
        } else {
            response.getOutputStream().write(bytes);
        }
    }

    private String filterString(String inputString) {
        return xssEscapeFilter.doFilter(null, null, inputString);
    }

    private static class ByteArrayServletStream extends ServletOutputStream {
        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos) {
            this.baos = baos;
        }

        public void write(int param) {
            baos.write(param);
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener listener) {}
    }

    private static class ByteArrayPrintWriter {
        private ByteArrayOutputStream baos = new ByteArrayOutputStream();
        private PrintWriter pw = new PrintWriter(baos);
        private ServletOutputStream sos = new ByteArrayServletStream(baos);

        public PrintWriter getWriter() {
            return pw;
        }

        public ServletOutputStream getStream() {
            return sos;
        }

        byte[] toByteArray() {
            return baos.toByteArray();
        }
    }

    private static class CharResponseWrapper extends HttpServletResponseWrapper {
        private ByteArrayPrintWriter output;
        private boolean usingWriter;

        public CharResponseWrapper(HttpServletResponse response) {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

        public byte[] getByteArray() {
            return output.toByteArray();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            if (usingWriter) {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            if (usingWriter) {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

        public String toString() {
            return output.toString();
        }

        public boolean isJsonResponse() {
            return Objects.nonNull(super.getResponse().getContentType())
                    && super.getResponse().getContentType().contains(MediaType.APPLICATION_JSON_VALUE);
        }
    }

    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
    }

    public void destroy() {
        filterConfig = null;
    }
}

 

참고 : https://stackoverflow.com/questions/14736328

반응형

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
Comments