보안

CSRF 공격 정리 (feat. 스프링 시큐리티)

Jake Seo 2022. 5. 3. 21:50

CSRF 란?

Cross-Site Request Forgery 라고 불리는 공격 방법이다. 웹사이트 취약점 공격의 일종이다. 특정 웹사이트에 로그인된 상태에서 해커가 원하는 URL 에 접근하게 만든다.

공격 주요 개념 정리

  • 브라우저가 SESSION ID 등의 인증정보를 가지고 있는 것을 활용하는 것이다.
  • 브라우저를 사용하지 않으면, 요청 시마다 인증정보를 수동으로 입력해줘야 하기 때문에 보통은 CSRF 공격에 당하지 않는다.
    • 그러나 이 역시도 애플리케이션에서 인증 상태를 저장하고 있다면, 브라우저와 동일하게 취약하다.

해킹 시나리오

시나리오 1. GET 요청의 경우

  • 피해자는 브라우저를 통해 https://any-bank-site.com/ 웹사이트에 접속하고 로그인한 상태이다.
  • 브라우저에는 로그인을 통해 생성된 인증정보를 가지고 있다. ex) JESSIONID=xxxx
    • 이로 인해 이용자는 로그인을 한번만 해도 모든 페이지에서 로그인된 효과를 받을 수 있다.
  • 이 웹사이트에는 게시판이 있고, 게시판은 HTML 태그가 허용되어 있다.
  • 사이트 이용 도중 피해자는 해커가 올린 게시물을 보게 된다.
  • 게시물에는 이미지가 있었고, 이미지는 다음과 같은 태그를 가지고 있다.
    <img src="https://any-bank-site.com/sendMoney?to=hacker&amount=100000">
  • 이는 사실 해커가 미리 알아낸 송금 URL이다. hacker 에게 100000 원을 보내게 만드는 행위를 하게 만드는 것이다.
  • img 태그의 src 에 있는 주소는 브라우저에 의해 해석될 때 자동으로 GET 요청이 가게 된다.
  • 브라우저는 은행 인증에 필요한 인증정보를 저장하고 있었기 때문에 위의 요청은 정상적으로 수행된다.
  • 피해자는 자신도 모르는 사이에 돈이 송금되어 버린다.

시나리오 2. POST 요청의 경우

  • 피해자는 이번에도 브라우저를 통해 https://any-bank-site.com/ 웹사이트에 로그인한 상태이다.

  • 브라우저에는 피해자가 로그인하면서 생성한 인증정보를 가지고 있다. ex) JESSIONID=xxxx

  • 은행 웹사이트에서 보안에 대한 고려를 하여, GET 이 아닌 POST 요청을 날렸을 때만 송금이 이루어지게 했다.

  • 은행 웹사이트에서는 송금을 위해 다음과 같은 폼을 제공한다.

    <form method="post"
      action="/transfer">
    <input type="text"
      name="amount"/>
    <input type="text"
      name="routingNumber"/>
    <input type="text"
      name="account"/>
    <input type="submit"
      value="Transfer"/>
    </form>
  • 위의 폼을 제출하면 아래와 같은 HTTP 요청이 발생하여 송금이 되는 형태이다.

POST /transfer HTTP/1.1
Host: any-bank-site.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876
  • 해커는 이 구조를 이해하고 자신이 작성한 게시글에 숨겨진 폼을 만들어놓는다.
<form method="post"
    action="https://any-bank-site.com/transfer">
<input type="hidden"
    name="amount"
    value="100.00"/>
<input type="hidden"
    name="routingNumber"
    value="evilsRoutingNumber"/>
<input type="hidden"
    name="account"
    value="evilsAccountNumber"/>
<input type="submit"
    value="Win Money!"/>
</form>
  • 해커는 피해자가 댓글 작성 시에 저 폼이 함께 제출되도록 해놓은 상태이다.
  • 만일 피해자가 해커의 게시글에서 댓글을 달게 되면, 의도치 않게 돈이 송금될 것이다.

CSRF 방지의 핵심

핵심은 공격자 도메인에서의 외부 요청과 서비스 웹사이트 상의 실제 요청을 구분하는 것이다. 이 두 요청을 구분할 수 없다면 CSRF 공격에 취약해질 수 있다.

스프링 시큐리티에서의 CSRF 방지 방법

스프링에서는 2가지 매커니즘으로 CSRF 공격을 막는다.

멱등성을 지켜야 한다

멱등성을 지킨다는 것은 GET, HEAD, OPTIONS, TRACE 요청이 있을 때, 애플리케이션의 상태를 변화시키지 않는 것을 말한다. 이 요청을 몇번이고 수행해도 애플리케이션의 상태는 동일해야 한다. 그래야 이후에 설명할 Synchronizer Token Pattern 을 적용하는 효과가 있다.

Synchronizer Token Pattern 알아보기

Synchronizer Token Pattern 은 CSRF 방지를 위해 가장 많이 쓰이는 방법이다. 각 HTTP 요청이 세션 쿠키와 더불어 랜덤으로 생성된 CSRF 토큰을 포함하고 있는지 확인하는 방법이다.

요청이 왔을 때, 서버에서 CSRF 토큰을 확인하지 못하면, HTTP 요청이 거부된다.

다만 멱등성이 보장되어야 이 방법이 효과가 있다. 그래야 다른 곳에서 들어온 GET 요청을 수용할 수 있고, 우리가 GET 요청을 할 때는 CSRF 토큰이 필요 없으므로, CSRF 토큰의 유출도 막을 수 있다.

<form method="post"
    action="/transfer">
<input type="hidden"
    name="_csrf"
    value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<input type="text"
    name="amount"/>
<input type="text"
    name="routingNumber"/>
<input type="hidden"
    name="account"/>
<input type="submit"
    value="Transfer"/>
</form>

위와 같이 _csrf 라는 토큰 값을 함께 보내야 요청이 유효하다.

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

해커는 올바른 _csrf 값을 생성할 수 없을 것이다.

SameSite Attribute 알아보기

외부 사이트에서 들어오는 경우, 쿠키가 함께 보내지지 않을 것이라고 나타내는 SameSite 속성을 명시할 수 있다.

스프링 시큐리티의 경우엔 세션 쿠키의 생성을 직접 다루지 않기 때문에, SameSite 속성을 지원하진 않는다. 그러나 Spring Session은 서블릿을 기반으로 한 애플리케이션에 SameSite 속성을 지원한다. 스프링 프레임워크의 CookieWebSessionIdResolver 는 WebFlux 를 베이스로 한 애플리케이션에서 SameSite 속성을 지원한다.

Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax

CSRF 보호를 비활성화(disable) 하는 경우?

When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection.

일반 웹사이트처럼 브라우저 유저가 요청을 하는 것이 아니라, 브라우저가 아닌 애플리케이션의 클라이언트가 이용하는 경우에는 CSRF 를 비활성화 할 수 있다. 그러나 이 경우에도 CSRF 의 위협에서 무조건 안전하다고 할 수만은 없다. 만일 상태가 있는 어플리케이션인 경우에는 CSRF 공격을 받으면 브라우저에서 공격을 받는 것과 동일한 효과를 가지기 때문에 조심해야 한다.

레퍼런스

CODEPATH CSRF
스프링 시큐리티 CSRF

반응형