
공격자가 전달한 스크립트 코드가 사용자 브라우저를 통해 실행
공격자가 입력한 스크립트 코드가 취약한 서버에 저장되고, 사용자가 조회 시 저장된 스크립트 코드가 그대로 사용자 브라우저로 전달되어 실행
공격자가 게시판이 글을 쓸 때 <script> alert('xss') </script>를 입력하면 저장 페이지로 전달되어 그대로 DB에 저장된다.
희생자가 게시판의 글을 조회하면 취약한 서버에 심어 놓은 스크립트 코드가 그대로 브라우저에 전달되고, 희생자 브라우저에서 실행되게 된다.
불특정 다수가 공격자가 심어 놓은 스크립트 코드가 들어있는 게시글을 볼 때마다 각자의 브라우저에서 실행되게 된다.
한 번 저장된 스크립트 코드는 다수의 사용자에게 지속해서 실행된다.
입력값이 다음 화면 출력에 사용되는 경우, 입력값에 스크립트 코드 포함 여부를 확인하지 않고 그대로 화면 출력에 사용하면 입력값으로 전달된 스크립트 코드가 사용자 브라우저에서 실행
ID 중복 체크를 할 때, abc를 입력하고 체크를 누르면 서버로 요청이 된다. 서버에서는 요청 파라미터로 전달된 ID 값 abc와 일치하는 값이 존재하는지 확인 후 반환한다.
이때, 입력자가 abc<script> alert('xss') </script> 를 입력했다면, 서버에서는 SELECT * FROM members WHERE id = 'abc <script> alert('xss') </script>'으로 일치하는 값을 찾는다. 이후 존재한다면 존재합니다. 아니면 존재 하지 않습니다. 와 같은 조회 결과를 반환한다. 결과를 반환할 때 어떤 것이 존재하는지 존재하지 않는지 알려주기 위해 서버는 입력받은 매개변수를 같이 반환한다. 즉, abc<script> alert('xss') </script>는 존재하지 않습니다. 라고, 반환하게 된다.
반환될 때 adc 옆에 있는 스크립트 코드가 사용자의 브라우저에서 실행되게 되는 것이다. 이와 같은 동작이 가능한 것을 확인한 공격자는 공격 문자열을 만들어 SMS, Email, 게시판 글쓰기 등으로 불특정 다수에게 전달할 수 있다.
XSS 취약점을 가지고 있는 페이지 주소에 스크립트가 포함된 문자열을 링크 태그로 묶어 많은 사람의 클릭을 유도하는 링크 제목으로 만들어 전달하게 된다. 예를 들어 아래와 같은 스크립트를 사용하게 된다. check.jsp는 XSS 취약점을 가지고 있는 페이지 주소를 뜻한다.
<a href="check.jsp?abc<script>alert('xss')</script>">유도 제목</a>
일반적으로 링크 주소는 단축 URL과 같은 방법을 통해서 링크 내용(주소)을 확인할 수 없도록 변형해서 전달하기 때문에 알기 쉽지 않다. 해당 링크를 클릭하면 해당 브라우저에서 링크에 숨어 있던 스크립트 코드가 실행되게 된다.
개발자가 작성한 스크립트 코드의 취약점을 이용한 공격
실습으로 알아본다.

위 경로에 html 파일을 만들고 코드를 입력한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Insert title here</title>
<script>
const hash = window.location.hash.slice(1)
if (hash) {
window.location.href = decodeURIComponent(hash)
}
window.addEventListener('hashchange', function() {
window.location.href = decodeURIComponent(window.location.hash.slice(1));
});
</script>
</head>
<body>
<h1>DOM Based XSS 공격</h1>
<div>
<a id="first" href="#first">First 바로가기</a>
<a id="second" href="#second">Second 바로가기</a>
</div>
</body>
</html>
이 코드는 해시인데 주소 뒤에 #이 붙는 경우가 있다. # 뒤에는 id 값이 저장되는데, 같은 문서 안에서 #(3) DOM Based XSS로 된 링크가 있으면 id가 같은 값이 있는 곳으로 이동하기 때문에 책갈피 용도로 많이 사용한다.
window.location은 주소를 말하고, hash는 #을 말한다. # 다음에 나오는 글자를 slice하고 hash 변수에 넣는다.
만약 hash 변수가 있으면, 현재 주소에 hash 값을 URL decode 해서 넣는다.
addEventListerner는 이벤트를 등록하는 것이다. 사용자에게 이벤트를 받는 것이 아니고, 어떤 이벤트가 발생했을 때 그 이벤트에 대한 처리를 등록한다.
위 코드의 주소는 http://localhost:8080/WebGoat/message.html 인데, 이 주소만 입력했을 때는 message.html 페이지가 나오는데, 주소 뒤에 http://localhost:8080/WebGoat/message.html#first와 같이 #을 입력하면 # 뒤에 나오는 글자를 추출해서 주소를 http://localhost:8080/WebGoat/first로 바꾸고 리다이렉션을 하게 된다.
이때 first 페이지가 없으면 오류가 나는 것은 정상이다. first 페이지가 있어야 이동한다.
이 코드는 개발자가 만든 스크립트 코드다. 그냥 링크를 달고 보내줘도 되는데 굳이 스크립트를 사용해서 바꿔준다.
이때, http://host.pc:8080/WebGoat/message.html#http://www.naver.com 이렇게 입력하면 자동으로 네이버로 이동하게 된다. 여기서 네이버가 아니고 아래와 같이 링크 태그로 만든다면 공격자만 만든 페이지로 이동하게 된다.
<a href="http://host.pc:8080/WebGoat/message.html#공격자가만들어놓은페이지"> 꼭 보세요. </a>
여기서 중요한 것은 링크 주소는 누가 봐도 신뢰하고 안전한 사이트의 링크 주소로 만든다는 것이다. 링크에 마우스를 올려놓으면 링크의 주소가 나오는데 그 주소가 길면 뒤에는 잘려서 나오고 익숙한 앞 주소만 나오게 된다. 그래서 공격자가 만든 페이지를 같이 입력해도 사용자는 그 링크를 신뢰하고 안전한 사이트라고 믿고 클릭하게 되고, 그러면 신뢰한 사이트가 아니고 공격자가 만든 페이지로 이동하게 된다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Insert title here</title>
<script>
const hash = window.location.hash.slice(1)
if (hash) {
document.write("<h1>" + decodeURIComponent(hash) + "</h1>");
} else {
document.write("<h1>메시지가 없습니다.")
}
</script>
</head>
<body>
</body>
</html>
위와 같은 코드가 있을 때 주소를 입력하면 정상적으로 나온다.

하지만 공격자가 hash를 통해 출력 메시지를 바꿀 수 있다.


개발자가 만든 스크립트 코드를 통해서 공격자의 코드가 실행되는 것이다.
방어 기법

입력창에 아무 글자나 쓰고 Go를 눌러 동작이 어떻게 되는지 확인한다.
공격기법의 이름처럼 스크립트 코드를 사용해서 공격하는 것이다. 이전에 했던 SQL, Command Injection과 같이 공격한다고 생각하면 공격이 이루어지지 않는다.
a <script> alert('xss') </script>
First name에 위 스크립트 코드를 같이 입력해서 스크립트 코드 처리가 되어 있는지 확인할 수 있다.
Last name에는 아무 문자 또는 공백을 넣어 넘어가도 된다.

XSS가 출력되는 것을 볼 수 있다.
a <script> alert("document.cookie") </script>
간단한 글자를 출력하는 게 아니라 쿠키를 출력하는 스크립트 코드를 입력하면 쿠키값을 얻을 수 있다.

쿠키가 나온 주소를 복사해서 공격 문자열을 링크로 만들어 불특정 다수에게 배포하게 되면 다른 사람의 쿠키 값을 얻을 수 있다.
<a href="http://bee.box/bWAPP/xss_get.php?firstname=first+%3Cscript%3E+alert%28document.cookie%29+%3C%2Fscript%3E%09&lastname=last&form=submit"> 비트코인 대박 정보 </a>
이렇게 만들게 되면 비트코인 대박 정보라는 이름의 링크가 생기게 되고 많은 사람들은 클릭하게 되는 것이다.


위 DOM Based XSS 소스 코드를 위 스크립트 공격 링크로 바꿔 실습해 봤다. 위 비트코인 대박 정보 링크를 누르면 bee box 페이지로 넘어가는데 로그인이 되어 있으면 쿠키값이 나오게 된다.

게시판 기능으로 사용자가 입력한 내용을 저장하고 조회할 수 있다.
스크립트 공격을 하기 위해서는 먼저 HTML 태그가 작동하는지 확인해야 한다.

HTML 태그가 단순한 텍스트가 아닌 HTML 태그로 해석되어 처리되는 것을 확인할 수 있다.
게시글 내용에 HTML 태그가 있어도 검증을 하지 않는다는 것을 알았다.
그렇다면 스크립트 태그를 포함해 공격 문자를 저장하면 된다.



해당 서버에 스크립트 코드가 저장되어 있으므로, 해당 페이지에 접근할 때마다 스크립트 코드가 전달되어 실행하게 된다. 이 공격도 사용자의 클릭을 유도하는 게시글에 그럴싸한 제목으로 만들어 게시한다면 불특정 다수가 피해를 보게 된다.
Stateless 한 HTTP 프로토콜에서 요청과 요청 간의 관계를 유지하기 위해서 도입된 개념
추가 설명
로그인 페이지가 있다고 가정한다.
| Client | POST /login | Server |
|---|---|---|
| ID : abc PW : xyz | ID=abc&PW=xyz | 요청 파라미터로 전달된 ID와 PW를 이용해서 인증 |
클라이언트는 ID와 PW로 서버에게 요청을 보내면 요청 본문에 포함되어 전달된다. 서버는 요청받은 파라미터로 전달된 ID와 PW를 이용해서 DB 조회를 해서 인증을 한다.
서버가 인증에 성공하면 클라이언트에게 식당 번호표를 주는 것과 비슷하다. 식당에 예약하고 번호표를 받아 차례가 되면 번호표를 들고 다시 요청하듯이 서버는 클라이언트에게 번호표와 비슷한 쿠키를 준다.
쿠키는 서버가 주는 것이고 다음 요청에서 서버가 필요로 하는 값이다. 서버는 쿠키를 전달할 때 Set-Cookie로 응답 헤더에 담아서 전달한다.
클라이언트는 동일한 서버로 요청할 때 브라우저가 자동으로 쿠키라는 헤더로 설정해서 그대로 전달한다. 서버는 클라이언트가 쿠키를 주고 요청하면 쿠키로 전달된 값을 이용해서 해당 사용자에게 맞는 응답을 전달한다.
| Client | POST /login | Server |
|---|---|---|
| ID : abc PW : xyz | ID=abc&PW=xyz | 요청 파라미터로 전달된 ID와 PW를 이용해서 인증 서버가 가지고 있는 객체에 사용자 정보를 저장 서버에 의해 보호하고 해당 정보에 접근할 수 있는 키 발급 sid:1234 |
서버 Session에서는 다음 요청에 필요로 하는 값을 주는데 키를 발급해서 sid로 준다. 쿠키를 줄 때와는 다르게 정보 그 자차를 전달하지 않고 정보에 접근할 수 있는 키만 전달한다.
클라이언트가 동일한 서버로 요청할 때 브라우저가 자동으로 쿠키를 설정해서 전달하고 서버는 쿠키를 통해 전달된 키를 이용해서 정보에 접근하여 사용자에게 맞는 서비스를 제공한다.