1. CSRF란?
- CSRF는 임의 이용자의 권한(쿠키를 탈취하여)으로 임의 주소에 HTTP 요청을 보낼 수 있는 취약점입니다
- 의도치 않은 요청에 동의하게 하는 공격을 말합니다. 그럴듯한 웹 페이지를 만들어서 이용자의 입력을 유도하고, 이용자가 값을 입력하면 이를 은행이나 중요 포털 사이트 등으로 전송하여 마치 이용자가 동의한 것 같은 요청을 발생시킵니다. 만약, 이용자가 “자동 로그인”등의 기능을 사용하여 브라우저에 세션 쿠키를 저장하고 있었다면, 실제로 계좌 이체가 발생하거나 비밀번호 초기화가 이뤄질 수도 있습니다.
- 사이트 간 요청 위조는 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 공격을 말한다. 유명 경매 사이트인 옥션에서 발생한 개인정보 유출 사건에서 사용된 공격 방식 중 하나입니다.
(2008년 옥션에서 발생했던 관리자 계정 탈취 사건
관리자가 admin 권한으로 로그인 한 상태에서 피싱 메일을 열람하였고 피싱 메일안에 숨겨진 CSRF코드가 실행되어 관리자 권한을 탈취하게 된 것입니다.)
- 네이버 카페, 인스타 등등에 의도치 않은 게시글이 작성될 수 있습니다.(광고, 유해성 게시글)
2. 공격 과정
1. 희생자가 위조 요청을 보낼 사이트에 로그인 되어있는 상태로 피싱 사이트에 접속합니다.
공격자는 피싱 사이트 접속 유도를 위해 피싱 메일, 팝업 광고를 띄우는 등의 행동을 합니다.
2. 희생자가 피싱 사이트에 접속하면 피싱 사이트에서 희생자로 가장하여 요청을 위조해 전송합니다.
3. 위조 요청을 받은 사이트는 해당 요청에 대한 응답을 하게 되고 이로 인해 희생자가 의도하지 않은 행동이 실행됩니다.
공격 예시)
<img src="/sendmoney?to=dreamhack&amount=1337">
<img src=1 onerror="fetch('/sendmoney?to=dreamhack&amount=1337');">
<link rel="stylesheet" href="/sendmoney?to=dreamhack&amount=1337">
3. 대응방법
3-1) CAPCHA 사용
- 이미지를 보여주고 그 이미지에 해당하는 문자/숫자/그림이 아니라면 요청을 거부
3-2) Referrer 검증법
- 요청이 들어올 때 request의 header에 담겨있는 referrer 값을 확인하여 같은 도메인에서 보낸 요청인지 검증하여 차단하는 방법입니다
- 하지만 동일 사이트 내에서 XSS 취약점이 발견된다면 이를 통하여 CSRF 공격을 실행할 수 있다는 점을 유의해야 하며 이는 페이지 단위까지 쪼개어서 도메인 검증을 하는것으로 페이지 간 CSRF 공격을 방어할 수 있습니다.
3-3) CSRF Token 사용
- 사용자의 세션에 임의의 값을 저장하여 모든 요청마다 그 값을 포함하여 전송합니다.
그리고 요청이 들어올 때 마다 백엔드에서 세션에 저장된 값과 요청으로 전송된 값이 일치하는지 검증하여 방어하는 방법입니다. referrer 검증법과 같이 XSS를 통한 CSRF 공격에 취약하다는 특징이 있습니다.
Host: localhost:5000
Connection: keep-alive
Content-Length: 55
Pragma: no-cache
Cache-Control: no-cache
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Origin: http://localhost:5000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:5000/user/test1
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: Pycharm-dcf76cbf=673fcf26-b3ca-4fcb-ae7e-b19b6ab92d45; Idea-e8149320=3b5645dd-790f-42cc-a9bb-b58b5365d317; JSESSIONID=D54EC5877C72F6EA1F34D87FCFC7F515; mytoken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6InRlc3QxIiwiZXhwIjoxNjU0NjY4NzIxfQ.8JSA-6iVPm4pmNxwy3Nmlb97bCL_D6aji1HiBqB_Iso
HTTP 리퍼러(HTTP referer)는 웹 브라우저로 월드 와이드 웹을 서핑할 때, 하이퍼링크를 통해서 각각의 사이트로 방문시 남는 흔적을 말한다.
예를 들어 A라는 웹 페이지에 B 사이트로 이동하는 하이퍼링크가 존재한다고 하자. 이때 웹 사이트 이용자가 이 하이퍼링크를 클릭하게 되면 웹 브라우저에서 B 사이트로 참조 주소(리퍼러)를 전송하게 된다. B 사이트의 관리자는 이 전송된 리퍼러를 보고 방문객이 A 사이트를 통해 자신의 사이트에 방문한 사실을 알 수 있다.
-> 이메일에 심어진 링크를 클릭하여 csrf 공격이 진행될 경우 referer이 정상 도메인과 다르므로 요청을 거절시키면 된다.
-> 의문점 링크에서 header의 referer을 set해서 던지면???
Cookie에 인증 토큰이 들어있어 로그인한 상태일 경우 자동으로 헤더를 통해 cookie값이 날라와 로그인한 사용자라고 판별함.
Security Token 사용 (A.K.A CSRF Token)
Referrer 검증이 불가한 환경이라면, Security Token를 활용할 수 있습니다. 우선 사용자의 세션에 임의의 난수 값을 저장하고 사용자의 요청 마다 해당 난수 값을 포함 시켜 전송시킵니다. 이후 Back-end 단에서 요청을 받을 때마다 세션에 저장된 토큰 값과 요청 파라미터에 전달되는 토큰 값이 일치하는 지 검증하는 방법입니다. 이 방법도 결국 같은 도메인 내에 XSS 취약점이 있다면 CSRF 공격에 취약해집니다. 아래는 간략한 샘플 코드입니다.
// 로그인시, 또는 작업화면 요청시 CSRF 토큰을 생성하여 세션에 저장한다.
session.setAttribute("CSRF_TOKEN",UUID.randomUUID().toString());
// 요청 페이지에 CSRF 토큰을 셋팅하여 전송한다
<input type="hidden" name="_csrf" value="${CSRF_TOKEN}" />
// 파라미터로 전달된 csrf 토큰 값
String param = request.getParameter("_csrf");
// 세션에 저장된 토큰 값과 일치 여부 검증
if (request.getSession().getAttribute("CSRF_TOKEN").equals(param)) {
return true;
} else {
response.sendRedirect("/");
return false;
}
Security Token vs Double Submit Cookie
Security Token : 검증 값을 서버의 세션에 저장 이를 html 페이지에 심어 놓고
Double Submit Cookie : 자바스크립트 요청 시 검증 값을 쿠키에 저장하고 파라미터 or 헤더에 담아 보내 이 둘이 일치하는지 확인
Double Submit Cookie 검증
Double Submit Cookie 검증은 Security Token 검증의 한 종류로 세션을 사용할 수 없는 환경에서 사용할 수 있는 방법입니다. 웹브라우저의 Same Origin 정책으로 인해 자바스크립트에서 타 도메인의 쿠키 값을 확인/수정하지 못한다는 것을 이용한 방어 기법입니다. 스크립트 단에서 요청 시 난수 값을 생성하여 쿠키에 저장하고 동일한 난수 값을 요청 파라미터(혹은 헤더)에도 저장하여 서버로 전송합니다. 서버단에서는 쿠키의 토큰 값와 파라미터의 토큰 값이 일치하는 지만 검사하면 됩니다. 서버에 따로 토큰 값을 저장할 필요가 없어 위에서 살펴본 세션을 이용한 검증보다 개발 공수가 적은 편입니다. 피싱 사이트에서는 도메인이 달라 facebook.com 쿠키에 값을 저장하지 못하므로 (조금 전에 언급한 Same Origin 정책) 가능한 방어 기법입니다. 아래는 샘플 코드입니다.
/**
* Generate 256-bit BASE64 encoded hashes
*
* @see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Synchronizer_.28CSRF.29_Tokens
* @return {string}
*/
var generateCsrfToken = function() {
function generateRandomString(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for(var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};
return btoa(generateRandomString(32));
}
// 쿠키 셋팅
var setCookie = function (cname, cvalue) {
document.cookie = cname + "=" + cvalue + ";path=/";
}
// 모든 ajax 요청 시 쿠키 및 header에 토큰 값을 같이 전달
jQuery.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
var csrfToken = generateCsrfToken();
setCookie('CSRF_TOKEN', encodeURIComponent(csrfToken));
xhr.setRequestHeader("_csrf", csrfToken);
}
}
});
// 헤더로 전달된 csrf 토큰 값
String paramToken = request.getHeader("_csrf");
// 쿠키로 전달되 csrf 토큰 값
String cookieToken = null;
for (Cookie cookie : request.getCookies()) {
if ("CSRF_TOKEN".equals(cookie.getName())) {
cookieToken = URLDecoder.decode(cookie.getValue(), "UTF-8");
// 재사용이 불가능하도록 쿠키 만료
cookie.setPath("/");
cookie.setValue("");
cookie.setMaxAge(0);
response.addCookie(cookie);
break;
}
}
// 두 값이 일치하는 지 검증
if (cookieToke.equals(paramToken)) {
return true;
} else {
return false;
}
Cookies and CSRF Attack
CSRF Attack is an attack that forces a user to do an unintended request. For example, if a website is accepting an email change request via:
POST /email/change HTTP/1.1
Host: site.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 50
Cookie: session=abcdefghijklmnopqrstu
email=myemail.example.com
Then an attacker can easily make a form in a malicious website that sends a POST request to https://site.com/email/change with a hidden email field and the session cookie will automatically be included.
However, this can be mitigated easily using sameSite flag in your cookie and by including an anti-CSRF token.
참조 :
https://tibetsandfox.tistory.com/11
https://itstory.tk/entry/CSRF-공격이란-그리고-CSRF-방어-방법 [덕's IT Story:티스토리]
'프로그래밍 > 개발지식' 카테고리의 다른 글
코드리뷰를 왜 해야하는가 (0) | 2022.07.02 |
---|---|
정규표현식 문법 정리 (0) | 2022.06.03 |
XXS(Cross Site Scripting)란? (0) | 2022.06.02 |
쿠키 & 세션 (0) | 2022.05.30 |
인증(Authentication) vs 인가(Authorization) (0) | 2022.05.30 |