본문 바로가기

개발공부/그 외

[WEB] CSRF란?

💡 코드가 보이지 않으시다면 드래그 혹은 오른쪽 아래 🌜 아이콘을 눌러 테마 색을 변경해주세요.

 

 

안녕하세요!

키크니 개발자 입니다. 🦒

 

강의를 보다가 CSRF라는 개념이 나오길래 다시 한번 정리하고자 합니다.

 

CSRF란?

Cross-Site Request Forgery로 사이트 간 요청 위조를 의미합니다.

 

웹 애플리케이션 취약점 중 하나로

사용자가 자신의 의지와 무관하게 공격자가 의도한 행동을 하여

특정 웹 페이지를 보안에 취약하게 한다거나 수정, 삭제 등의 작업을 하게 만드는 공격방법을 의미합니다.

 

XSS 공격과 차이

XSS 공격(Cross Site Scripting, 사이트간 스크립팅) 은 사용자가 웹사이트를 신용하여 악성 스크립트가 실행된다면,

CSRF 공격은 반대로 특정 웹사이트가 사용자의 브라우저를 신용하여 발생하는 공격이다.

간단히 말해서 XSS 공격은 악성 코드가 클라이언트에서 발생하는데,

CSRF 공격은 악성 코드가 서버에서 발생한다고 볼 수 있습니다.

 

공격방법은 어떻게?

  1. 공격자는 이메일이나 게시판에 CSRF 스크립트가 포함 된 게시물을 전송합니다.
  2. 관리자는 공격자가 등록한 CSRF 스크립트가 포함 된 게시물을 확인합니다.
  3. 관리자가 CSRF 스크립트가 포함 된 게시물을 열람하면, 관리자의 권한으로 공격자가 원하는 CSRF 스크립트 요청이 발생합니다.
  4. 공격자가 원하는 CSRF 스크립트가 실행되어, 관리자 및 사용자의 피해가 발생합니다.

 

추가적인 예시

대표적으로 GET / POST 를 통한 공격에 대해 알아보겠습니다.

GET Examples

GET http://bank.com/trasnfer?accountNumber=1122&amount=10000

위 예시는 로그인을 한 사용자가 특정 계좌 ("1122")로 금액(10000)을 이체하기 위해 사용하는 GET 요청입니다.

위의 URL에서 공격자가 자신의 계좌("5678")으로 금액(10000)을 이체 하도록 하려면 피해자가 요청을 thriger 하도록 해야합니다.

GET http://bank.com/trasnfer?accountNumber=5678&amount=10000

공격자가 위와 같은 상황을 만들기 위해서는 여러 방식이 존재합니다.

 

  • Link(링크) : 공격자는 이체를 실행하도록 하기 위해 사용자(희생자)가 링크를 클릭하도록 유도할 수 있습니다.
<a href="http://bank.com/transfer?accountNumber=5678&amount=10000">
Pictures@
</a>

 

  •  Image(이미지) : 공격자는 대상 URL과 함께 <img /> 태그를 이미지 소스로 사용할 수 있습니다. 따라서 사용자는 따로 클릭이 필요하지가 않는데, 페이지가 로드되면 요청은 자동적으로 실행이 됩니다.
<img src="http://bank.com/transfer?accountNumber=5678&amount=10000"/>

 

POST Examples

POST http://bank.com/transfer
accountNumber=1122&amount=10000

위와 같이 POST 요청에서 공격자는 피해자가 다음과 같이 실행되도록 합니다.

(accountNumber가 1122인 것을 5678로 실행되도록 합니다.)

POST http://bank.com/transfer
accountNumber=5678&amount=10000

위와 같은 경우에는 <a> 태그나 <img /> 태그 모두 동작하지 않습니다.

따라서 공격자는 다음과 같이 <form> 태그가 필요합니다.

(<input /> 태그에 accountNumber에 5678의 값을 넣은 상태입니다.) 

<form action="http://bank.com/transfer" method="POST">
    <input type="hidden" name="accountNumber" value="5678"/>
    <input type="hidden" name="amount" value="10000"/>
    <input type="submit" value="Pictures@"/>
</form>

자바 스크립트를 사용한다면 다음과 같이 양식을 자동으로 전송(submit) 하도록 설정할 수 있습니다.

<body onload="document.forms[0].submit()">

<form>

...

 

방어방법은?

Spring의 공식 문서에서는 다음과 같이 언급이 되어있습니다.

 

When to 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 공격을 방어하기 위해 대표적으로 다음과 같은 방법들이 있습니다.

  • Referrer 검증
  • Spring Security CSRF Token 사용

보통 CSRF 공격의 방어는 조회(GET) 데이터는 방어 대상에 두지 않고,

POST, PATCH, DELETE 메소드에만 적용을 합니다.

(물론 정말 중요한 데이터를 조회할 때에는 GET 메서드에도 방어를 해야할 수 있습니다.)

 

Referrer 검증

서버단에서 request의 referrer를 확인하여 domain이 일치하는지 검증하는 방법입니다.

referrer 검증만으로 대부분의 CSRF 공격을 방어할 수 있습니다.

 

Spring Security CSRF Token

임의의 토큰을 발급한 후 자원에 대한 변경 요청일 경우 Token 값을 확인한 후 클라이언트가 정상적인 요청을 보낸것인지 확인합니다.

만약 CSRF Token이 존재하지 않거나, 기존의 Token과 일치하지 않는 경우 4XX 상태코드를 리턴합니다.

요청한 파라미터에 Token을 포함한 뒤 전송하여 정상적인 요청인지 혹은 공격자로부터 의도 된 요청인지 판단할 수 있습니다.

 

Spring Security 에서는 @EnableWebSecurity 어노테이션을 지정할 경우 자동으로 CSRF 보호 기능이 활성화 됩니다.

따라서 CSRF 비활성화가 필요한 경우 아래와 같이 csrf().disable() 설정을 추가합니다.

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf().disable();
    }
}

CSRF 보호는 데이터 변조가 가능한 POST, PUT 등의 method에 적용합니다.

csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())를 사용하여 CSRF Token을 지정합니다.

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

 

csrfTokenRepository()

public CsrfConfigurer<H> csrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
        Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
        this.csrfTokenRepository = csrfTokenRepository;
        return this;
    }

csrfTokenRepository 메서드의 파라미터는 CsrfTokenRepository 인터페이스를 받고 있습니다.

실제 메서드에 들어갈 구현체는 CookieCrsfTokenRepository 입니다.

 

CookieCsrfTokenRepository의 공식문서에는 다음과 같이 설명되어 있습니다.

A CsrfTokenRepository that persists the CSRF token in a cookie named 
"XSRF-TOKEN" and reads from the header "X-XSRF-TOKEN" following the conventions of AngularJS.
When using with AngularJS be sure to use withHttpOnlyFalse().

-> CsrfTokenRepository는 "XSRF-TOKEN"이라는 이름의 쿠키에서 CSRF 토큰을 유지하고,
AngularJS의 규약을 따르는 헤더인 "X-XSRF-TOKEN"에서 읽습니다.
AngularJS와 함께 사용할 경우 withHttpOnlyFalse()와 함께 사용해야 합니다.

CookieCsrfTokenRepository

public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
    static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
    static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
    static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
    private String parameterName = "_csrf";
    private String headerName = "X-XSRF-TOKEN";
    private String cookieName = "XSRF-TOKEN";
    private boolean cookieHttpOnly = true;
    private String cookiePath;
    private String cookieDomain;
    private Boolean secure;
    private int cookieMaxAge = -1;
    
	...

}

CookieCsrfTokenRepository 클래스 내부를 확인해보면 쿠키 네임, 파라미터 네임, 헤더 네임이 디폴트로 설정 되어있습니다.

 

withHttpOnlyFalse() 메서드를 통해 cookieHttpOnly 의 값은 false 로 설정이 됩니다. 

Http Only Cookie는 다음 과 같습니다.

An HttpOnly Cookie is a tag added to a browser cookie that prevents
client-side scripts from accessing data.
It provides a gate that prevents the specialized cookie from being accessed by anything other than the server.
Using the HttpOnly tag when generating a cookie helps mitigate the risk of client-side scripts accessing the protected cookie,
thus making these cookies more secure.

-> Http Only Cookie는 클라이언트측 스크립트가 데이터에 접근하는 것을 방지하는 브라우저 쿠키에 추가된 태그입니다.
이는 서버 이외의 다른 사용자가 특수 쿠키에 접근하지 못하도록 하는 게이트를 제공합니다.
쿠키를 생성할 때 HttpOnly 태그를 사용하면 클라이언트 스크립트가 보호된 쿠키에
액세스하는 위험을 줄일 수 있으므로 쿠키의 보안을 강화할 수 있습니다.

 

CSRF를 비활성화하는 경우는 어떤 경우인가요?

CSRF는 "사이트 간 요청"이 발생하기 쉬운 웹에 대해 요청할 때 필요합니다.

이러한 애플리케이션은 보통 템플릿 엔진(Thymeleaf, JSP) 등을 사용하여 서버 측에서 전체 HTML을 생성하는 구조입니다.

하지만 최신의 애플리케이션은 주로 REST API의 앤드포인트에 의존하는 구조입니다.

이러한 앤드포인트는 대부분 JSON 방식으로 통신을 하도록 설계가 되어있습니다.

 

REST API는 HTTP 형식을 따르기 때문에 무상태(stateless)이며, 서버쪽의 세션이나 브라우저 쿠키에 의존하지 않습니다.

CSRF 설명에 따르면, 위와 같은 REST API의 조건(쿠키 기반의 세션 처리)에는 더 이상 CSRF이 관련이 없으므로 

이러한 API는 CSRF 공격을 받을 가능성이 존재하지 않습니다.

 

따라서 대부분의 현재 애플리케이션(API만 노출하는)의 경우 앤드포인트에 대해 CSRF를 비활성화하고 있습니다.

 

 

References

https://www.baeldung.com/spring-security-csrf

https://zzang9ha.tistory.com/341

https://brownbears.tistory.com/251

https://velog.io/@gwanuuoo/CSRF-%EA%B3%B5%EA%B2%A9%EA%B3%BC-%EB%B0%A9%EC%96%B4-%EA%B8%B0%EB%B2%95

반응형