CSRF 심층 이해
교차 사이트 요청 위조 (CSRF) 공격에 대한 심층적인 탐구를 제공하며, 그 메커니즘, 예시, 다양한 예방 방법을 설명하여 웹 애플리케이션 보안을 강화합니다.
웹 개발, 특히 쿠키를 사용할 때 "이 설정은 CSRF를 방지하는데 도움이 됩니다"와 같은 문구를 자주 듣습니다. 그러나 많은 사람들은 "CSRF"가 실제로 무엇을 의미하는지에 대해 어렴풋이 알고 있을 뿐입니다.
오늘 우리는 일반적인 웹 보안 결함인 CSRF (교차 사이트 요청 위조)에 대해 심도 있게 살펴볼 것입니다. 이를 통해 CSRF 관련 문제를 보다 효과적으로 처리할 수 있습니다.
CSRF란 무엇인가?
CSRF(교차 사이트 요청 위조)는 공격자가 인증된 사용자를 속여 의도치 않은 행동을 하도록 만드는 웹 공격의 일종입니다. 간단히 말해서, "해커들이 사용자인 척하여 무단 행동을 수행하는 것"입니다.
CSRF는 어떻게 작동하나
CSRF를 이해하려면 몇 가지 핵심 개념을 이해해야 합니다:
브라우저의 동일 출처 정책
동일 출처 정책은 하나의 출처에서 가져온 문서나 스크립트가 다른 출처의 리소스와 상호작용할 수 있는 방법을 제한하는 브라우저의 보안 기능입니다.
출처는 프로토콜(예: HTTP 또는 HTTPS), 도메인 이름, 포트 번호로 구성됩니다. 예를 들어, https://example.com:443
은 하나의 출처이고, https://demo.com:80
은 다른 출처입니다.
동일 출처 정책은 서로 다른 출처의 페이지 간 데이터 접근을 제한합니다:
- 한 출처의 자바스크립트가 다른 출처의 DOM을 읽을 수 없습니다.
- 한 출처의 자바스크립트가 다른 출처의 쿠키, IndexedDB, 또는 localStorage를 읽을 수 없습니다.
- 한 출처의 자바스크립트가 다른 출처로 AJAX 요청을 보낼 수 없습니다 (CORS를 사용하는 경우 제외).
그러나 웹의 개방성 및 상호운용성을 유지하기 위해 동일 출처 정책은 교차 출처 네트워크 요청을 제한하지 않습니다:
- 페이지는 어떤 출처든 GET 또는 POST 요청을 보낼 수 있습니다 (이미지 로딩이나 폼 제출 등).
- 어떤 출처든 리소스를 포함할 수 있습니다 (
<script>
,<img>
,<link>
,<iframe>
태그 등).
쿠키 자동 전송 메커니즘
쿠키 자동 전송 메커니즘은 브라우저의 중요한 기능입니다. 브라우저가 도메인에 요청을 보내면, 해당 도메인의 모든 쿠키를 자동으로 첨부합니다. 이 과정은 자동으로 이루어지며, JavaScript 코드나 사용자 상호작용이 필요하지 않습니다.
이 메커니즘을 통해 웹사이트는 사용자의 로그인 상태를 쉽게 기억할 수 있습니다. 각 요청이 사용자의 식별 정보와 함께 자동으로 전달되기 때문입니다.
예를 들어, 은행 웹사이트(bank.com
)에 로그인하여 식별 쿠키를 얻은 후 명세서를 보기 위해 클릭하면, 브라우저는 bank.com
과 일치하는 모든 쿠키를 자동으로 찾고 명세서 요청에 첨부합니다. 은행의 서버는 백엔드에서 사용자를 식별하고 사용자의 명세서 정보를 반환할 수 있습니다.
CSRF 공격 단계
-
사용자는 대상 웹사이트(예: 은행 사이트)에 로그인하고 인증 쿠키를 얻습니다. 이 단계에서는 자동 쿠키 전송 메커니즘을 사용합니다. 은행 사이트가 식별 인증 쿠키를 설정한 후, 브라우저는 해당 사이트로 보내는 모든 요청에 이 쿠키를 자동으로 첨부합니다.
-
로그아웃하지 않은 상태로 사용자는 악성 웹사이트를 방문합니다. 이 시점에서 동일 출처 정책 덕분에 악성 사이트는 은행 사이트의 쿠키를 직접 읽거나 수정할 수 없습니다. 이는 사용자의 식별 정보가 직접적으로 도난당하지 않도록 보호합니다.
-
악성 사이트는 대상 사이트(예: 이체 작업)로 요청을 포함합니다. 동일 출처 정책은 교차 출처 접근을 제한하지만
img
,form
태그 등을 통해 시작된 요청과 같은 교차 출처 네트워크 요청을 허용합니다. 공격자는 이 "허점"을 악용합니다. -
사용자의 브라우저는 이 요청을 자동으로 보내며, 대상 사이트의 쿠키도 함께 전송합니다. 이것이 CSRF 공격의 핵심입니다. 이는 동일 출처 정책에서 교차 출처 요청을 허용하는 점과 자동 쿠키 전송 메커니즘(악성 사이트에 의해 촉발된 요청이라도 해당 도메인에 일치하는 쿠키를 밀어넣기)을 악용합니다.
-
대상 사이트는 요청을 수신하고 쿠키가 유효한지를 확인한 후 이 작업을 수행합니다. 서버는 이 요청이 적법한 사용자 행동에 의한 것인지 알려줄 수 없습니다. 첨부된 쿠키가 유효하기 때문입니다.
CSRF 공격 예시
CSRF 공격이 어떻게 발생하는지 보여주는 특정 예제로 설명해 보겠습니다. 우리는 은행 웹사이트 bank.com
을 예로 사용합니다.
먼저 사용자는 https://bank.com
을 방문하고 자신의 계정에 로그인합니다.
성공적으로 로그인한 후, 서버는 인증 쿠키를 설정합니다. 예를 들어:
사용자는 은행 웹사이트에서 1,000 달러를 Alice에게 송금하는 작업을 수행합니다. 이 작업은 이와 같은 요청을 보낼 수 있습니다:
이제 공격자가 다음 HTML을 포함한 악성 사이트 https://evil.com
을 생성했다고 가정합시다:
사용자가 자신의 은행 계정에서 로그아웃하지 않고 https://evil.com
링크를 클릭했을 때, 이미 bank.com
에 로그인되어 있으므로 브라우저에는 유효한 session_id
쿠키가 있습니다.
악성 페이지가 로드되고 나면, 숨겨진 양식을 자동으로 제출하여 https://bank.com/transfer
로 송금 요청을 보냅니다.
사용자의 브라우저는 이 요청에 bank.com
쿠키를 자동으로 첨부합니다. bank.com
서버는 이 요청을 수신하고, 쿠키가 유효한지를 확인한 후 이 무단 전송 작업을 수행합니다.
CSRF 공격을 방지하는 일반적인 방법
여기 몇 가지 일반적으로 사용되는 CSRF 방어 방법이 있습니다. 각 방법의 원리와 CSRF 공격을 효과적으로 방지하는 방법을 자세히 설명합니다:
CSRF 토큰 사용
CSRF 토큰은 CSRF 공격에 대한 가장 일반적이고 효과적인 방어 방법 중 하나입니다. 작동 방식은 다음과 같습니다:
- 서버가 각 세션에 대해 고유하고 예측할 수 없는 토큰을 생성합니다.
- 이 토큰은 민감한 작업에 대한 모든 양식에 포함됩니다.
- 사용자가 양식을 제출할 때, 서버는 토큰의 유효성을 확인합니다.
CSRF 토큰은 사용자 세션에 바인딩된 고유 값이며, 공격자는 이 값을 알거나 추측할 수 없습니다(각 세션마다 다름). 공격자가 사용자를 속여 요청을 보내도록 하더라도 유효한 CSRF 토큰이 없기 때문에 서버에 의해 요청이 거부됩니다.
구현 예시:
서버 측(Node.js 및 Express 사용):
프론트엔드 JavaScript에서:
Referer
헤더 검사
Referer
헤더에는 요청을 시작한 페이지의 URL이 포함됩니다. Referer
헤더를 검사함으로써 서버는 요청이 합법적인 출처에서 오는지 여부를 판단할 수 있습니다.
CSRF 공격은 보통 다른 도메인에서 발생하므로 Referer
헤더는 공격자의 도메인 이름을 표시합니다. Referer
가 예상되는 값인지 확인함으로써 알 수 없는 출처로부터의 요청을 차단할 수 있습니다.
그러나 이 방법은 완전히 신뢰할 수는 없습니다. 일부 브라우저는 Referer
헤더를 보내지 않을 수 있으며, 사용자는 브라우저 설정이나 플러그인을 통해 Referer
헤더를 비활성화할 수 있습니다.
구현 예시:
SameSite
쿠키 속성 사용
SameSite
는 쿠키가 교차 사이트 요청에 포함되는지 제어하는 쿠키 속성입니다. 세 가지 가능한 값이 있습니다:
Strict
: 쿠키는 동일한 사이트 요청에서만 전송됩니다.Lax
: 쿠키는 동일한 사이트 요청과 최상위 탐색에서 전송됩니다.None
: 쿠키는 모든 교차 사이트 요청에서 전송됩니다 (반드시Secure
속성과 함께 사용해야 함).
SameSite
를 Strict
로 설정하면 제삼의 웹사이트가 쿠키를 보내는 것을 완전히 방지할 수 있어 CSRF 공격을 효과적으로 방지할 수 있습니다.
SameSite
를 Lax
로 설정하면 외부 링크에서 웹사이트에 들어가는 것과 같은 일반적인 교차 사이트 사용 사례를 허용하면서 민감한 작업을 보호할 수 있습니다.
구현 예시:
사용자 정의 요청 헤더 사용
AJAX 요청의 경우 사용자 정의 요청 헤더를 추가할 수 있습니다. 동일 출처 정책에 제한되어 공격자는 교차 출처 요청에서 사용자 정의 헤더를 설정할 수 없습니다. 서버는 이 사용자 정의 헤더의 존재 여부를 확인하여 요청의 정당성을 검증할 수 있습니다.
구현 예시:
프론트엔드에서:
서버 측에서:
이중 쿠키 검증
이중 쿠키 검증은 효과적인 CSRF 방어 기술입니다. 그 핵심 원리는 서버가 임의의 토큰을 생성하여 쿠키와 페이지(보통 숨겨진 양식 필드로) 모두에 설정하는 것입니다. 브라우저가 요청을 보낼 때 자동으로 쿠키를 포함시키며, 페이지의 JavaScript는 요청 매개변수로 토큰을 보냅니다. 서버는 쿠키의 토큰이 요청 매개변수의 토큰과 일치하는지 확인합니다.
공격자는 교차 사이트 요청에서 대상 웹사이트의 쿠키를 포함할 수 있지만, 쿠키의 값을 읽거나 수정할 수 없으며, 페이지의 토큰 값을 액세스하거나 수정할 수도 없습니다. 쿠키와 매개변수 양쪽에 있는 토큰을 요청에 포함하도록 요구함으로써, 쿠키를 읽을 수 있는 권한이 있는 원본에서 요청이 온다는 것을 보장하므로 CSRF 공격을 효과적으로 방어합니다.
민감한 작업에 대해 재인증 사용
특히 민감한 작업(예: 비밀번호 변경 또는 대량 송금)에 대해 사용자는 재인증을 요구할 수 있습니다. 이는 사용자에게 추가적인 보안 검사를 제공합니다. 사용자가 CSRF 공격을 성공적으로 시작하더라도, 재인증 단계를 통과할 수 없습니다.
구현 제안:
- 민감한 작업을 수행하기 전에 별도의 인증 페이지로 리디렉션하십시오.
- 이 페이지에서 사용자가 비밀번호나 다른 신원 확인 정보를 입력하도록 요구하십시오.
- 인증이 통과되면 일회성 토큰을 생성하고 이후 민감한 작업에 이 토큰을 사용하십시오.
요약
이 심층적인 토론을 통해 CSRF 공격에 대해 보다 포괄적인 이해를 가지셨기를 바랍니다. 우리는 CSRF의 작동 원리뿐만 아니라 다양한 효과적인 방어 조치도 탐구했습니다. 이러한 모든 방법은 웹 애플리케이션의 보안을 효과적으로 강화할 수 있습니다.
이 지식이 일상적인 개발에서 CSRF 관련 문제를 보다 잘 처리하고 더 안전한 웹 애플리케이션을 구축하는 데 도움이 되기를 바랍니다.