📄 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
  • 공격 벡터: 게시글 내용에 악성 이미지 태그 삽입

실습 단계:

  1. 게시글 내 img 태그 삽입 (display:none으로 숨김)

    • img src 속성에 공격 URL 삽입: /bbs11/updateGetAuth?confirm=click_ok&loginId=jjj@jj.com&adminYn=2
  2. 동작 원리

    • 피해자가 게시글 열람
    • 브라우저가 img 태그의 src 자동 로드
    • 피해자의 세션 쿠키가 자동으로 포함되어 요청 전송
    • 서버는 정상 요청으로 인식하여 권한 변경 처리
  3. 다른 공격 벡터

    • 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

공격 흐름:

  1. 공격자가 악성 페이지 또는 게시글 작성 (JavaScript로 Form 생성 및 자동 submit)

  2. 피해자가 해당 페이지 방문 (이미 대상 사이트에 로그인된 상태)

  3. JavaScript가 자동 실행되어 Form submit (피해자의 브라우저가 쿠키를 자동 포함)

  4. 서버는 정상적인 POST 요청으로 인식 (세션 쿠키가 유효하므로 인증 통과)

  5. 권한 변경 또는 중요 작업 수행됨

발견 가능한 취약점:

  • 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 우회 공격 흐름:

  1. 공격자가 악성 페이지를 작성 (iframe으로 Token 발급 페이지 로드)

  2. 피해자가 악성 페이지 방문 (iframe 내에서 Token 발급 페이지가 로드됨)

  3. window.onload 이벤트로 완전 로딩 대기 (iframe의 DOM이 완전히 로드될 때까지 대기)

  4. iframe.contentWindow.document 로 내부 접근 (iframe 안의 csrf_token input 값 추출)

  5. 추출한 토큰을 공격 요청에 포함 (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 파라미터 조작

실습 단계:

  1. SSRF 취약 엔드포인트 확인

  2. 내부 파일 접근 시도

    • url=http://eqst-ssrf-victim.com/include/db_conf.php
  3. localhost 변형 시도

    • url=http://127.0.0.1/include/db_conf.php
    • url=http://localhost/include/db_conf.php
  4. 내부 IP 대역 스캔

    • url=http://192.168.0.1/
    • url=http://10.0.0.1/
  5. 파일 프로토콜 시도

    • 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):

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 안전성 검증 절차:

  1. 프로토콜 검증 → http/https만 허용
  2. 도메인 화이트리스트 검증
  3. IP로 변환하여 내부 IP 확인 (ipaddress 라이브러리 활용)
  4. 차단된 네트워크에 속하는지 확인

안전한 요청 처리:

  • 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 설정 하나로 완전히 차단할 수 있다는 점이 인상적이었다. 보안은 결국 여러 방어층을 쌓아 올리는 과정이며, 한 층이 뚫려도 다른 층에서 막을 수 있는 심층 방어 전략이 핵심이다.