본문 바로가기

java/spring

[security] 스프링 시큐리티 기능 정리

5.1. Authentication(인증)

5.1.1 Autentication Support

  • 스프링 시큐리티는 사용자 인증 처리를 기본적으로 지원한다

5.1.2 Password Storage

Password Storage History

  • 스프링 시큐티리는PasswordEncoder 인터페이스를 통해 비밀번호를 안전하게 저장할 수 있는 단방향 변환을 수행한다.
    • org.springframework.security.crypto.password.PasswordEncoder
  • 스프링 시큐리티 5.0부터 BcryptPasswordEncoder가 기본값이 되었다. (before: NoOpPasswordEncoder)

DelegatingPasswordEncoder

  • 과거 어플리케이션의 레거시 비밀번호 방식과 현재 변경된 비밀번호 인코딩 방식의 호환성을 보장하고자 사용된다
    • 비밀번호를 현재 권장하는 저장방식으로 인코딩을 보장한다
    • 비밀번호 검증은 최신, 레거시 형식 모두 지원한다
    • 나중에 인코딩을 변경할 수 있다

Password Storage Format

  • {id}encodedPassword
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG 
{noop}password 
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc 
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc= 
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
  • BcryptPasswordEncoder
  • NoOpPasswordEncoder
  • Pbkdf2PasswordEncoder
  • SCryptPasswordEncoder
  • StandardPasswordEncoder

Password Encoding

  • idForEncode 값이 인코딩에서 사용할 PasswordEncoder를 결정한다

Password Matching

  • {id} 를 기반으로 매칭되며, id는 생성자에서 제공한 PasswordEncoder로 매핑된다
  • id를 사용하여 어떠한 인코딩과도 매치시킬 수 있다 .
  • 암호화된 코드가 {id}를 prefix로 추가된다

PasswordEncoders

  • BCryptPasswordEncoder
    • bcrypt
    • 의도적으로 느리게 동작하기 떄문에 비밀번호를 해독하기 어렵다
  • Argon2PasswordEncoder
    • Argon2
    • 커스텀 하드웨어에서도 비밀번호를 해독하지 못하도록 Argon2는 의도적으로 메모리를 많이 사용하며 느리게 동작한다.
    • 최신 구현체는 BouncyCastle 라이브러리가 필요하다
  • Pbkdf2PasswordEncoder
    • PBKDF2
    • 의도적으로 느리게 동작하기 떄문에 비밀번호를 해독하기 어렵다
    • FIPS 인증 시 적합하다
  • SCryptPasswordEncoder
    • scrypt
    • 커스텀 하드웨어에서도 비밀번호를 해독하지 못하도록 Argon2는 의도적으로 메모리를 많이 사용하며 느리게 동작한다.

5.2. Protection Against Exploits (취약점 공격 보호)

5.2.1 Cross Site Request Forgery(CSRF)

  • 사이트 간 요청 위조.
  • 사용자가 로그인한 상태에서 CSRF 스크립트가 삽입된 페이지를 열면 공격에 노출될 수 있다
  • 공격받는 웹 사이트의 HTTP 요청과 공격하는 웹 사이트의 요청이 완전히 동일하기 떄문에 두 요청을 구분할 방법이 없다

Protecting Against CSRF Attacks

  • 동기화 토큰 패턴
  • 세션 쿠키의 SameSite 속성

Safe 메서드는 반드시 멱등성을 보장해야 한다

  • GET, HEAD, OPTIONS, TRACE 요청은 애플리케이션 상태를 변화시키면 안된다

Synchronizer Token Pattern

  • CSRF 공격을 방어하는 방법으로 동기화 토큰 패턴이 지배적이며 포괄적이다.
  • 이 패턴은 모든 HTTP 요청에 세션 쿠기와는 별도로 CSRF 토큰이라 불리는 안전한 랜덤으로 생성한 값을 추가한다
  • HTTP 요청을 제출하면 서버에서 CSRF 토큰을 찾아 요청에 있는 토큰을 검증한다
  • HTTP 요청에 브라우저가 자동으로 넣어주지 않는 CSRF 토큰이 있어야 한다
    • HTTP 파라미터나 HTTP헤더에서 실제 CSRF 토큰을 받으면 CSRF 공격을 방어할 수 있다
  • 어플리케이션 상태를 변경하는 HTTP 요청에 대해서만 CSRF 토큰을 사용하도록 조건을 완화해도 좋다
    • 이를 위해선 SAFE 메서드가 멱등성을 보장해야 한다
    • HTTP GET 요청에 랜덤 토큰을 사용하면 토큰이 유출될 수 있다 
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

 

SameSite Attribute

  • 쿠키에 SameSite 속성을 지정하는 방법이다. 서버에서 쿠키에 SameSite 속성을 명시하는 것으로 외부 사이트가 보내는 요청엔 쿠키를 사용하지 않겠다고 지정할 수 있다
  • 스프링 시큐리티는 세션 쿠키 생성을 직접 제어하지 않으므로 SameSite속성을 지원하지 않는다.
    • 서블릿 기반 어플리케이션 : 스프링 세션이 지원
    • 웹플럭스 기반 어플리케이션: 스프링 프레임워크의 CookieWebSessionResolver 
    • Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax
  • SameSite 속성
    • Strict: 동일 사이트가 보내는 모든 요청에 쿠키를 포함시킨다
    • Lax: 동일 사이트가 보내는 모든 요청에 쿠키를 포함시킨다. 또는 사이트가 다르더라도 top-level navigation 에서 보낸 요청이면서 멱등성을 보장하는 메소드면 포함시킨다
  • 세션 쿠키에 SameSite 솓성을 설정했다면 브라우저는 JSESSIONID를 계속 보낸다. 그렇지 않은 사이트에는 보내지 않기 때문에 CSRF 공격에 안전하다
  • 주의사항
    • Strict 속성을 지정한 경우 쿠키가 차단되기 때문에 인증 방식에 주의를 기울여야 한다 ex)다른 호스팅 요청이 발생할 경우 쿠키 차단)
    • 브라우저도 SameSite 속성을 지원해야 한다. 현재 최신 브라우저 대부분은 SameSite를 지원한다

When to use CSRF protection

  • 일반 사용자가 브라우저에서 처리할 수 있는 모든 요청에 CSRF 방어를 적용을 권장한다
  • 브라우저가 아닌 다른 클라이언트에서만 사용하는 경우 CSRF 방어'를 비활성화해도 좋다

CSRF protection and JSON

  • 자바스크립트에서 만드는 JSON 요청에서의 CSRF 방어를 시도할 수 있다
<!-- 취약점 예시1. content-type을 검증하지 않는 경우 JSON 데이터를 submit -->
<form action="https://bank.example.com/transfer" method="post" enctype="text/plain">
    <input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
    <input type="submit" value="Win Money!"/>
        <!-- expected result
        { 
          "amount": 100,
          "routingNumber": "evilsRoutingNumber",
          "account": "evilsAccountNumber",
          "ignore_me": "=test"
        }
        -->
</form>

<!-- 취약점 예시2. .json URL에 대해서는 여전히 취약점에 노출될 수 밖에 없다. -->
<form action="https://bank.example.com/transfer.json" method="post" enctype="text/plain">
    <input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
    <input type="submit" value="Win Money!"/>
</form>
 

Are JSON web services vulnerable to CSRF attacks?

I am building a web service that exclusively uses JSON for its request and response content (i.e., no form encoded payloads). Is a web service vulnerable to CSRF attack if the following are true? ...

stackoverflow.com

 

CSRF And Stateless Browser Applications

  • 상태가 없는 어플리케이션에서도 CSRF 공격에 노출되어 있다
  • CUSTOM 쿠키를 사용하는 경우 JSESSIONID 쿠키가 전송된 것과 동일하게 요청에 포함되고 이는 CSRF 공격을 발생할 수 있다
  • 기본 인증을 사용하는 어플리케이션도 JSESSIONID 쿠키가 전송된 것과 동일한 방식으로 브라우저가 모든 요청에 사용자 이름과 비밀번호를 추가하기 때문에 CSRF 공격에 노출되어 있다고 볼 수 있다

CSRF Considerations

Logging In

  • 로그인 요청 위조를 막으려면 로그인 HTTP 요청을 CSRF 공격으로 보호해야 한다. 
  • 공격이 이루어지는 과정
    • 악의적인 사용자가 본인의 credential로 CSRF 로그인을 수행한다. (희생자는 악의적인 사용자 계정으로 인증됨)
    • 악의적인 사용자는 트릭을 사용하여 취약 사이트에 방문하고, 민감정보를 입력하게 만듦
    • 악의적인 사용자 계정에서 작성된 정보이기 때문에 악의적인 사용자는 자신의 credential로 로그인하여 희생자가 작성한 민감 정보를 조회할 수 있다
  • 로그인 HTTP 요청을 CSRF로부터 보호할 경우 발생하는 문제점
    • 세션 타임아웃되면 사용자 요청을 거절당하는 불편함이 발생한다

Logging Out

  • 로그아웃 요청 위조를 막으려면 로그인 HTTP 요청을 CSRF 공격으로 보호해야 한다.
  • 로그아웃 HTTP 요청을 CSRF로부터 보호할 경우 발생하는 문제점
    • 세션 타임아웃되면 사용자 요청을 거절당하는 불편함을 발생한다

CSRF and Session Timeouts

  • 서버에서 비교할때 사용하는 CSRF 토큰은 종종 세션에 저장하곤 한다. 즉, 세션이 만료되는 즉시 서버에서 CSRF 토큰을 조회할 수 없음을 의미하고, HTTP 요청을 거절한다는 뜻이다. 타임아웃 해결 방법은 아래와 같다
    • 폼을 제출할 때 CSRF 토큰을 요청한다 => CSRF 토큰을 업데이트하고 제출할 수 있다
    • 자바스크립트로 사용자 세션이 곧 만료됨을 알린다 => 사용자 버튼을 통해 세션을 갱신한다
    • CSRF 토큰을 쿠키에 저장한다 => CSRF 토큰이 세션보다 더 오래 지속된다
  • CSRF 토큰을 디폴트로 쿠키에 저장하지 않는 이유는 헤더를 다른 도메인으로 설정하는 취약점 공격 때문이다. 또 다른 이유는 상태를 제거하면 토큰이 손상되었을 경우 강제로 종료할 수 없기 때문이다 

Multipart (file upload)

  • 파일 업로드 요청을 CSRF 공격으로부터 보호할 경우 HTTP 요청 body를 읽어 CSRF 토큰을 확인을 해야 한다. body를 읽는다는 것은 파일 업로드를 뜻하므로 문제가 발생할 수 있다 => CSRF 토큰을 읽으려고 파일 업로드를 허용해야 하는 상황
  • 방어 옵션은 아래와 같다
    • body에 CSRF 토큰 사용
      • body를 읽고 권한을 부여한다. 즉, 누구든지 서버에 임시 파일을 만들 수 있다. 하지만 인가된 사용자가 제출한 파일만 처리된다. 임시 파일 업로드가 서버에 주는 영향은 거의 무시해도 될 수준이기 때문에 일반적으로 권장되는 방식이다
    • URL에 CSRF 토큰 사용
      •  권한이 없는 사용자가 임시 파일을 서버에 만들지 못하게 하려면 폼의 action 속성에 쿼리 파라미터로 CSRF 토큰을 넣을 수 있다. 쿼리 파라미터가 유출되는 단점이 있다.
    • HiddenHttpMethodFilter
      • 폼 파라미터로 HTTP 메소드를 재정의한다 
      • 해당 필터는 스프링 시큐리티 필터보다 먼저 처리되야 한다 
<!-- CSRF Hidden Http Method Form --> 
<form action="/process"
    method="post">
    <!-- ... -->
    <input type="hidden"
        name="_method"
        value="delete"/>
</form>

5.2.2 Security HTTP Response Headers

Default Security Headers

//cache
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0

// content type options
X-Content-Type-Options: nosniff

// hsts
Strict-Transport-Security: max-age=31536000 ; includeSubDomains

// protect clickjacking
X-Frame-Options: DENY

// protect reflected xss
X-XSS-Protection: 1; mode=block

 

Cache Control

  • 스프링 시큐리티는 사용자 컨텐츠 보호를 위해 기본적으로 캐시를 비활성화한다 
  • 다른 사용자가 악의적으로 뒤로가기 버튼을 눌러 해당 정보를 보지 못하게 하기 위함이다
  • 어플리케이션 자체에서 Cache-Control 헤더를 사용하면 스프링 시큐리티는 디폴트 헤더를 추가하지 않는다

Content Type Options

  • 브라우저는 컨텐츠 스니핑(바이트 스트림의 콘텐츠를 검사하여 그 안에 있는 데이터의 파일 형식을 추론하는 방법)을 사용해 요청의 컨텐츠 타입을 추론한다.
  • 스프링 시큐리티는 기본적으로 컨텐츠 스니핑을 비활성화하기 때문에 컨텐츠 타입을 지정해주어야 한다
  • 컨텐츠 스니핑의 문제점은 악의적으로 다국어를 사용해 XSS 공격을 실행할 수 있다

HTTP Strict Transport Security(HSTS)

  • 웹 사이트 접속 시 프로토콜을 생략하는 것은 중간자 공격(Man in the Middle Attack)에 취약하다. 웹 사이트에서 리다이렉트하더라도, 악의적으로 최초 HTTP 요청을 가로채서 응답을 조작할 수 있다(악의적인 사이트로 리다이렉트한 뒤 credential 탈취) 
  • 프로토콜을 생략하고 접근하는 사용자가 많기 때문에 HTTP Strict Transport Security(HSTS)가 생겨났고, HTST 호스트에 추가되면 브라우저는 요청의 프로토콜을 해석하여 중간자 공격 받을 가능성을 떨어뜨린다
  • HTTPS 응답에만 HSTS 헤더를 추가한다. 브라우저가 헤더를 인식하려면 먼저 브라우저가 신뢰할 수 있는 CA에서 서명한 SSL 인증서로 커넥션을 맺어야한다 
  • HSTS 호스트로 표시하는 방법 예시) 1년간 브라우저에게 이 도메인을 HTST 호스트로 취급하라고 알림
Strict-Transport-Security: max-age=31536000 ; includeSubDomains ; preload

HTTP Public Key Pinning(HPKP)

  • 스프링 시큐리티는 하위 호환성을 위해 서블릿 환경에서 HPKP를 지원하고 있지만 더이상 권장하지는 않는다
  • 특정 웹 서버에서 사용할 공개키를 웹 클라이언트에 지정하는 방식으로, 위조 인증서를 사용하는 중간자(MITM) 고역을 방어한다. HPKP의 복잡성 때문에 더이상 권장하지 않고 있으며, 크롬은 지원을 중단했다

X-Frame-Options

  • 프레임에 웹 사이트를 넣을 수 있게 허용하는 것은 보안 이슈가 있다. 예를 들어 은행 사이트에 로그인한 사용자가 버튼을 클릭하면 다른 사용자에게 권한을 내줄 수 도 있다. 이런 류의 공격을 클릭재킹(Clickjacking)이라 한다
  • 컨텐츠 보안 정책(Content Security Policy, CSP)도 클릭재킹을 방어하는 최신 메커니즘이다
  • 클릭재킹 공격 방어 기법
    • 프레임을 막는 코드 작성한다(구 버전 브라우저)
    • X-Frame-Options 헤더로 방어한다(스프링 시큐리티는 아이프레임 내부에서 페이지를 렌더할 수 없게 막는다)

X-XSS-Protection

  • 일부 브라우저는 기본적으로 reflected XSS 공격을 필터링한다
  • 브라우저가 XSS 공격을 감지했을 때 할 일을 지시할 수 있다. 

Content Security Policy (CSP)

  • 컨텐츠 보안 정책(CSP)는 웹 어플리케이션에서 XSS(cross-site scripting)같은 컨텐츠 인젝션 공격 취약성을 개선할 수 있는 메커니즘이다
  • CSP는 웹 어플리케이션이 로드할 수 있는 리소스를 개발자가 직접 명시해서 궁극적으로 클라이언트(user-agent)에게 알리는 정책이다 
  • 컨텐츠 보안 정책은 모든 인젝션 공격 취약점을 해결해 주지 않는다(CSP는 컨텐츠 인젝션 공격의 피해를 최소하해준다). 일차적인 방어는 웹 어플리케이션에서 입력을 검증하고 출력을 인코딩하는 것이다
  • CSP 사용 방법은 아래의 내용을 Response Header를 추가하면 된다
    • Content-Security-Policy
    • Content-Security-Policy-Report-Only : 보안정책 모니터링 가능. 보통 개발 단계에서 사용한다
//csp ex
//script-src : 선언한 리소스 외의 다른 리소스에서 로드하려고 하면 user-agent에서 막힘
Content-Security-Policy: script-src https://trustedscripts.example.com


//csp with report-uri
// 웹 어플리케이션이 선언한 보안 정책을 위반하면, user-agent에 report-uri에 명시한 URL로 보고할 것을 지시한다
Content-Security-Policy: script-src https://trustedscripts.example.com; report-uri /csp-report-endpoint/

Referrer Policy

  • 사용자가 마지막 방문한 페이지를 가지고 있는 referrer 필드를 관리할 수 있는 메커니즘이다
  • 브라우저가 사용자가 이전에 방문했던 곳을 도착지에 알린다
Referrer-Policy: same-origin

Feature Policy

  • 웹 개발자가 원하는 대로 특정 API와 브라우저의 웹 기능을 활성화, 비활성화하거나 동작을 수정할 수 있게 해준다
  • 개발자가 브라우저의 '정책' 집합에 관여해서 사이트 전체에서 사용할 특정 기능을 강제할 수 있다
  • 예를 들어 사이트에서 접근할 수 있는 API를 제한하거나, 특정 기능에서의 브라우저 동작 방식을 바꿀 수 있다
Feature-Policy: geolocation 'self'

Clear Site Data

  • 쿠키, 로컬 스토리지 등의 브라우저 단 데이터를 삭제할 수 있는 메커니즘이다
  • 로그아웃할 때 실행하기 알맞은 작업이다
Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"

Custom Headers

  • 스프링 시큐리티는 좀 더 일반적인 보안 헤더를 어플리케잇션에 편리하게 추가하도록 지원한다. (훅도 제공한다)

5.2.3 HTTP

  • 스태틱 리소스를 포함한 모든 HTTP 통신은 TLS로 보호해야 한다
  • 스프링 시큐리티는 프레임워크이기 때문에 HTTP 커넥션을 직접 다루지 않으며, HTTPS를 직접적으로 지원하지 않는다. 다만 HTTPS 사용에 도움을 될만한 기능을 제공한다

Redirect to HTTPS

  •  클라이언트가 HTTP를 사용했을 때 서블릿, 웹플럭스 환경 모두 HTTPS로 리다이렉트하게 설정할 수 있다

Strict Transport Security

  • 기본적으로 활성화되어 있음

Proxy Server Configuration

  • 프록시 서버를 사용하는 경우 어플리케이션 설정을 잘 확인해야 한다.
  • RFC 7239를 사용해서 로드 밸런서를 사용중임을 명시하여 로드밸런서의 존재를 알려야 한다
  • 어플리케이션 서버에 X-Forwarded 헤더를 알 수 있게 해주어야 한다 
    • 톰캣: RemoteIpValve
    • 제티: ForwaredRequestCustomizer
    • 스프링: ForwaredHeaderFilter
    • 스프링 부트: server.use-forward-headers 프로퍼티 추가

 

https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#features

 

'java > spring' 카테고리의 다른 글

spring4shell 취약점 내용 정리  (0) 2022.04.01
Spring Scheduler  (0) 2022.03.18