본문 바로가기
웹개발/jsp

xss 방지

by heavenLake 2022. 2. 17.
반응형

 

 

출처사이트 : https://developer111.tistory.com/40

 

 

xss(croos site script)란??

 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며, 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.

 

 

XSS 공격 보안 방법

 

XSS 공격을 보안하려면 세가지 상황에 대해 모두 보안을 해주셔야 합니다. 

 

 

첫번째, <form>또는 <input>과 같은 html 태그에 스크립트 공격이 들어오는 경우

 

두번째, <form  enctype="multipart/form-data">태그에 스크립트 공격이 들어오는 경우

 

세번째,  json 형식의 데이터 전송에 스크립트 공격을 하는 경우

 

네번째, 웹소켓에서 스크립트 공격이 들어오는 경우

 

 

첫번째와 두번째 상황은 방어하는 방법이 거의 같습니다. 두번째 상황에 대해서는 1-2가지 설정만 더 추가하면 되므로 

 

같이 설명하겠습니다. 

 

많은 개발자들이 사용하는 naver의 xss 필터 라이브러리를 사용하겠습니다. 

 

 

<input>, <form>, multipart/form-data 형태에 스크립트 공격 필터링 하기

 

먼저 pom.xml에 아래의 코드를 입력해주세요.

 

<!-- naver xss 크로스 사이트 스크립트 방지 filter-->	
	<dependency> 
		<groupId>com.navercorp.lucy</groupId> 
		<artifactId>lucy-xss-servlet</artifactId> 
		<version>2.0.0</version> 
	</dependency>

 

 

그다음 web.xml 파일에  아래와 같이 설정해주세요.

 

	<!-- multipart/form-data도 xss필터를 거치기 위해 -->
    <filter>
		<filter-name>multipartFilter</filter-name>
		<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>multipartFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!-- xss 필터링 -->
	<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>

 

주석으로 설명을 해놨는데 <!-- multipart/form-data도 xss필터를 거치기 위해 --> 이 주석의 밑에 부분이

 

multipart/form-data 형식으로 전송되는 데이터에도 xss 필터를 거치게 하기 위해 추가되는 내용입니다. 

 

이 부분이 없이 필터링 아래에 있는 내용만 추가되면 multipart/form-data 형식은 필터링이 되지 않습니다.

 

 

자 그런 다음 톰캣의 server.xml 파일에

 

allowCasualMultipartParsing="true" 설정을 추가합니다. 적용할 프로젝트를 베이스로 하는 context 태그에 넣어주세요. 

 

<Context docBase="myWebProject" path="/" allowCasualMultipartParsing="true" reloadable="true" source="org.eclipse.jst.jee.server:myWebProject"/>

 

 

server.xml이 아닌 context.xml파일에  아래와 같이 설정하셔도 됩니다.

 

<Context allowCasualMultipartParsing="true">

 

 

 

 

 

 

그리고 아래와 같은 설정으로 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>

       <!-- XssSaxFilter 등록 -->
       <defender>
           <name>xssSaxFilterDefender</name>
           <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssSaxFilterDefender</class>
           <init-param>
               <param-value>lucy-xss-sax.xml</param-value>   <!-- lucy-xss-filter의 sax용 설정파일 -->
               <param-value>false</param-value>        <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
           </init-param>
       </defender>

       <!-- XssFilter 등록 -->
       <defender>
           <name>xssFilterDefender</name>
           <class>com.navercorp.lucy.security.xss.servletfilter.defender.XssFilterDefender</class>
           <init-param>
               <param-value>lucy-xss.xml</param-value>    <!-- lucy-xss-filter의 dom용 설정파일 -->
               <param-value>false</param-value>         <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
           </init-param>
       </defender>
   </defenders>

    <!-- default defender 선언, 별다른 defender 선언이 없으면 default defender를 사용해 필터링 한다. -->
    <default>
        <defender>xssPreventerDefender</defender>
    </default>

    <!-- global 필터링 룰 선언 -->
    <global>
        <!-- 모든 url에서 들어오는 globalParameter 파라메터는 필터링 되지 않으며 
                또한 globalPrefixParameter로 시작하는 파라메터도 필터링 되지 않는다. -->
        <params>
            <param name="globalParameter" useDefender="false" />
            <param name="globalPrefixParameter" usePrefix="true" useDefender="false" />
        </params>
    </global>

    <!-- url 별 필터링 룰 선언 -->
    <url-rule-set>
       
       <!-- url disable이 true이면 지정한 url 내의 모든 파라메터는 필터링 되지 않는다. -->
       <url-rule>
           <url disable="true">/disableUrl1.do</url>
       </url-rule>
       
        <!-- url1 내의 url1Parameter는 필터링 되지 않으며 또한 url1PrefixParameter로 시작하는 파라메터도 필터링 되지 않는다. -->
        <url-rule>
            <url>/url1.do</url>
            <params>
                <param name="url1Parameter" useDefender="false" />
                <param name="url1PrefixParameter" usePrefix="true" useDefender="false" />
            </params>
        </url-rule>
        
        <!-- url2 내의 url2Parameter1만 필터링 되지 않으며 url2Parameter2는 xssSaxFilterDefender를 사용해 필터링 한다.  -->
        <url-rule>
            <url>/url2.do</url>
            <params>
                <param name="url2Parameter1" useDefender="false" />
                <param name="url2Parameter2">
                    <defender>xssSaxFilterDefender</defender>
                </param>
            </params>
        </url-rule>
    </url-rule-set>
</config>

 

 

그런 다음 웹프로젝트의 src/main/resources 아래에 lucy-xss-servlet-filter-rule.xml 파일을 넣어주세요.

 

 

 

 

참고로 lucy-xss-servlet-filter-rule.xml 파일명은 바뀌면 안됩니다. 파일명이 바뀌면 필터가 읽지를 못합니다.

 

 

설정은 끝이났고요.

 

xss테스트를 해보니 스크립트 공격을 잘 필터링 하네요. 

 

하지만 끝이 아닙니다.

 

json 객체로 데이터를 전달하는 상황에서는 필터링을 하지 않았습니다.

 

json형식의 데이터 전달에서 스크립트 공격 필터링도 해보겠습니다.

 

 

 

 

 

 

 

 

json형식의 데이터 전달에서 스크립트 공격 필터링

 

먼저 아래와 같이 특수문자를 코딩 해줄수 있는 클래스 파일을 만듭니다.

 

public class HtmlEscapingObjectMapperFactory implements FactoryBean<ObjectMapper> {

    private final ObjectMapper objectMapper;

	public HtmlEscapingObjectMapperFactory() {
        objectMapper = new ObjectMapper();
        objectMapper.getFactory().setCharacterEscapes(new HTMLCharacterEscapes());
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Override
    public ObjectMapper getObject() throws Exception {
        return objectMapper;
    }

    @Override
    public Class<?> getObjectType() {
        return ObjectMapper.class;
    }



    @Override
    public boolean isSingleton() {
        return true;
    }


    public static class HTMLCharacterEscapes extends CharacterEscapes {

		private static final long serialVersionUID = 1L;
		private final int[] asciiEscapes;

        public HTMLCharacterEscapes() {
            // start with set of characters known to require escaping (double-quote, backslash etc)
            asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
            // and force escaping of a few others:
            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;
        }

        // and this for others; we don't need anything special here
        @Override
        public SerializableString getEscapeSequence(int ch) {
            return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch)));
        }
    }
}

 

 

 

 

그리고 bean설정하는 xml 파일에 아래와 같이 빈설정을 해주세요.

 

<bean id="htmlEscapingObjectMapper" class="[패키지명].HtmlEscapingObjectMapperFactory" />
 <mvc:annotation-driven>
 <mvc:message-converters>
	        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" >
	        	<property name="objectMapper" ref="htmlEscapingObjectMapper"></property>
	        </bean>
</mvc:message-converters>
</mvc:annotation-driven>

 

 

 자 이렇게 설정하여 톰캣을 재구동하여 테스트 해보면 json형식의 데이터를 주고받을 때에도

 

xss 공격에 대한 필터링이 가능합니다.

 

 

 

 

 

 

 

 

웹소켓에서 스크립트 공격이 들어오는 경우

 

 

방금 위에서 진행한 json형식의 xss 필터링을 진행하는 클래스 파일과 빈설정은 웹소켓에서의

 

xss 필터링까지 해주지 않습니다. 

 

그렇기 때문에 웹소켓에서 스크립트 공격에 대한 보안 설정도 해주셔야 하는데요.

 

참고로 저는 웹소켓 설정은 xml형식으로 설정하지 않았고 java config  형식으로 했기때문에

 

자바 코드로 설명을 하겠습니다.

 

그리고 저의 웹소켓은 AbstractWebSocketMessageBrokerConfigurer을 상속 받았습니다.

 

AbstractWebSocketMessageBrokerConfigurer의 클래스 정의를 보니

 

public boolean configureMessageConverters(List<MessageConverter> messageConverters); 와 같은

 

메소드가 정의 되어있습니다. 웹소켓의 메세지컨버터를 설정하는 메소드인거죠.

 

이 메소드에 메세지 컨버터를 아래와 같이 설정해주시면 됩니다.

 

@Override
	public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
    	messageConverters.add(escapingConverter());
        return true;
    }

    private MessageConverter escapingConverter() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.getFactory().setCharacterEscapes(new HTMLCharacterEscapes());

        MappingJackson2MessageConverter escapingConverter =
                new MappingJackson2MessageConverter();
        escapingConverter.setObjectMapper(objectMapper);

        return escapingConverter;
    }
    

 

자 이렇게 해서 웹소켓에 대한 xss 보안 필터링도 설정을 해주었고요.

 

하지만 참고로 이 필터링은 실시간 채팅에 대한 보안은 설정해주지만

 

혹시 본인의 프로젝트에서 웹소켓을 통해 주고 받는 대화를 DB에 저장하는 방식으로 사용하신다면

 

이때에는 또 xss 핕터링이 되지 않기 때문에 직접 필터링을 설정해주셔야 하고요.

 

DB에 저장하고 메세지를 다시 상대방한테 뿌릴때는 또 방금 위에서 설정한 메세지 컨버터가 동작 되기 때문에

 

다시 원래의 메세지로 변환시켜서 뿌려야 하는 번거로움이 있습니다. 

 

저 같은 경우에는 원래 메세지를 변수에 저장하고  DB에 저장되는 메세지 내용은 따로 필터링을 해주고

 

변수에 저장한 원래 메세지를 상대방에게 보내는 방식으로 구현했습니다.

 

 

 

 

직접 필터링을 사용하실거라면 

 

 public static String ConvertInputValue(String message){
     message = message.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;")
     .replace("'", "&apos;").replace("\\", "&#x2F;").replace(" ", "&nbsp;").replace("\n", "<br />");
        return message;
    }

 

위와 같은 메소드로 직접 필터링을 구현하는 것도 좋은 방법이라고 생각됩니다.

 

 

 

 

자 이렇게 xss 필터링에 관한 내용들을 세세하게 살펴보았습니다.

 

저도 xss 필터링을 하면서 상당히 시행착오도 많이 겪으면서 진행을 했는데요.

 

혹시나 저와 같이 많은 시행착오를 겪으시는 분들을 위해

 

제가 xss 필터링을 하면서 겪었던 오류들을 어떻게 해결했는지 밑에 나열해보겠습니다.

 

 

 

-null pointer exception

server.xml에 allowCasualMultipartParsing="true"하니 사라졌다.

xss 필터링을 설명하는 많은 포스팅에서 이 부분에 대한 내용이 빠져 있는 경우가 많은데

혹시라도 나처럼 null pointer exception이 나타났다면 allowCasualMultipartParsing="true"를 설정하자.

context.xml파일에 설정해도 된다.

 

 

-data trucated error 

모델의 타입이 String인데 테이블의 int타입 컬럼에 들어가서 발생

xss필터링을 설정하기 전에는 int타입의 컬럼에 string타입의 객체를 넣고 있었다. 

이를 캐치하지 못한채 오류가 나지 않아 그냥 운영을 하고 있었는데 xss 필터링을 적용하니

이부분에서 오류가 발생되었다.

물론 내가 잘못 구현시켜놨던 부분이었지만 혹시라도 이런 에러를 봤다면 

테이블에 들어가는 모델의 객체의 타입과 테이블 컬럼의 객체타입이 일치하는지 확인해보자.

 

 


-400에러 : input 태그의 타입이 text 인데 모델의 필드타입 int여서 발생 

위와 마찬가지의 에러이다. 

이제까지 String객체를 int타입 컬럼에 넣고 있어 view파일의 input type도 text였는데

위의 data trucated error를 해결하며 모델 타입을 int로 바꾸니 view파일에서도

type 미쓰가 났다.

 

 

 

 

제가 참조했던 사이트입니다. 

 

사이트에 가보면 xml 파일에서 설정한 속성에 대해서도 자세하게 나와 있는 매뉴얼이 있네요 .

 

참조하면 구체적으로 공부하는데 도움이 되실 것입니다.

 

출처:

 

github.com/naver/lucy-xss-servlet-filter

 

naver/lucy-xss-servlet-filter

Contribute to naver/lucy-xss-servlet-filter development by creating an account on GitHub.

github.com

 

 

xml 파일 속성 설명 내용 : 

 

github.com/naver/lucy-xss-servlet-filter/blob/master/doc/manual.md

 

naver/lucy-xss-servlet-filter

Contribute to naver/lucy-xss-servlet-filter development by creating an account on GitHub.

github.com

zoou.tistory.com/21

 

Spring에서 JSON에 XSS Filter 처리하기

Spring Framework를 사용하여 개발한다면 Lucy-xss-servlet-filter 또는 전자정부프레임워크의 HtmlTagfilter로 XSS 공격 방지하는 경우를 볼 수 있다. 하지만 위 방법들은 기본적으로 Request Parameter에 대해서..

zoou.tistory.com

 

 

serverwizard.tistory.com/67

 

XSS 공격에 대한 방어

프로젝트를 진행하며 XSS(Cross Site Scripting) 공격에 대한 방어를 하기 위해 네이버에서 개발한 Lucy-Xss-Servlet-Filter 라이브러리를 사용했습니다. 해당 라이브러리는 웹어플리케이션으로 들어오는

serverwizard.tistory.com

 

useful-coding-dictionary.tistory.com/entry/Cross-Site-Scripting-XSS

 

Cross Site Scripting (XSS)

Cross Site Scripting(XSS) 란? 게시글과 같은 페이지에, 공격자가 악성 스크립트를 삽입하여, 사용자의 정보를 탈취, 악성코드등을 유입시키는 공격을 말합니다. 이 공격의 특징으로는 사용자(클라이

useful-coding-dictionary.tistory.com

 

 

 

반응형

댓글