[js, jQuery] XSS 를 조심하자

식빵·2022년 11월 30일
0
post-thumbnail

🥝 작성 계기

올해 프로젝트가 거의 막바지에 왔다.
그리고 막바지에 오면 이제 엄청 테스트를 많이 하게 되는데,
깜빡하고 XSS 테스트를 많이 안 해봤다.

다행히 이번 프로젝트의 요구사항에서 CRUD 기능이 딱 하나고
해당 기능만 조심해서 봤다.

그런데 왠걸, 바로 XSS 가 터졌다.
이유는 아래와 같은 상황 때문이었다.



🥝 내 코드의 XSS 발생 과정

참고로 백엔드 코드를 Spring 으로 짜고 있는 상태다. 그리고 WAS는 톰캣이다.


- 등록 작업

  1. 브라우저에서 insert 를 위한 ajax 요청을 수행. body 에는 json 형태로 전달.
    body 내용: {"content": "<script>alert('XSS ATTACK')</script>"}
  2. 서버에서는 해당 http body 의 내용을 그대로 받는다.
  3. 서버는 body 의 "content" 내용을 받아서 DB 에 INSERT 를 한다.

- 조회 작업

  1. 브라우저에서 AJAX 요청으로 앞서 등록한 정보를 조회
  2. 서버는 앞서 등록한 정보를 json 형태로 만들어서 돌려줌
  3. 브라우저에서는 받은 json 의 내용을 파싱해서 jquery.html('html 문자열')을 통해서 화면에 뿌린다.
  4. <script>alert('XSS ATTACK')</script> 이 실행된다 ==> XSS 발생 😨




🥝 XSS 에 안전하지 않은 jQuery

jquery 에는 html 조작과 관련한 다양한 유틸리티 메소드를 제공한다.
하지만 이런 메소드들의 대부분이 XSS 에 대해서 안전하지 않다.
특히 자주 쓰는 jquery.html(), jquery.append*() 메소드 모두 XSS 에 안전하지 않다.
단적으로 아래와 같은 codepen 테스트만 돌려봐도 알 수 있다.




🥝 해결법은?

해결법은 결국 화면단에서 XSS 공격이 가능한 문자열을 브라우저가 일반적인 TAG 처럼
다룬지 않게만 하면된다.

그렇다면 브라우저에서 이런 XSS 공격성 문자열이 실행되지 않으면서도
화면에 표출시키려면 어떡할까?

나는 아래 3가지 방법을 알아내고, 그 중에서 마지막 방법을 사용했다.

(() => {
  
  	const xssAttackString = '<script>alert("XSS ATTACK!!!!!")</script>';
  
  	// 방법1: innerText 사용
  	let docFrag = document.createDocumentFragment();
  	let spanElem = document.createElement("span");
  	spanElem.innerText = xssAttackString; // 완전히 문자열로 받아들인다.
  	docFrag.appendChild(spanElem);
  	document.body.appendChild(docFrag);
  	// 몇몇 분들은 innerHTML 로 테스트해도 xssAttackString 가 먹히지 않는 것을 
  	// 확인했을 것이다. 이건 HTML 명세에 이미 정해진 동작 방식이다.
  	// 하지만 xss 공격은 script 만 쓸 수 있는 게 아니다.
  	// innerHTML 의 경우 <img src="/bad/code"/> 같은 경우에도 실행이 되버린다.
  	// 그리고 뭐니뭐니 해도 화면에 해당 내용이 보이지 않는다는 문제도 있다.
   
  
    // 방법2: jQuery 의 .text() 사용
  	// let spanElem2 = document.createElement("span");
  	// $(spanElem2).text(xssAttackString);
	// $(body).append(spanElem2);
  
  
  	// 방법3: 들어온 HTML 문자열을 html entity 로 변환 시켜주는 메소드를 생성한다.
    // 나의 경우에는 기본으로 제공되는 document.createTextNode 를 사용해서 이를 해결했다.
  	// 이 방법은 특히 jquery 의 html(), append*() 메소드와 함께 쓸 때 사용하면 편하다.
  	// html, append 하기 직전에 이 메소드를 한번 수행해주기만 하면 된다.
	function changeToHtmlEntity(str) {
      	//이렇게 하면 자동으로 html Entity 변환해준다.
        let txt = document.createTextNode(str);
        let p = document.createElement('p');
        p.appendChild(txt);
      	return p.innerHTML;
    }
  	
	let docFrag3 = document.createDocumentFragment();
  	let spanElem3 = document.createElement("span");
  	
  	// &lt;script&gt;alert("XSS ATTACK!!!!!")&lt;/script&gt; 로 변환!
  	spanElem3.innerHTML = changeToHtmlEntity(xssAttackString);
  	docFrag3.appendChild(spanElem3);
 	document.body.append(docFrag3);
  
  	// $(body).append(spanElem3); // 이래도 큰 문제가 없습니다!
  
})();
  • 물론 이게 모든 것을 막아주지는 못할 수도 있다.
    하지만 일단 내가 아는 선에서는 이게 최선인 거 같다.



✨ 참고 링크

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글