'csrf'에 해당되는 글 2건

  1. 2019.04.29 Spring Security CSRF 프로텍션 4
  2. 2014.07.14 CSRF(cross site request forgery) 3
Java/Spring2019. 4. 29. 01:18
반응형

Spring Security로 CSRF 프로텍션 적용

최근의 Java기반의 웹 프로젝트는 대부분 Spring(스프링) 프레임워크 기반으로 구현되기 때문에 자연스럽게 Spring Security를 이용하여 보안관련 기능들을 구현하게 됩니다. 그중 CSRF(Cross Site Request Forgery : 사이트 간 요청 위조)는 개발중에는 매우 귀찮은 존재입니다. 웹에서 CSRF 프로텍션은 보안 대책으로 거의 필수적으로 요구되지만 개발 편의성 문제로 개발중에는 전혀 신경쓰지 않거나 비활성화해두는 경우가 많이 있습니다. 결국에는 보안점검을 통해 지적받고 일괄적으로 소스코드를 변경합니다. 하지만 급하게 고친 코드는 항상(?) 문제를 일으킵니다. 또한 운영중에도 잘못된 수정으로 배포후에 예외를 만나는 경우가 많이 있습니다.

CSRF 프로텍션 활성화

먼저 다들 아시는 내용이겠지만 복습차원에서 Spring Security에서 CSRF 기능을 활성화 시키는 방법에 대해서 알아보겠습니다.

dependencies {
    //...
    compile group: 'org.springframework.security', name: 'spring-security-config', version: '5.1.5.RELEASE'
    compile group: 'org.springframework.security', name: 'spring-security-web', version: '5.1.5.RELEASE'
    //...
    //compile('org.springframework.boot:spring-boot-starter-security') - starter를 이용하는 경우
}

위와같이 Spring Security 의존성을 추가하거나 Spring Boot를 사용하는 경우는 Starter를 통해서 의존성을 추가할 수 있습니다.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //...Some Configuration
        http.csrf();
        //...Other Configuration
    }
}

설정방법은 위와 같지만 @EnableWebSecurity에 의해서 Spring Security가 설정되면 기본적으로 CSRF 프로텍션은 활성화되므로 http.csrf(); 이 설정은 사실상 필요없습니다만 설정방식을 명시적으로 보여주는 차원에서 위와같이 기술했습니다.

Spring Security의 CSRF 프로텍션은 Http 세션과 동일한 생명주기(Life Cycle)을 가지는 토큰을 발행한 후 Http 요청(PATCH, POST, PUT, DELETE 메소드인 경우)마다 발행된 토큰이 요청에 포함되어(Http헤더 혹은 파라메터 둘중 하나) 있는지 검사하는 가장 일반적으로 알려진 방식의 구현이 설정되어 있습니다.

Spring Security에서는 토큰의 저장소로 Cookie를 사용하는 방식도 아래의 예와같은 설정을 통해서 지원하고 있습니다. 이 경우에는 Http헤더 혹은 파라메터를 통해 전송된 토큰을 쿠키를 통해서 전송된 토큰과 검증하는 방식으로 동작하게 됩니다. API서버 등과 같이 세션을 사용하지 않는 Stateless한 경우에는 어쩔수 없이 채택하는 방식이긴 합니다만 일반적으로 세션의 속성으로 토큰을 보관하는 방식이 좀더 안전하다고 알려져 있으므로 추천드리지 않습니다. 자세한 내용을 설명하는 것은 이 글의 범위를 벗어나므로 간단하게 설명하면 Cookie를 저장소로 사용할 경우 Cookie에 토큰이 주입되거나 토큰의 유출이 발생할 경우 토큰의 무효화를 할 수 없는 등의 취약점이 있습니다. 더 자세한 내용은 Spring Security의 문서를 참조하시기 바랍니다.

예) http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());

CSRF 토큰의 구조

public interface CsrfToken extends Serializable {
    String getHeaderName();
    String getParameterName();
    String getToken();
}

위와같이 Http헤더의 헤더명과 파라메터명 그리고 토큰으로 구성되어 있습니다. Http헤더를 사용할 경우는 헤더명을 키로 토큰을 값으로 전송하면 되고 파라메터로 전송할 경우는 파라메터명을 키로 역시 토큰을 값으로 전송하면 됩니다.

CSRF 토큰을 Http요청에 포함시키는 방법

CSRF토큰은 Http 세션과 동일한 생명주기를 가지기 때문에 세션속성으로 저장되게 됩니다. Spring Security의 기본구현은 Http 세션에 org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN 와 같이 다소 긴이름으로 저장되기 때문에 세션을 직접 참조하는 방식을 사용하기가 곤란합니다. 따라서 Spring Security는 해당 토큰을 Http요청마다 '_csrf' 라는 간단한 이름의 Http Request 속성으로 바인딩시켜 사용성을 올려 줍니다.

1. Form의 파라메터

위에서 설명한 바와 같이 Http Request 속성에 바인딩 되어 있기때문에 el표현식을 사용할 수 있는 템플릿 엔진을 사용중이라면 간단하게 Form의 파라메터로 추가할 수 있습니다.

<!-- Some HTML -->
<form action="/some-uri" method="post">
  <input type="text" name="some" value="thing" />
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
  <input type="submit" />
</form>
<!-- Other HTML-->

위와같이 '_csrf' 라는 Http Request 속성을 통해서 파라메터 명과 토큰을 취득하여 Form의 숨겨진 요소로 추가하면 간단하게 포함 시킬 수 있습니다.

Spring MVC에서 제공하는 <form:form> 태그를 사용하거나 Thymeleaf 2.1이상이면서 @EnableWebSecurity를 사용한 경우에는 CsrfRequestDataValueProcessor가 적용되어 필요한 form에 자동으로 CSRF 토큰이 포함되게 됩니다.

2. Ajax 요청시 헤더

사용자 경험을 향상시키기 위해서 화면이동을 제외한 처리는 대부분 Ajax를 통한 JSON 요청으로 처리하는 경우가 많습니다. 이경우 위와같이 파라메터로 토큰을 전송할 수 없으므로 Http 헤더를 통해서 전송할 수 있습니다.

$.ajaxPrefilter(function (options) {
  var headerName = '${_csrf.headerName}';
  var token = '${_csrf.token}';
  if (options.method === 'POST') {
      options.headers = options.headers || {};
      options.headers[headerName] = token;
  }
});

위의코드는 jQuery를 사용하는 경우 모든 Ajax Post 요청에 대해 Http 헤더에 CSRF 토큰을 설정하는 코드 입니다. 혹시 초반에는 CSRF설정을 무효화 하고 구현하는 경우에는 Ajax요청을 특정 라이브러리로 단일화 하여 구현하기를 추천드립니다. 그럴 경우에는 위처럼 단순한 설정만으로도 모든 요청에 CSRF 토큰을 추가하는 것이 가능하므로 소스코드 수정을 최소화 할 수 있습니다.

그 이외의 설정

비활성화

CSRF 프로텍션을 비활성화 시키고 싶은경우에는(추천하지 않지만) 아래와 같이 disabled()를 호출하면 됩니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    //...Some Configuration
    http.csrf().disabled();
    //...Other Configuration
}

특정 요청만 예외처리

@Override
protected void configure(HttpSecurity http) throws Exception {
    //...Some Configuration
    http.csrf()
        .ignoringAntMatchers()
        .ignoringRequestMatchers();
    //...Other Configuration
}

Ant Matcher 혹은 Request Matcher를 지정하여 특정 요청은 대상에서 제외할 수 있습니다. 예를들어 외부에서 오는 요청이나 콜백의 경우 제외해야할 필요가 있는 경우가 있습니다.

특정 요청은 적용

@Override
protected void configure(HttpSecurity http) throws Exception {
    //...Some Configuration
    http.csrf()
        .requireCsrfProtectionMatcher();
    //...Other Configuration
}

그다지 쓸일이 있어보이지는 않습니다만 특정 요청은 적용되어야 할 경우에는 위의 설정을 사용할 수 있습니다. 예를들어 기본적으로 제외되어있는 GET, HEAD, TRACE, OPTIONS 메소드중 일부 요청을 추가할 수 있을 것 같습니다.

예외처리

토큰검증에 실패 했을경우 403 Http 응답코드가 반환됩니다. 이때 이동할 페이지 혹은 예외처리를 위와같은 설정을 통해 할 수 있습니다. 세션이 만료되어 서버에서 토큰정보를 취득할 수 없는 경우에는 MissingCsrfTokenException 예외가 발생하고 전송된 토큰이 다른 경우에는 InvalidCsrfTokenException 예외가 발생하므로 필요에 따라 적절한 예외처리를 기술하면 되겠습니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    //...Some Configuration
    http.exceptionHandling()
        .accessDeniedPage("/denied.html")
        .accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) throws IOException, ServletException {
                if (exception instanceof MissingCsrfTokenException) {
                    //Some Exception Handling
                } else if (exception instanceof InvalidCsrfTokenException) {
                    //Some Exception Handling
                }
        });
    //...Other Configuration
}

이상으로 Spring Security를 통해 CSRF 프로텍션을 적용하는 방법에 대해서 알아보았습니다. 위에서 언급한대로 CSRF 프로텍션은 웹 보안에있어 거의 필수로 요구되므로 가급적 개발초기부터 고려해서 빠지지 않게 적용되도록 합시다.

참고자료

2014/07/14 - [Security] - CSRF(Cross Site Request Forgery)

Spring Security 5.0.x - 19. Cross Site Request Forgery

Posted by Reiphiel
Security2014. 7. 14. 23:27
반응형

CSRF란?


 CSRF(Cross site request forgery, 사이트간 요청 위조)란 웹 사이트의 취약점을 이용하여 사용자가 의도하지 않는 요청을 송신하도록 하는 공격의 의미합니다. 이는 http프로토콜의 상태없음(stateless) 특성에 기인한 특정 웹 어플리케이션에 대한 일련의 요청들의 상관관계를 특정할 수 없기 때문에 세션 유지등에 일반적으로 사용되는 쿠키 정보 등이 조건만 만족한다면 자동적으로 송신되기 때문에 가능합니다. 여기서 상관관계를 특정할 수 없다는 의미는 예를 들어 카트화면 -> 주문정보 입력 -> 주문완료로 이어지는 주문 프로세스를 가진 웹 어플리케이션에서 각각의 페이지에대한 요청이 연속적으로 이어지는지에 대한 제어를 할 수 없다는 것을 의미합니다. 이 공격수법은 결과적으로 피해자가 의도한 요청과 동일한 과정으로 진행되므로 공격자에 대한 추적이 어려울 수 있으며 피해자에게 인가된 범위안에서만 공격이 이루어진다는 특징이 있습니다.(피해자가 특정 웹 어플리케이션의 관리자 계정으로 인증&인가된 상태라면 피해범위가 커질 수 있습니다.)



대략적인 공격 시나리오


  1. 공격자가 공격코드를 가진 웹페이지를 제작하여 공개하거나 특정 웹 사이트에 공격용 코드를 삽입
  2. 피해자가 공격자가 준비해둔 페이지에 접속
  3. 피해자(피해자의 브라우저)는 공격자가 준비해둔 요청을 서버로 송신



공격방법


 CSRF공격방법에는 정형화된 수법이 있다기 보다는 웹에 요청을 보낼수 있는 모든 방법이 공격방법이 된다고 할 수 있다. javascript와 ajax를 이용한 방법, 전통적인 form방법, img태그를 이용한 방법 등등 요청을 보낼수 있는 방법이라면 그 어떤 것이라도 가능하다.



대책


 일반적으로 가장 널리 이용되는 방법에는 Synchronizer token pattern(동기화된 토큰 패턴)이 있다. 이 패턴은 서버 사이드(세션 스코프 등)에 보관된 토큰을 CSRF방어가 필요한 요청마다 포함(요청할 form에 hidden필드를 이용하여 토큰을 추가)시켜서 요청하고 서버에서 비교하는 방식으로 CSRF를 방어하는 방법이다. 가장 간단한 방식으로 사용자 경험에 영향을 주지 않는 방식으로 방어할 수 있으므로 널리 사용된다. 이 경우 토큰은 세션ID와 동일한 수준의 보호 수단이 필요하다.(SSL이용, URL노출 금지, 출력대상 페이지 캐시 컨트롤, xss취약점 방어 등등) 토큰의 유출이 염려스러운 경우 토큰 갱신 혹은 세션 파기 등등 즉각적인 조치가 필요하다.


 이외에 제한적인 환경에서 이용가능한 방법으로 Referer Header 참조하여 사이트 외부에서의 요청을 배제하는 방법등이 있겠다. 물론 Header영역의 데이터는 기본적으로 조작가능한 데이터 이기는 하지만 일반적인 브라우징 환경에서는 조작하기 힘들기 때문에(불가능하다는 의미는 아님) 폐쇄적인 환경등에서는 유효한 해결책이 될수도 있다. 물론 위와같은 방법으로는 사이트 내부에서의 공격에 취약하다. 게다가 http/https이동간, 최근의 브라우저등에 탑재된 private browsing 옵션에 의해서 Referer헤더가 송신 안되는 제약이 있으므로 제한적으로 이용해야 할 듯하다.


 가장 강력한 대책으로는 CAPCHA라든이 재인증등의 방법이 있겠지만 사용자 경험에 영향을 미치므로 신중하게 생각해서 도입해야 할 듯 하다.



참조

Open Web Application Security Project (OWASP). Cross-Site Request Forgery (CSRF).https://www.owasp.org/index.php/CSRF




※본 포스트의 내용은 해킹을 조장하기위해 쓰여진 글이 아닙니다. 위의 내용을 악용할 경우 처벌받을 수도 있으므로 내용을 이해하고 방어할 목적으로만 이용하시길 당부합니다.



'Security' 카테고리의 다른 글

robots.txt와 보안  (0) 2015.03.18
보안에 대한 생각 - 파일 해시 검증하기  (0) 2015.03.04
Session Fixation 취약점  (2) 2014.07.02
웹 파일 업로드 보안 취약점  (2) 2013.12.20
윈도우 Self-Signed SSL증명서 생성  (0) 2013.12.03
Posted by Reiphiel