| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
| 31 |
- 카카오인가코드받기
- 백엔드공부
- MIND 2023 #후기
- 어떤 개발자?
- 백엔드 로드맵
- ci/cd
- Java
- html
- form
- feignClient
- Docker
- Interface
- 엔티티 생명주기
- 인스턴스
- 카카오사용자정보가져오기
- 예외
- spring
- static
- input
- 제로베이스
- tag
- button
- 상속
- 엔티티 매니저
- 백엔드스쿨
- Spring API
- jenkins
- 카카오인증토큰받기
- GitHub_Actions
- oAuth2
- Today
- Total
HiDevelop
CSRF 공격에 대하여.. 본문
근래 개발을 하다가, CSRF 공격에 대해 공부할 기회가 생겨, 공부한 것을 토대로 내용을 공유하고자 한다
Cookie의 동작 원리
브라우저는 서버로 요청을 보낼 때, 요청 목적지 도메인에 해당하는 쿠키를 자동으로 포함해서 전송한다.
이 과정은 개발자가 직접 제어하지 않아도 브라우저 레벨에서 자동으로 처리된다.
중요한 점은 다음이다.
- 사용자가 직접 버튼을 눌렀는지
- 다른 사이트에서 요청이 시작됐는지
이 여부와 무관하게,
요청 URL의 도메인이 쿠키 도메인과 일치하면 쿠키는 자동으로 전송된다.
Cookie의 특징
1. 같은 도메인이면 쿠키를 공유한다
예를 들어 bank.com에서 로그인하면 인증 정보가 쿠키에 저장된다.
이 상태에서 사용자가 악성 사이트에 접속했다고 가정해보자.
그 사이트 내부에 다음과 같은 요청이 숨어 있다면:
<form action="https://bank.com/request" method="POST">
브라우저는 요청의 출처가 어디든 상관하지 않고
요청 목적지가 bank.com이라는 이유만으로 기존 쿠키를 자동 포함시킨다.
이 특성이 바로 CSRF 공격의 출발점이다.
CSRF (Cross-Site Request Forgery)
CSRF는 브라우저의 쿠키 자동 전송 특성을 악용한 공격 기법이다.
브라우저는 요청을 보낼 때 다음 기준만 확인한다.
- 요청 URL의 도메인
- 쿠키의 도메인 / 경로 / 정책
반대로, 다음은 확인하지 않는다.
- 요청이 어떤 사이트에서 시작됐는지
- 사용자가 직접 의도한 요청인지
즉, 서버 입장에서는
쿠키만 보고는 이 요청이 정상 요청인지, 공격 요청인지 판단할 수 없다.
이 점이 CSRF가 가능한 근본 원인이다.
CSRF 방어 방법
1. CSRF Token 방식 (기본이자 정공법)
핵심 아이디어는 단순하다.
이 요청이 사용자가 직접 만든 요청임을 서버가 증명받자
동작 흐름
- 서버가 랜덤 토큰을 생성
- 클라이언트가 요청 시 토큰을 함께 전송
- 서버가 기존 값과 비교
// 1. 서버에서 토큰 발급
String csrfToken = generateRandomToken();
session.setAttribute("CSRF_TOKEN", csrfToken);
// 2. 요청 시 비교
String sessionToken = session.getAttribute("CSRF_TOKEN");
String requestToken = request.getParameter("_csrf");
if (!sessionToken.equals(requestToken)) {
throw new CsrfException();
}
외부 사이트에서는 이 토큰 값을 알 수 없기 때문에
요청을 만들어도 서버 검증 단계에서 차단된다.
현재도 가장 표준적인 CSRF 방어 방식이다.
2. SameSite Cookie (보조 수단)
SameSite는
외부 사이트에서 시작된 요청에 쿠키를 보낼지 말지를 브라우저에 알려주는 옵션이다.
Set-Cookie: JSESSIONID=abc123; SameSite=Strict
옵션 종류
Strict
- 외부 사이트에서 시작된 요청 → 쿠키 전송 안 됨
- 주소창 직접 입력 → 쿠키 전송 안 됨
장점 :CSRF 방어 효과가 매우 큼
단점 : OAuth, 외부 로그인 흐름이 거의 불가능
Lax ( 기본값)
- 외부 GET 링크 클릭 → 쿠키 전송
- 외부 POST / form 요청 → 쿠키 차단
- 내부 요청 → 쿠키 전송
대부분의 서비스에서 기본값으로 사용된다.
None
- 외부 요청 전부 허용
- HTTPS 필수
OAuth, SSO 같은 외부 인증이 필요한 경우 사용된다.
SameSite의 한계
SameSite는 브라우저 정책에 의존한다.
서버가 요청의 의도를 직접 검증하지는 못한다.
따라서 CSRF Token을 대체할 수 없으며,
반드시 보조 수단으로만 사용해야 한다.
3. Origin / Referer 검증 (보조 수단)
요청 헤더에 포함된 출처 정보를 검사하는 방식이다.
이 요청이 우리 서비스에서 시작된 요청인지 확인한다
Origin 헤더
Origin: https://myapp.com
- 주로 POST / PUT / DELETE 요청에 포함
- 출처 도메인만 전달
Referer 헤더
Referer: https://myapp.com/page/form
- 요청이 발생한 정확한 URL
- GET 요청에도 자주 포함
- 브라우저 보안 정책에 따라 제거될 수 있음
CSRF 공격에서 왜 효과적인가
- 정상 요청
Origin: https://bank.com - 공격 요청
Origin: https://evil.com
서버에서는 다음과 같이 검증할 수 있다.
if (!origin.equals("https://bank.com")) {
reject();
}
한계점
- 헤더가 누락될 수 있음
- XSS 환경에서는 무력화
- 브라우저 정책에 의존
따라서 단독 사용은 위험하며,
CSRF Token의 보조 수단으로만 사용하는 것이 적절하다.
실제로는 어떻게 적용할까?
- Spring Security 사용
- CSRF Token 방식이 기본 적용됨
- 대부분 이 설정으로 충분
- Spring Security 미사용
- 직접 구현 필요
- Filter에서 처리하는 것이 일반적
왜 Filter에서 CSRF 검증을 할까?
OncePerRequestFilter를 사용하는 이유
CSRF 검증은 요청당 한 번이면 충분하다.
일반 Filter를 사용하면 :
- FORWARD / ERROR 디스패치마다 다시 실행됨
- 불필요한 비용 발생
OncePerRequestFilter는 요청당 한 번만 실행되므로 적합하다.
Interceptor나 Controller를 피하는 이유
- CSRF는 비즈니스 로직이 아니라 요청 검증 영역
- Filter는 Spring MVC 진입 전에 차단 가능
- 불필요한 리소스 사용을 줄일 수 있음
정리
CSRF는 브라우저의 쿠키 자동 전송 특성 때문에 발생한다.
가장 안전한 구조는 다음과 같다.
- 기본: CSRF Token
- 보조: SameSite Cookie, Origin / Referer 검증
이 조합이 현재 웹 환경에서 가장 현실적이고 안정적인 선택이다.