배경
토큰을 어디에 저장하는 것이 좋은지 찾다가 관련된 보안 내용이 많아서 정리하게 되었습니다.
Cookie를 이용하여 XSS 공격을 막는 방법은 작성하였고, CSRF 공격에 대한 방어가 필요하기 때문에 추가 학습 후 작성하였습니다.
CSRF 공격에 대해 알아본 후, 어떤 방법으로 토큰을 효율적으로 관리하고 CSRF 공격을 방어하는지에 대해 알아보겠습니다.
http.csrf(AbstractHttpConfigurer::disable)
CSRF란?
사이트 간 요청 위조(Cross-site request forgery, CSRF)는 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격을 말한다. - 위키백과
일반 사용자가 자신도 모르게 서버를 공격하게 되는 경우입니다.
공격자가 만든 악성 페이지를 통해 사용자는 자신도 모르게 공격을 수행합니다. 어떻게 가능한지 알아보겠습니다.
전제 조건과 공격 과정
CSRF 공격을 시도하기 위해선 아래와 같은 몇 가지 조건이 필요합니다.
1. 사용자가 보안이 취약한 서버로부터 이미 인증을 받은 상태여야 합니다.
2. 쿠기 기반으로 서버 세션 정보를 획득할 수 있어야 합니다.
3. 공격자는 서버를 공격하기 위한 요청 방법에 대해 미리 파악하고 있어야 합니다. 예상치못한 파라미터가 있으면 불가능합니다.
위와 같은 조건이 만족되면 다음과 같은 과정을 통해 CSRF 공격이 수행됩니다.
1. 로그인 이후 서버에 저장된 세션 정보를 사용할 수 있는 sessionID가 사용자 브라우저 쿠키에 저장됩니다.
2. 서버에 인증된 브라우저의 사용자가 악성 스크립트 페이지를 누르도록 유도합니다.
3. 페이지 접근시 쿠키에 저장된 sessionID는 브라우저에 의해 자동적으로 함께 서버로 요청됩니다.
즉, 쿠키에 이미 sessionID가 담겨 있기 때문에 누군가 악성 스크립트로 요청을 강제하면 사용자의 요청과 무관한 요청이 이루어질 수 있음을 이용한 공격방법 입니다.
CSRF 공격 방법
간단한 코드를 작성하여 CSRF 공격을 재현하겠습니다.
공격자는 서버의 게시글을 삭제하는 방법을 파악하고, 악성 스크립트를 클릭하도록 유도했다고 가정합니다.
GET 방식 공격
<img /> 태그를 사용하거나 하이퍼링크를 걸어주는 <a></a> 태그를 이용합니다. <img /> 태그를 사용하겠습니다.
악성 페이지 - GET
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>GET 공격</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
</head>
<body>
<div id="wrap">
<h1>악성 페이지 - 숨겨진 이미지 태그</h1>
<img src="http://localhost:8080/item/1" style="width: 0px; height: 0px;"/>
</div>
</body>
</html>
CSRF 공격 후 게시글 삭제 확인
POST 방식 공격
<form></form> 태그를 이용합니다.
악성 페이지 - POST
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>POST 공격</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
</head>
<body>
<div id="wrap">
<h1>악성 페이지 - 숨겨진 폼 태그</h1>
<form action="http://localhost:8080/item/1" method="post">
</form>
<script>
setTimeout(function () {
document.forms[0].submit();
}, 3000);
</script>
</div>
</body>
</html>
CSRF 공격 후 게시글 삭제 확인
CSRF 방어 방법
Referer 검증
Referrr header는 Request에 포함된 요청을 하는 쪽의 웹 페이지 url를 나타냅니다.
CSRF 공격의 대부분 Referer값에 대한 검증만으로 방어가 가능하다고 합니다.
저는 Referer 값 검증으로 알아보겠습니다.
String referer = request.getHeader("Referer");
String host = request.getHeader("host");
if (referer == null || !referer.contains(host)) {
filterChain.doFilter(request, response);
return;
}
다를 경우 토큰 검증, 컨텍스트에 담는 로직을 진행하지 않습니다.
CSRF 토큰 검증
로그인 시 CSRF토큰을 생성하여 세션에 저장해두고 클라이언트 요청 시 이 값을 검증합니다.
세션을 저장해야하는 단점이 있습니다.
마치며
토큰을 어디에 저장해야 될지 많이 고민하고 찾아봤습니다.
로컬 스토리지와 쿠키 저장소의 장단점을 비교해보고 XSS, CSRF 공격을 직접 구현해보며,
쿠키가 없으면 CSRF 공격도 없지만, XSS 공격 방어를 위해 쿠키를 선택했습니다.
상황에 맞게 변하기 때문에 제가 택한 방법이 완벽한 방법은 아니지만, 현 상황에서 안전하게 웹을 구현할 수 있었습니다.
'Spring' 카테고리의 다른 글
좌표값(객체) 테스트에 대한 고민 (0) | 2023.11.25 |
---|---|
테스트 코드 - Presentation Layer (0) | 2023.11.12 |
LocalStorage, Cookie JWT토큰은 어디에 보관할까? (0) | 2023.10.17 |
XSS(Cross Site Scripting) 공격 (0) | 2023.10.16 |
Redis) 싱글벙글 Refresh Token을 Redis에 저장하고 사용해보자 (0) | 2023.08.04 |