[6. CSRF]

Minseok Jo·2025년 12월 31일

웹 시큐어코딩

목록 보기
6/11
post-thumbnail

웹취약점 점검에서 개발한 웹에 대하여 점검을 진행하였고, 여러 취약점들을 발견하였다.
발견한 취약점들을 대상으로 시큐어 코딩을 진행하고자 한다.

이번 대상은 주요정보통신기반시설 분석·평가 가이드 15번 항목 - CSRF 이다.

CSRF 취약점이 발견된 항목은 다음과 같다.

URL파라미터메뉴
http://jmseok.com/web/my/mypageedit_proc.phpinput_pw,
input_pw2
회원정보 수정

0. CSRF 대응방안

[1. CSRF 토큰]

CSRF Token

  • 서버 → 예측 불가능한 랜덤 토큰을 생성
  • 해당 토큰을 정당한 경로로 클라이언트에게 전달
  • 이후, 클라이언트는 요청시 → 해당 토큰을 함께 전송
  • 서버에서 토큰 일치 여부를 검증

[2. Referer 체크]
요청 헤더의 출처를 확인하여, 정당한 경로로부터 전송된 요청인지 검증


[3. 인증 정보 추가]
중요 정보 변경시, 기존 비밀번호를 입력하는 등의 인증 정보 추가 & 검증


1. 시큐어 코딩

CSRF 토큰 기반 방어는 공격자가 토큰 값을 알 수 없다는 가정을 전제로 하므로, XSS 취약점이 존재하는 경우 해당 가정이 깨져 방어 효과가 저하된다.
따라서 XSS 취약점이 존재하지 않는다는 것을 전제로 적용되어야 하며, 두 취약점은 함께 대응하는 것이 바람직하다.

XSS 대응방안은 앞서 정리 하였으므로 (시큐어 코딩 - XSS), XSS 취약점이 제거되었다는 전제 하에 CSRF Token 시큐어 코딩을 수행하였다.

[1. 마이페이지 - 회원정보 수정]

흐름 정리
1. 회원정보 수정 페이지 (mypage_edit.php) 접속시 CSRF Token 발행
2. mypage_edit.php 에서 "회원정보 변경" 요청시, 앞서 발행받은 Token을 포함하여 요청을 전송
3. 회원정보 수정처리 페이지 (mypageedit_proc.php) 에서 전달받은 CSRF Token 검증 수행


① 회원정보 수정 페이지 - CSRF Token 발행 기능 추가

// mypage_edit.php
if(!isset($_SESSION["csrf_token"]))
	$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
  • csrf_token 값으로, 32바이트의 랜덤 문자열 생성하여 Session에 저장

② 회원정보 수정페이지 - 회원정보 변경시 CSRF Token 전송

// mypage_edit.php
<form class="mypage-edit-form" method="post" action="mypageedit_proc.php">
    <input type="hidden" name="csrf_token" value="<?= $_SESSION["csrf_token"] ?>">

    <div class="mypage-edit-row">
        <label>아이디</label>
        <input value="<?php echo $row["user_id"]?>" disabled>
    </div>

    <div class="mypage-edit-row">
        <label for="input_pw">비밀번호</label>
        <input type="password" id="input_pw" name="input_pw" placeholder="새 비밀번호 입력">
    </div>

    <div class="mypage-edit-row">
        <label for="input_pw2">비밀번호 확인</label>
        <input type="password" id="input_pw2" name="input_pw2" placeholder="비밀번호 확인">
    </div>

    <div class="mypage-edit-row">
        <label for="input_name">사용자명</label>
        <input id="input_name" name="input_name" placeholder="<?php echo $row["user_name"]?>">
    </div>

    <div class="mypage-edit-row">
        <label for="input_mail">이메일(선택)</label>
        <input id="input_mail" name="input_mail" placeholder="<?php echo $row["user_mail"]?>">
    </div>

    <div class="mypage-edit-actions">
        <input type="submit" value="수정완료">
        <button type="button" onclick="history.back()">이전</button>
    </div>
</form>
  • 회원정보 변경 요청시, 세션에 저장된 Token값을 hidden 태그로 전달

③ 회원정보 수정 처리 페이지 - CSRF Token 검증 수행

// mypageedit_proc.php : CSRF Token 비교 및 검증
if (!isset($_SESSION["csrf_token"]) or !isset($_POST["csrf_token"]) or (!hash_equals($_SESSION["csrf_token"], $_POST["csrf_token"]))) {
    echo "<script>
        alert('잘못된 요청입니다.');
        history.go(-1);
    </script>";
    exit;
}
  • 세션에 CSRF Token이 존재하지 않는 경우, 잘못된 요청 처리
  • mypageedit_proc.php로 전달된 CSRF Token이 없는 경우, 잘못된 요청 처리
  • 세션의 토큰과, 전달받은 토큰을 비교하여 일치하지 않는 경우 잘못된 요청 처리 (타이밍 공격을 방지하기 위해 hash_equals() 함수 사용)


2. 보안 조치 결과

① 회원정보 수정 페이지(mypage_edit.php) 접속

CSRF Token 발행 확인

② 회원정보 수정 요청시, CSRF Token 전달

CSRF Token 전달

③ 회원정보 수정 처리 페이지(mypageedit_proc.php)에서 CSRF Token 검증 수행

CSRF Token 검증에 실패할 경우, 해당 요청은 수행 X


0개의 댓글