📄 2026.02.04 (Day 70) - CSRF와 SSRF 취약점 심화
1. 핵심 개념 정리
CSRF (Cross-Site Request Forgery) 기본 개념
| # | 핵심 개념 | 설명 | 실무/보안 관점 |
|---|---|---|---|
| 1 | CSRF 정의 | 사용자가 의도하지 않은 요청을 인증된 세션으로 서버에 전송하는 공격 | 권한 상승, 계정 정보 변경, 금전 거래 등 치명적 피해 가능 |
| 2 | 공격 메커니즘 | 로그인된 사용자가 악성 페이지 방문 시 자동으로 요청 전송 | 브라우저가 자동으로 쿠키를 포함하여 요청하는 특성 악용 |
| 3 | GET 방식 CSRF | img, iframe, video 태그로 자동 요청 발생 | 단순 URL 클릭만으로도 공격 가능, 가장 기본적인 형태 |
| 4 | POST 방식 CSRF | JavaScript로 Form을 생성하여 자동 submit | GET보다 은닉성이 높지만 XSS 방어 시 효과 감소 |
| 5 | 공격 시나리오 | 은행 이체, 관리자 권한 상승, 비밀번호 변경 등 | 사용자 권한으로 수행 가능한 모든 작업이 공격 대상 |
CSRF 방어 기법
| # | 핵심 개념 | 설명 | 실무/보안 관점 |
|---|---|---|---|
| 6 | 2차 인증 | OTP, SMS, 이메일 등 추가 인증 단계 | 가용성 저하 가능성 있으나 고위험 작업에는 필수 |
| 7 | Captcha | 자동화된 요청과 사람의 요청 구분 | 사용자 경험 저하, 모든 요청에 적용 시 불편 |
| 8 | CSRF Token | 서버에서 발급한 랜덤 토큰을 세션과 비교 검증 | 가장 효과적이고 표준적인 방어 방법 |
| 9 | Referer 검증 | 요청이 자사 도메인에서 발생했는지 확인 | JavaScript로 조작 가능, Referer 헤더 삭제 가능 |
| 10 | SameSite Cookie | 쿠키에 SameSite 속성 설정으로 교차 사이트 요청 시 쿠키 미전송 | 최신 브라우저에서 효과적, 구형 브라우저는 미지원 |
SSRF (Server-Side Request Forgery) 심화
| # | 핵심 개념 | 설명 | 실무/보안 관점 |
|---|---|---|---|
| 11 | SSRF vs XSS vs CSRF | XSS는 클라이언트, CSRF는 사용자 권한, SSRF는 서버 권한 악용 | SSRF는 내부 네트워크 접근 가능하여 가장 치명적 |
| 12 | 내부 IP 대역 공격 | 192.168.x.x, 10.x.x.x, localhost 등 내부망 접근 | 외부에서 접근 불가능한 내부 자원 탈취 |
| 13 | 클라우드 메타데이터 | AWS EC2의 169.254.169.254 엔드포인트 접근 | IAM 자격증명 탈취로 전체 인프라 장악 가능 |
| 14 | URL Shortener 악용 | bit.ly, goo.gl 등 단축 URL로 302 리다이렉트 우회 | 최종 목적지를 숨기고 화이트리스트 우회 |
| 15 | 공격자 서버 경유 | 공격자 서버에서 302 리다이렉트로 내부 IP 접근 유도 | 초기 URL 검증만으로는 방어 불가능 |
2. 실습 내용 정리
실습 1-1: GET 방식 CSRF 공격
목표: 이미지 태그를 이용한 자동 권한 상승 공격
실습 환경:
- 공격 대상: lab.eqst.co.kr:8442/bbs11/updateGetAuth
- 공격 파라미터: loginId, adminYn, confirm
- 공격 벡터: 게시글 내용에 악성 이미지 태그 삽입
실습 단계:
-
게시글 내 img 태그 삽입 (display:none으로 숨김)
- img src 속성에 공격 URL 삽입: /bbs11/updateGetAuth?confirm=click_ok&loginId=jjj@jj.com&adminYn=2
-
동작 원리
- 피해자가 게시글 열람
- 브라우저가 img 태그의 src 자동 로드
- 피해자의 세션 쿠키가 자동으로 포함되어 요청 전송
- 서버는 정상 요청으로 인식하여 권한 변경 처리
-
다른 공격 벡터
- iframe src 속성 활용 (동일 방식)
- video src 속성 활용 (동일 방식)
확인 항목:
- GET 방식으로 중요 작업(권한 변경) 처리 여부
- Referer 헤더 검증 여부
- CSRF Token 존재 여부
- 추가 인증 단계(2차 인증, 재인증) 여부
보안 인사이트:
- GET 방식은 브라우저가 자동으로 요청하는 모든 태그에서 악용 가능
- 이미지, 비디오, 오디오, iframe 등 src 속성을 가진 태그는 모두 공격 벡터
- 중요 작업은 반드시 POST 방식으로만 처리해야 함
실습 1-2: POST 방식 CSRF 공격
목표: JavaScript로 Form을 자동 생성하여 POST 요청 전송
실습 환경:
- 공격 대상: lab.eqst.co.kr:8442/bbs11/updatePostAuth
- 요청 방식: POST
- 파라미터: loginId, adminYn
공격 흐름:
-
공격자가 악성 페이지 또는 게시글 작성 (JavaScript로 Form 생성 및 자동 submit)
-
피해자가 해당 페이지 방문 (이미 대상 사이트에 로그인된 상태)
-
JavaScript가 자동 실행되어 Form submit (피해자의 브라우저가 쿠키를 자동 포함)
-
서버는 정상적인 POST 요청으로 인식 (세션 쿠키가 유효하므로 인증 통과)
-
권한 변경 또는 중요 작업 수행됨
발견 가능한 취약점:
- POST 방식이지만 CSRF Token 미적용
- Referer 검증 부재
- 중요 작업에 대한 재인증 절차 없음
- Content-Type 검증 부재
탐지 패턴:
- JavaScript 코드에 form.submit() 패턴
- hidden input 필드로 중요 파라미터 전달
- 게시글 내용에 form 태그 포함
방어 방법:
- CSRF Token 필수 적용
- POST 요청에 대한 Content-Type 검증
- XSS 방어로 악성 스크립트 실행 차단
실습 1-3: CSRF Token 우회 공격
목표: iframe으로 Token을 먼저 획득한 후 공격 요청에 포함
실습 환경:
- Token 발급 페이지: lab.eqst.co.kr:8442/community15/authpage
- 공격 대상: lab.eqst.co.kr:8442/bbs11/updateTokenAuth
- 우회 기법: iframe + JavaScript DOM 조작
CSRF Token 우회 공격 흐름:
-
공격자가 악성 페이지를 작성 (iframe으로 Token 발급 페이지 로드)
-
피해자가 악성 페이지 방문 (iframe 내에서 Token 발급 페이지가 로드됨)
-
window.onload 이벤트로 완전 로딩 대기 (iframe의 DOM이 완전히 로드될 때까지 대기)
-
iframe.contentWindow.document 로 내부 접근 (iframe 안의 csrf_token input 값 추출)
-
추출한 토큰을 공격 요청에 포함 (Token 검증을 통과하여 공격 성공)
iframe 접근 핵심 포인트:
- 잘못된 방법: document.getElementsByName(“csrf_token”) → 외부 document를 찾음
- 올바른 방법: frame.contentWindow.document.getElementsByName(“csrf_token”) → iframe 내부 document 접근
- window.onload 를 사용하는 이유: iframe의 내용이 완전히 로드되기 전에 접근하면 undefined 에러 발생
이 공격이 성공하는 조건:
- SameSite Cookie 미설정 (교차 출처에서 쿠키 전송)
- X-Frame-Options 미설정 (iframe 삽입 허용)
- CORS 정책 미흡 (교차 출처 DOM 접근 허용)
방어 방법:
- X-Frame-Options: DENY (iframe 삽입 완전 차단)
- Content-Security-Policy: frame-ancestors ’none’
- SameSite=Strict Cookie 설정
- Token을 HTTP-Only 쿠키로 관리 (JavaScript 접근 차단)
실습 2-1: SSRF로 내부 DB 설정 파일 접근
목표: SSRF 취약점을 이용해 내부 서버의 민감 파일 접근
실습 환경:
- 공격 대상: eqst-ssrf-victim.com/include/db_conf.php
- 페이로드 저장소: pastebin.com (단축 URL 경유)
- 공격 벡터: URL 파라미터 조작
실습 단계:
-
SSRF 취약 엔드포인트 확인
- http://target.com/proxy?url= 형태의 파라미터 존재
-
내부 파일 접근 시도
- url=http://eqst-ssrf-victim.com/include/db_conf.php
-
localhost 변형 시도
- url=http://127.0.0.1/include/db_conf.php
- url=http://localhost/include/db_conf.php
-
내부 IP 대역 스캔
- url=http://192.168.0.1/
- url=http://10.0.0.1/
-
파일 프로토콜 시도
- url=file:///var/www/html/include/db_conf.php
PHP 설정 파일 노출 시 획득 가능한 정보:
- $db_host (내부 DB 서버 IP)
- $db_user (DB 계정명)
- $db_pass (DB 비밀번호)
- $db_name (DB명)
탐지 패턴:
- URL 파라미터에 내부 IP 대역 포함
- localhost, 127.0.0.1 등 루프백 주소
- file:// 프로토콜 사용
- /include/, /config/, /admin/ 등 민감 경로
방어 방법:
- URL 파라미터 화이트리스트 검증
- 내부 IP 대역 전체 차단 (RFC1918)
- 프로토콜 제한 (http, https만 허용)
- 설정 파일은 웹 루트 외부 배치
3. CSRF와 SSRF 비교 분석
CSRF vs SSRF 핵심 차이
| 항목 | CSRF | SSRF | 공통점 | 차이점 |
|---|---|---|---|---|
| 공격 주체 | 사용자 브라우저 | 서버 | 모두 요청 위조 | CSRF는 클라이언트, SSRF는 서버 측 |
| 권한 | 피해자 사용자 권한 | 서버 권한 및 네트워크 위치 | 인증된 세션 악용 | SSRF가 훨씬 강력한 권한 |
| 접근 범위 | 외부 웹 서비스만 | 내부 네트워크 포함 | 타겟 시스템에 요청 | SSRF는 방화벽 우회 가능 |
| 방어 방법 | CSRF Token, SameSite | URL 검증, IP 필터링 | 입력값 검증 필수 | 각각 다른 방어 메커니즘 |
은행 이체 프로세스의 다층 방어
| 단계 | 기능 | 적용 방어 | 우회 난이도 |
|---|---|---|---|
| /login | 로그인 | 세션 쿠키 발급 | 낮음 (로그인 상태 유지) |
| /transfer | 이체 정보 입력 | CSRF Token | 중간 (Token 획득 필요) |
| /2ndpass | 공동인증서 비밀번호 | 추가 인증 | 높음 (사용자만 알고 있음) |
| /checkpin | PIN 입력 | 보안 키패드 | 매우 높음 (키로깅 방어) |
| /checkars | ARS 인증 | 전화 인증 | 매우 높음 (물리적 장치 필요) |
| /realtransfer | 최종 이체 실행 | 모든 단계 통과 필요 | 거의 불가능 |
4. 심화 분석
CSRF Token 동작 원리 상세
| 구분 | Form 페이지 요청 | Token 발급 | 처리 요청 | 검증 과정 |
|---|---|---|---|---|
| 클라이언트 | GET /updateForm | 페이지 렌더링 | POST /updateProcess | — |
| 서버 | 랜덤 Token 생성 | 세션에 저장 | 파라미터 Token 추출 | Token 비교 검증 |
| 결과 | — | hidden input으로 Token 포함 | csrf_token=abc123 전송 | 일치 시 처리, 불일치 시 차단 |
| 보안 효과 | — | 공격자는 Token 모름 | 공격자는 유효한 Token 포함 불가 | CSRF 공격 차단 |
Flask 기반 CSRF Token 구현 구조
Form 페이지 - Token 발급 흐름:
- 라우트: GET /updateForm
- secrets.token_hex(32) 로 랜덤 CSRF Token 생성
- session[‘csrf_token’] = token (세션에 저장)
- 템플릿에 Token 전달 → hidden input으로 포함
처리 페이지 - Token 검증 흐름:
- 라우트: POST /updateProcess
- request.form.get(‘csrf_token’) 으로 파라미터 Token 추출
- session.get(‘csrf_token’) 으로 세션 Token 조회
- 두 값 비교 → 불일치 시 403 Forbidden 반환
- 사용한 Token 무효화: session.pop(‘csrf_token’, None) (재사용 방지)
SSRF 공격 단계별 시나리오
1단계 - 정찰 (Reconnaissance):
- http://target.com/proxy?url=http://127.0.0.1:80 → 웹 서버 확인
- http://target.com/proxy?url=http://127.0.0.1:3306 → MySQL 확인
- http://target.com/proxy?url=http://127.0.0.1:6379 → Redis 확인
- http://target.com/proxy?url=http://192.168.0.1~255 → 내부 IP 스캔
2단계 - 정보 수집:
- http://169.254.169.254/latest/ → AWS 메타데이터
- file:///etc/passwd → 시스템 사용자
- file:///proc/self/environ → 환경 변수
- http://192.168.0.20/admin → 내부 관리자 페이지
3단계 - 권한 획득:
- gopher://localhost:6379/_ → Redis 명령 실행
- http://internal-api/create-admin → 관리자 계정 생성
- AWS IAM 크레덴셜 탈취 → 클라우드 장악
4단계 - 지속적 접근:
- WebShell 업로드
- 백도어 계정 생성
- 내부 시스템 완전 장악
5. 실무/보안 적용
보안 전문가 관점 - CSRF 탐지 및 대응
| 단계/유형 | 탐지 포인트 | 로그 예시 | 대응 방안 |
|---|---|---|---|
| GET CSRF | 중요 작업을 GET으로 처리·Referer 헤더 없음·비정상적인 요청 패턴 | GET /updateAuth?adminYn=2 (Referer 없음) | GET 방식 중요 작업 금지·POST 방식으로 전환·CSRF Token 적용 |
| POST CSRF | POST 요청이지만 Token 없음·Referer가 외부 도메인·Content-Type이 비정상 | POST /updateAuth (from evil.com) | CSRF Token 필수 적용·SameSite Cookie 설정·Referer 검증 추가 |
| Token 우회 시도 | iframe 내장 패턴·JavaScript로 Token 추출 시도·교차 출처 DOM 접근 | XSS 페이로드에 contentWindow.document 포함 | X-Frame-Options 설정·CSP frame-ancestors 적용·XSS 방어 강화 |
Django 기반 CSRF 방어 설정
MIDDLEWARE 설정:
- django.middleware.csrf.CsrfViewMiddleware → CSRF 미들웨어 활성화
SameSite Cookie 설정:
- SESSION_COOKIE_SAMESITE = ‘Strict’
- CSRF_COOKIE_SAMESITE = ‘Strict’
HTTPS 강제 (프로덕션):
- SESSION_COOKIE_SECURE = True
- CSRF_COOKIE_SECURE = True
X-Frame-Options 설정:
- X_FRAME_OPTIONS = ‘DENY’
SSRF 방어 구현 핵심 로직
차단할 IP 대역 정의:
- 127.0.0.0/8 → Loopback
- 10.0.0.0/8 → Private
- 172.16.0.0/12 → Private
- 192.168.0.0/16 → Private
- 169.254.0.0/16 → Link-local (AWS metadata)
URL 안전성 검증 절차:
- 프로토콜 검증 → http/https만 허용
- 도메인 화이트리스트 검증
- IP로 변환하여 내부 IP 확인 (ipaddress 라이브러리 활용)
- 차단된 네트워크에 속하는지 확인
안전한 요청 처리:
- requests.get(url, timeout=5, allow_redirects=False)
- 리다이렉트 발생 시 Location 헤더의 URL도 동일하게 재검증
종합 보안 점검 체크리스트
CSRF 방어:
- 모든 중요 작업은 POST 방식으로만 처리
- CSRF Token을 모든 Form에 포함
- SameSite=Strict 또는 Lax Cookie 설정
- X-Frame-Options: DENY 헤더 설정
- 중요 작업에 2차 인증 적용 (OTP, SMS)
- Referer 헤더 검증 (추가 방어층)
- XSS 방어로 악성 스크립트 실행 차단
SSRF 방어:
- URL 파라미터 화이트리스트 검증
- 프로토콜 제한 (http, https만 허용)
- 내부 IP 대역 전체 차단 (RFC1918)
- 클라우드 메타데이터 엔드포인트 차단 (169.254.169.254)
- 리다이렉트 자동 추적 비활성화 또는 재검증
- DNS 리바인딩 방어 (캐시 무효화, 재검증)
- 네트워크 레벨 방화벽 설정 (아웃바운드 제한)
6. 배운 점 및 인사이트
새로 알게 된 점
-
CSRF Token도 우회 가능: iframe과 JavaScript를 이용하면 CSRF Token을 추출하여 공격에 포함할 수 있다. X-Frame-Options와 SameSite Cookie 설정이 함께 적용되어야 완벽한 방어가 가능하다.
-
GET vs POST의 보안 차이: GET 방식은 이미지, 비디오 등 브라우저가 자동으로 로드하는 모든 태그에서 악용 가능하다. 중요 작업은 반드시 POST 방식으로만 처리해야 하며, GET으로 처리하는 것은 심각한 보안 취약점이다.
-
다층 방어의 중요성: 은행 이체 프로세스처럼 여러 단계의 인증(공동인증서, PIN, ARS)을 거치면 CSRF 공격 난이도가 기하급수적으로 증가한다. 단일 방어 메커니즘에 의존하지 말고 여러 방어층을 구성해야 한다.
-
CSRF와 SSRF의 연계: XSS를 통해 CSRF를 트리거하고, SSRF로 내부 시스템에 접근하는 공격 체인이 가능하다. 한 가지 취약점만 방어해서는 안 되며, 전체적인 보안 태세를 갖춰야 한다.
-
URL Shortener의 위험성: bit.ly, goo.gl 같은 단축 URL 서비스는 SSRF 공격에서 최종 목적지를 숨기는 효과적인 도구다. 화이트리스트 방식의 URL 검증에서도 단축 URL 도메인은 별도로 관리해야 한다.
이전 학습과의 연결고리
-
XSS와 CSRF의 관계: 이전에 학습한 XSS가 방어되지 않으면 JavaScript로 CSRF Token을 쉽게 추출할 수 있다. XSS 방어는 CSRF 방어의 전제 조건이며, 두 취약점은 밀접하게 연관되어 있다.
-
SSRF와 Path Traversal 연계: SSRF의 file:// 프로토콜은 사실상 Path Traversal과 동일한 효과를 낸다. 두 취약점 모두 입력값 검증 부재에서 발생하며, 파일 시스템 접근이라는 공통된 위험이 있다.
-
SQL Injection → CSRF → SSRF 공격 체인: SQL Injection으로 관리자 계정 탈취 → CSRF로 권한 상승 → SSRF로 내부 시스템 장악이라는 연계 공격 시나리오가 현실적으로 가능하다.
실무 적용 아이디어
보안 전문가 관점:
- CSRF 공격 패턴 탐지: WAF나 SIEM에서 Referer 헤더가 없거나 외부 도메인에서 온 중요 작업 요청을 탐지하는 룰 작성. 특히 권한 변경, 비밀번호 변경 등 고위험 작업은 실시간 알림 발생.
- SSRF 허니팟 구축: 내부망에 가짜 관리자 페이지나 민감 파일을 배치하고, 접근 시도 시 즉시 알림. 이를 통해 SSRF 공격 시도를 조기에 탐지하고 공격자 IP를 차단할 수 있다.
- SameSite Cookie 전면 적용: 모든 세션 쿠키에 SameSite=Strict 또는 Lax 속성을 적용하여 교차 사이트 요청에서 쿠키가 전송되지 않도록 설정. 이는 CSRF의 근본적인 방어 수단이다.
개발자 관점:
- 프레임워크 내장 CSRF 보호 활용: Django, Spring Security, Laravel 등 주요 프레임워크는 CSRF 보호 기능을 제공한다. 이를 반드시 활성화하고, 모든 Form에 자동으로 Token이 포함되도록 설정.
- API 설계 시 고려사항: RESTful API는 쿠키 대신 JWT나 OAuth 토큰을 사용하여 CSRF 위험을 원천적으로 제거. 하지만 XSS에 취약해지므로, HttpOnly 쿠키와 조합하는 하이브리드 방식 고려.
7. Quick Reference
CSRF 공격 벡터 유형
GET 방식:
- img src 속성 → 페이지 로드 시 자동 GET 요청
- iframe src 속성 → 동일 방식
- video src 속성 → 동일 방식
POST 방식:
- form 태그 생성 + hidden input + submit 자동 실행
- JavaScript로 form 동적 생성 → document.body.appendChild(form) → form.submit()
CSRF Token 우회:
- iframe으로 Token 발급 페이지 로드
- window.onload 이후 iframe.contentWindow.document 접근
- csrf_token input 값 추출 → 공격 요청에 포함
CSRF 방어 설정 빠른 참조
| 구분 | 방어 기법 | 설정 예시 | 효과 |
|---|---|---|---|
| Cookie | SameSite | Set-Cookie: sessionId=abc123; SameSite=Strict | 교차 사이트 요청에서 쿠키 미전송 |
| Header | X-Frame-Options | X-Frame-Options: DENY | iframe 삽입 완전 차단 |
| CSP | frame-ancestors | Content-Security-Policy: frame-ancestors ’none’ | iframe 삽입 차단 (최신 방식) |
| Token | CSRF Token | hidden input name=“csrf_token” | 요청 출처 검증 |
SSRF 방어 핵심 코드 패턴
URL 화이트리스트 검증:
- ALLOWED_DOMAINS = [’naver.com’, ‘google.com’]
- urlparse(url).hostname 추출 후 허용 도메인 여부 확인
내부 IP 차단:
- ipaddress.ip_network(‘127.0.0.0/8’), (‘10.0.0.0/8’), (‘172.16.0.0/12’), (‘192.168.0.0/16’), (‘169.254.0.0/16’) 정의
- socket.gethostbyname(hostname) 으로 IP 변환 후 CIDR 범위 체크
안전한 요청:
- requests.get(url, timeout=5, allow_redirects=False)
- 리다이렉트 시 Location 헤더 URL도 동일 검증
8. 트러블슈팅
| 문제 | 원인 | 해결 방법 |
|---|---|---|
| CSRF Token이 있는데도 공격 성공 | iframe으로 Token 추출 가능, X-Frame-Options 미설정 | X-Frame-Options: DENY 설정 · CSP frame-ancestors ’none’ 적용 · SameSite=Strict Cookie 설정 |
| POST 방식인데 CSRF 공격됨 | CSRF Token 미적용, XSS로 Form 생성 가능 | 모든 POST 요청에 CSRF Token 필수 · XSS 방어 (입력값 이스케이프) · Content-Type 검증 추가 |
| Referer 검증을 우회당함 | Referer 헤더는 JavaScript로 조작 또는 삭제 가능 | Referer만 의존하지 말고 CSRF Token 병행 · SameSite Cookie를 주 방어 수단으로 |
| SSRF 화이트리스트를 우회당함 | URL Shortener로 리다이렉트, DNS 리바인딩 | 단축 URL 서비스 도메인 차단 · 리다이렉트 최종 목적지 재검증 · DNS 캐시 무효화, 매 요청마다 재조회 |
| iframe 내부 Token 접근 실패 | window.onload 없이 즉시 접근, contentWindow 미사용 | window.onload 이벤트로 로딩 대기 · iframe.contentWindow.document 사용 · CORS 정책 확인 |
Today’s Insight:
CSRF와 SSRF는 이름은 비슷하지만 공격 메커니즘과 영향 범위가 완전히 다른 취약점이다. CSRF는 사용자의 인증된 세션을 악용하여 브라우저에서 비의도적 요청을 발생시키지만, SSRF는 서버의 권한과 네트워크 위치를 탈취하여 내부 시스템에 접근한다. 두 취약점 모두 다층 방어가 필수적이며, 단일 방어 메커니즘에만 의존해서는 안 된다. CSRF는 Token + SameSite Cookie + X-Frame-Options 조합이, SSRF는 화이트리스트 + IP 필터링 + 프로토콜 제한 조합이 효과적이다. 특히 iframe을 이용한 CSRF Token 우회 공격은 X-Frame-Options 설정 하나로 완전히 차단할 수 있다는 점이 인상적이었다. 보안은 결국 여러 방어층을 쌓아 올리는 과정이며, 한 층이 뚫려도 다른 층에서 막을 수 있는 심층 방어 전략이 핵심이다.