1, 크로스 사이트 스크립트(XSS : Cross-Site Scripting)
공격자가 전달한 스크립트 코드가 사용자 브라우저를 통해 실행
1) 공격자가 전달한 스크립트 코드
- 사용자에게 가짜 페이지를 제공하고 입력을 유도해서 사용자 정보를 탈취
- 사용자 브라우저 또는 PC에 저장된 정보를 탈취
- 브라우저에 설치되어 있는 각종 플러그인의 취약점 활용
- 사용자 PC의 제어권을 탈취해서 원격지에서 해당 PC를 제어
- BeEF
2) 유형
(1) Stored XSS(저장 크로스 사이트 스크립트)
공격자가 입력한 스크립트 코드가 취약한 서버에 저장되고, 사용자가 조회 시 저장된 스크립트 코드가 그대로 사용자 브라우저로 전달되어 실행
공격자가 게시판이 글을 쓸 때 <script> alert('xss') </script>를 입력하면 저장 페이지로 전달되어 그대로 DB에 저장된다.
희생자가 게시판의 글을 조회하면 취약한 서버에 심어 놓은 스크립트 코드가 그대로 브라우저에 전달되고, 희생자 브라우저에서 실행되게 된다.
불특정 다수가 공격자가 심어 놓은 스크립트 코드가 들어있는 게시글을 볼 때마다 각자의 브라우저에서 실행되게 된다.
한 번 저장된 스크립트 코드는 다수의 사용자에게 지속해서 실행된다.
(2) Reflective XSS(반사 크로스 사이트 스크립트)
입력값이 다음 화면 출력에 사용되는 경우, 입력값에 스크립트 코드 포함 여부를 확인하지 않고 그대로 화면 출력에 사용하면 입력값으로 전달된 스크립트 코드가 사용자 브라우저에서 실행
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과 같은 방법을 통해서 링크 내용(주소)을 확인할 수 없도록 변형해서 전달하기 때문에 알기 쉽지 않다. 해당 링크를 클릭하면 해당 브라우저에서 링크에 숨어 있던 스크립트 코드가 실행되게 된다.
(3) DOM Based XSS
개발자가 작성한 스크립트 코드의 취약점을 이용한 공격
실습으로 알아본다.
위 경로에 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를 통해 출력 메시지를 바꿀 수 있다.
개발자가 만든 스크립트 코드를 통해서 공격자의 코드가 실행되는 것이다.
방어 기법
- 입력값에 브라우저에서 실행할 수 있는 코드(스크립트 코드)가 포함되어 있는지 확인
- 오류 처리
- 제거 후 사용
- 안전한 문자로 대체해서 사용 - HTML 인코딩 처리
- 출력값에 의도하지 않은 실행 가능한 코드가 포함되어 있는지 확인
- 제거 후 출력
- 안전한 문자로 대체해서 사용 - HTML 인코딩해서 출력
- 필터링, 인코딩 작업을 수행할 때는 검증된 로직, 라이브러리, 프레임워크를 사용해서 구현
- 사용자의 입력 패턴은 개인이 수작업으로 방어하는 것이 불가능
- 설명
2. Cross-Site Scripting - Reflected (GET)
1) 동작 확인
입력창에 아무 글자나 쓰고 Go를 눌러 동작이 어떻게 되는지 확인한다.
공격기법의 이름처럼 스크립트 코드를 사용해서 공격하는 것이다. 이전에 했던 SQL, Command Injection과 같이 공격한다고 생각하면 공격이 이루어지지 않는다.
2) 스크립트 코드 입력
a <script> alert('xss') </script>
First name에 위 스크립트 코드를 같이 입력해서 스크립트 코드 처리가 되어 있는지 확인할 수 있다.
Last name에는 아무 문자 또는 공백을 넣어 넘어가도 된다.
XSS가 출력되는 것을 볼 수 있다.
a <script> alert("document.cookie") </script>
간단한 글자를 출력하는 게 아니라 쿠키를 출력하는 스크립트 코드를 입력하면 쿠키값을 얻을 수 있다.
3) 공격
쿠키가 나온 주소를 복사해서 공격 문자열을 링크로 만들어 불특정 다수에게 배포하게 되면 다른 사람의 쿠키 값을 얻을 수 있다.
<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>
이렇게 만들게 되면 비트코인 대박 정보라는 이름의 링크가 생기게 되고 많은 사람들은 클릭하게 되는 것이다.
4) 실습
위 DOM Based XSS 소스 코드를 위 스크립트 공격 링크로 바꿔 실습해 봤다. 위 비트코인 대박 정보 링크를 누르면 bee box 페이지로 넘어가는데 로그인이 되어 있으면 쿠키값이 나오게 된다.
3. Cross-Site Scripting - Stored (Blog)
1) 게시판 기능
게시판 기능으로 사용자가 입력한 내용을 저장하고 조회할 수 있다.
스크립트 공격을 하기 위해서는 먼저 HTML 태그가 작동하는지 확인해야 한다.
2) HTML 태그 확인
HTML 태그가 단순한 텍스트가 아닌 HTML 태그로 해석되어 처리되는 것을 확인할 수 있다.
게시글 내용에 HTML 태그가 있어도 검증을 하지 않는다는 것을 알았다.
그렇다면 스크립트 태그를 포함해 공격 문자를 저장하면 된다.
3) script 태그
해당 서버에 스크립트 코드가 저장되어 있으므로, 해당 페이지에 접근할 때마다 스크립트 코드가 전달되어 실행하게 된다. 이 공격도 사용자의 클릭을 유도하는 게시글에 그럴싸한 제목으로 만들어 게시한다면 불특정 다수가 피해를 보게 된다.
4. Cookie vs Session
1) Cookie
Stateless 한 HTTP 프로토콜에서 요청과 요청 간의 관계를 유지하기 위해서 도입된 개념
추가 설명
로그인 페이지가 있다고 가정한다.
Client | POST /login | Server |
ID : abc PW : xyz |
ID=abc&PW=xyz | 요청 파라미터로 전달된 ID와 PW를 이용해서 인증 |
클라이언트는 ID와 PW로 서버에게 요청을 보내면 요청 본문에 포함되어 전달된다. 서버는 요청받은 파라미터로 전달된 ID와 PW를 이용해서 DB 조회를 해서 인증을 한다.
서버가 인증에 성공하면 클라이언트에게 식당 번호표를 주는 것과 비슷하다. 식당에 예약하고 번호표를 받아 차례가 되면 번호표를 들고 다시 요청하듯이 서버는 클라이언트에게 번호표와 비슷한 쿠키를 준다.
쿠키는 서버가 주는 것이고 다음 요청에서 서버가 필요로 하는 값이다. 서버는 쿠키를 전달할 때 Set-Cookie로 응답 헤더에 담아서 전달한다.
클라이언트는 동일한 서버로 요청할 때 브라우저가 자동으로 쿠키라는 헤더로 설정해서 그대로 전달한다. 서버는 클라이언트가 쿠키를 주고 요청하면 쿠키로 전달된 값을 이용해서 해당 사용자에게 맞는 응답을 전달한다.
(1) 안전한 쿠키 사용
- 중요 정보가 쿠키에 포함되지 않도록 한다.
- 설계 시 중요 정보가 포함되지 않도록 지침을 만들고 지침에 따라서 개발
- 쿠키에 중요 정보가 포함되어야 하는 경우
- 암호화해서 전달
- 안전한 암호화 알고리즘과 키 길이를 사용, 키 관리를 안전하게 진행
- HTTPS와 같은 보안 채널을 전달
- Secure 속성을 활성화해서 쿠키를 전달
- 쿠키가 하드디스크에 지속해서 남아 있거나 임의로 접근하지 못 하도록 설정
- 쿠키의 지속 시간(Max-Age)과 유효 기간(Expires)을 최소한으로 설정
- 스크립트를 이용해서 쿠키값에 접근, 조작하는 것 방지
- HttpOnly 속성을 활성화해서 쿠키 전달
2) Session
Client | POST /login | Server |
ID : abc PW : xyz |
ID=abc&PW=xyz | 요청 파라미터로 전달된 ID와 PW를 이용해서 인증 서버가 가지고 있는 객체에 사용자 정보를 저장 서버에 의해 보호하고 해당 정보에 접근할 수 있는 키 발급 sid:1234 |
서버 Session에서는 다음 요청에 필요로 하는 값을 주는데 키를 발급해서 sid로 준다. 쿠키를 줄 때와는 다르게 정보 그 자차를 전달하지 않고 정보에 접근할 수 있는 키만 전달한다.
클라이언트가 동일한 서버로 요청할 때 브라우저가 자동으로 쿠키를 설정해서 전달하고 서버는 쿠키를 통해 전달된 키를 이용해서 정보에 접근하여 사용자에게 맞는 서비스를 제공한다.
- 요청, 응답 과정에서 정보 유출이 발생하지 않음
- 정보에 접근할 수 있는 키가 유출되는 경우 서버를 속여 서버에 저장된 정보에 접근이 가능
- 키를 잘 만들고 관리하는 것이 중요
(1) 세션 ID를 잘못 생성, 관리했을 때 발생할 수 있는 문제
- 세션 ID 고정
- 인증 전과 인증 후 동일한 세션 ID를 유지하는 경우
- 세션 ID 추측
- 세션 ID 생성 규칙을 유추할 수 있는 경우
- 세션 ID 훔치기
- XSS 공격
- 스크립트 코드를 이용해서 브라우저에 저장된 세션 ID를 탈취할 수 있는 경우
댓글