작업 환경

  • VMware Workstation Pro (17.6.2 ver)
  • Server 
    • Web 서버 : Rocky_Linux(8.10 ver, VMnet 2, PHP로 구성)
    • DNS 서버 : Rocky_Linux(8.10 ver, VMnet 1)
  • Client
    • Kali Linux : Web 취약점 점검용 (VMnet 1)
  • GNS 구성

  • 해당 점검은 게시글 페이지에 한해서 진행됨
CF (상) 15. 크로스사이트 리퀘스트 변조(CSRF)
취약점 개요
점검 내용 사용자의 신뢰(인증) 정보의 변조 여부 점검
점검 목적 사용자 입력 값에 대한 적절한 필터링 및 인증에 대한 유효성을 검증하여 신뢰(인증)
정보 내의 요청(Request)에 대한 변조 방지
 보안 위협 사용자의 신뢰(인증) 정보 내에서 사용자의 요청(Request)을 변조함으로써 해당 사용자의
권한으로 악의적인 공격을 수행할 수 있음
판단 기준

양호 : 사용자 입력 값에 대한 검증 및 필터링이 이루어지는 경우
취약 : 사용자 입력 값에 대한 필터링이 이루어지지 않으며, HTML 코드(또는 스크립트)를 입력하여 실행되는 경우
참고 OWASP TOP 10 항목 중 
A01 : Broken Access Control
A07 : Identification and Authentication Failures
에 해당한다고 판단

 

 

점검 방법

  • Step 1) XSS 취약점이 존재하는지 확인
    • XSS 취약점 → 이전 발행글 확인

2025.05.13 - [주요정보통신기반시설가이드/Web 취약점 점검] - [XS (상)] 11. 크로스사이트 스크립팅

 

[XS (상)] 11. 크로스사이트 스크립팅

작업 환경VMware Workstation Pro (17.6.2 ver)Server Web 서버 : Rocky_Linux(8.10 ver, VMnet 2, PHP로 구성)DNS 서버 : Rocky_Linux(8.10 ver, VMnet 1)ClientKali Linux : Web 취약점 점검용 (VMnet 1)GNS 구성해당 실습은 게시글페이지에

jijibae.tistory.com

 

  • Step 2) 등록 및 변경 등의 데이터 수정 기능의 페이지가 있는지 조사함
    • 비밀번호 수정 페이지는 데이터의 수정 가능
  • Step 3) 데이터 수정 페이지에서 전송되는 요청(Request) 정보를 분석하여 임의의 명령을 수행하는 스크립트 삽입 후 해당 게시글을 타 사용자가 열람하였을 경우 스크립트가 실행되는지 확인
    • 공격자가 게시글 페이지에 아래의 코드를 포함하여 게시물 작성
    • 해당 버튼을 클릭시 해당 사용자의 비밀번호가 1 로 변경됨
<form method=POST action=proc/update_proc.php>
<input type=hidden name=pw value=1>
<input type=submit value='lotto!!!'>
</form>

 

 

클릭 전 test123 계정 정보 (PW : test123)
test123 계정으로 Lotto 클릭시

 

클릭 후 test123 계정 확인(PW : 1)

 

해시 함수를 사용하여 비밀번호 암호화를 하여도 비밀번호가 변경되는 것을 확인할 수 있으며
이를 이용하여 공격자가 test123 계정으로 로그인하여 피해를 줄 수 있음

 


보안 설정 방법

  • 웹 사이트에 사용자 입력 값이 저장되는 페이지는 요청이 일회성이 될 수 있도록 설계
  • 사용 중인 프레임워크에 기본적으로 제공되는 CSRF 보호 기능 사용
  • 사용자가 정상적인 프로세스를 통해 요청하였는지 HTTP 헤더의 Referer 검증 로직 구현
  • 정상적인 요청(Request)과 비정상적인 요청(Request)를 구분할 수 있도록 Hidden Form을 사용하여 임의의 암호화된 토큰(세션 ID, Timestamp, nonce 등)을 추가하고 이 토큰을 검증하도록 설계
  • HTML이나 자바스크립트에 해당되는 태그 사용을 사전에 제한하고, 서버 단에서 사용자 입력 값에 대한 필터링 구현
  • HTML Editor 사용으로 인한 상기사항 조치 불가 시, 서버 사이드/서블릿/DAO(Data Access Object) 영역에서 조치하도록 설계
  • XSS 조치 방안 참조

참고

📌 CSRF 란 ?

CSRF는 공격자가 사용자의 브라우저를 이용해 인증된 상태로
원하지 않는 요청을 서버에 보내도록 유도하는 공격

  • 피해자가 로그인된 상태를 노려 의도하지 않은 요청을 서버가 수행하게 만드는 공격
  • 요청 예시 : 비밀번호 변경, 글 삭제, 계좌 이체 등

 

XSS vs CSRF 차이점

항목  XSS (Cross-Site Scripting) CSRF (Cross-Site Request Forgery)
공격 대상 사용자 브라우저 서버의 인증된 요청 처리
공격 조건 사용자가 악성 스크립트를 직접 실행하게 유도 사용자가 로그인한 상태에서 공격자가 서버로 요청을 보내게 유도
실행 위치 피해자 브라우저 안에서 피해자의 인증된 세션을 통해 서버에서
예시 <script>alert('XSS')</script> <form method=POST ~~ 요청>
쿠키 활용 불필요 (JS로 직접 조작) 필요 (자동 쿠키 포함으로 인증 요청 유도)
방어 htmlspecialchars(), strip_tags() 등 CSRF Token, Referer 검증, SameSite 쿠키

 

 

CSRF 방어 방법

1. CSRF 토큰

  • 요청마다 임의 토큰을 부여하고, 서버는 이 값이 맞는지 검증
    • 이 예시에는 update.php 에 CSRF 토큰을 삽입하고, update_proc.php 에서 검증을 한다

update.php 에 CSRF 토큰 생성

<?php
session_start();

// CSRF 토큰 생성 (세션에 저장)
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['csrf_token'];
?>

...중략
  • random_bytes(32) : 매 요청모다 고유하게 생성 가능(유일성)
  • 세션 기반 : 토큰은 세션에 저장되어 서버 기준 유효성 검증 가능

 

②  토큰을 update.php form 안에 <input type="hidden">으로 추가

...중략

<form action="proc/update_proc.php" method="POST">
    <input type="password" name="pw" placeholder="새 비밀번호" required><br>
    <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>">
    <input type="submit" value="비밀번호 변경">
</form>

...중략
  • hidden input 이라서 사용자 모르게 자동 제출

 

update_proc.php 에서 토큰 검증 로직 추가

<?php
session_start();

// CSRF 토큰 검증
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die("CSRF 공격 의심: 요청이 거부되었습니다.");
}

 

③ - ① 더 자세한 결과를 위해 update_proc.php 추가

if (!isset($_POST['csrf_token'], $_SESSION['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    echo "POST 토큰: " . htmlspecialchars($_POST['csrf_token'] ?? '없음') . "<br>";
    echo "SESSION 토큰: " . htmlspecialchars($_SESSION['csrf_token'] ?? '없음') . "<br>";
    die("CSRF 공격 의심: 요청이 거부되었습니다.");
}

 

  • 검증 성공 시 정상적으로 비밀번호 변경 로직 실행
  • 실패 시 요청 중단

 

새로운 계정 추가(PW : jijibaeeee)
Lotto 클릭
CSRF 탐지하여 요청 거부
자세한 결과로 확인해보기
DB 확인

해시 값이 클릭 전, 후로 동일한 것을 확인
비밀번호 변경 요청이 서버에서 거부되었다.

 

2. SameSite 쿠키

  • PHP 7.2 이하 : SameSite 공식으로 미지원
  • PHP 7.3 이상 : SameSite 공식 지원

/var/www/html/include/session_secure.php 파일에 아래 내용 추가

<?php
session_set_cookie_params(
    0,
    '/; samesite=Strict',
    '',
    true,
    true
);
session_start();
  •  path + SameSite 속성 우회하여 삽입

 

② 모든 페이지 상단에서 다음을 추가

require_once("../include/session_secure.php");
  • 현재 폴더 구조는 상단으로 한 번 올라가야 /var/www/html 인 구조

 

 

🔐 OWASP TOP 10(2021)과의 연관

OWASP TOP 10 공식문서중 일부
"Cross-Site Request Forgery (CSRF) vulnerabilities allow attackers to induce users to perform actions they do not intend to. CSRF attacks exploit the trust that a site has in a user's browser."

1. A01:  Broken Access Control

  • 서버가 인증된 사용자의 의도를 검증하지 않고 민감 요청을 수행

2. A07 : Identification & Authentication Failures

  • 인증된 요청이라도 별도의 확인 절차 없이 민감 작업 수행 시 포함