• μλ νμΈμ~ μ΄μ μ μ΄μνλ λΈλ‘κ·Έ λ° GitHub, κ³΅λΆ λ΄μ©μ μ 리νλ Study-GitHub κ° μμ΅λλ€!
• π
π CSRF(Cross-Site Request Forgery)
μλ νμΈμ, μ΄λ²μ μ 리ν λ΄μ©μ CSRF(Cross-Site Request Forgery) μ λλ€!
μ€νλ§ μν리ν°μ μ΄λ Έν μ΄μ μΈ @EnableWebSecurity μ΄λ Έν μ΄μ μ κΈ°λ³Έμ μΌλ‘ CSRF 곡격μ λ°©μ§νλ κΈ°λ₯μ μ§μνκ³ μμ΅λλ€.
μν리ν°λ₯Ό μ μ©νλ©΄ λ³΄ν΅ configure() λ©μλμλ μλμ κ°μ΄ csrf().disable()λ‘ μ μ©μ νλλ°μ,
μ΄λ¬ν CSRFλ 무μμΈμ§ μμλ³΄κ² μ΅λλ€! π
π― CSRFλ?
μ¬μ΄νΈ κ° μμ² μμ‘°(Cross-Site Request Forgery)
CSRFλ μΉ μ ν리μΌμ΄μ μ μ·¨μ½μ μ€ νλλ‘, μ΄μ©μκ° μλνμ§ μμ μμ²μ ν΅ν 곡격μ μλ―Έν©λλ€.
μ¦ CSRF 곡격μ΄λ, μΈν°λ· μ¬μ©μ(ν¬μμ)κ° μμ μ μμ§μλ 무κ΄νκ² κ³΅κ²©μκ° μλν νμ(λ±λ‘, μμ , μμ λ±)λ₯Ό νΉμ μΉμ¬μ΄νΈμ μμ²νλλ‘ λ§λλ 곡격 μ λλ€.
β» 2008λ μ λ°μνλ μ₯μ μ κ°μΈμ 보 μ μΆ μ¬κ±΄μμλ κ΄λ¦¬μ κ³μ μ νμ·¨νλλ° μ΄ CSRF λ°©λ²μ΄ μ¬μ©λμμ΅λλ€.
π― CSRFμ 곡격 λ°©λ²
CSRFμ 곡격μ μ¬λ¬ λ°©μμ΄ μ‘΄μ¬νλλ°μ, λνμ μΌλ‘ GET / POST λ₯Ό ν΅ν 곡격μ λν΄ μμλ³΄κ² μ΅λλ€.
GET Examples
GET http://bank.com/trasnfer?accountNumber=1122&amount=10000
μ μμλ λ‘κ·ΈμΈμ ν μ¬μ©μκ° νΉμ κ³μ’("1122")λ‘ κΈμ‘(10000)μ μ΄μ²΄νκΈ° μν΄ μ¬μ©νλ GET μμ²μ λλ€.
μμ URLμμ 곡격μκ° μμ μ κ³μ’("5678")μΌλ‘ κΈμ‘(10000)μ μ΄μ²΄ νλλ‘ νλ €λ©΄ νΌν΄μκ° μμ²μ trigger νλλ‘ ν΄μΌν©λλ€.
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 Example
μμμ GET λ°©μμ μμ λ₯Ό λ€μ΄λ³΄μλλ°μ, μ΄λ²μλ POST μμ λ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
POST http://bank.com/transfer
accountNumber=1122&amount=10000
μμ κ°μ΄ POST μμ²μμ 곡격μλ νΌν΄μκ° λ€μκ³Ό κ°μ΄ μ€νλλλ‘ ν΄μΌν©λλ€.
POST http://bank.com/transfer
accountNumber=5678&amount=10000
μμ κ°μ κ²½μ°μλ <a> νκ·Έλ <img /> νκ·Έ λͺ¨λ λμνμ§ μμ΅λλ€.
λ°λΌμ 곡격μλ λ€μκ³Ό κ°μ΄ <form> νκ·Έκ° νμνλ°μ,
<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>
...
π― CSRF λ°©μ΄ λ°©λ²
μμμ CSRFμ λν κ°λ λ° κ³΅κ²© λ°©λ²μ λν΄ μμ보μμ΅λλ€.
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 μνμ½λλ₯Ό 리ν΄ν©λλ€.
β» νμ리ν ν νλ¦Ώ λ° jspμ spring:form νκ·Έλ₯Ό μ¬μ©νλ€λ©΄ κΈ°λ³Έμ μΌλ‘ csrf tokenμ λ£μ΄μ€λλ€.
μμ²ν νλΌλ―Έν°μ Tokenμ ν¬ν¨ν λ€ μ μ‘νμ¬ μ μμ μΈ μμ²μΈμ§ νΉμ 곡격μλ‘λΆν° μλλ μμ²μΈμ§ νλ¨ν μ μμ΅λλ€.
Spring Securityμμλ @EnableWebSecurity μ΄λ Έν μ΄μ μ μ§μ ν κ²½μ° μλμΌλ‘ CSRF λ³΄νΈ κΈ°λ₯μ΄ νμ±νκ° λ©λλ€.
λ°λΌμ CSRF λΉνμ±νκ° νμν κ²½μ° μλμ κ°μ΄ csrf().disable() μ€μ μ μΆκ°ν©λλ€.
CSRF 보νΈλ λ°μ΄ν° λ³μ‘°κ° κ°λ₯ν POST, PUT λ±μ method μ μ μ©ν©λλ€.
csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())λ₯Ό μ¬μ©νμ¬ CSRF Tokenμ μ§μ ν©λλ€.
csrfTokenRepository()
csrfTokenRepository λ©μλμ νλΌλ―Έν°λ CsrfTokenRepository μΈν°νμ΄μ€λ₯Ό λ°κ³ μμ΅λλ€.
μ€μ λ©μλμ λ€μ΄κ° ꡬν체λ CookieCsrfTokenRepository μ λλ€.
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
CookieCsrfTokenRepository ν΄λμ€ λ΄λΆλ₯Ό νμΈν΄λ³΄λ©΄ μΏ ν€ λ€μ, νλΌλ―Έν° λ€μ, ν€λ λ€μμ΄ λν΄νΈλ‘ μ€μ λμ΄ μμ΅λλ€.
- DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
- DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
- DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
withHttpOnlyFalse() λ©μλλ₯Ό ν΅ν΄ cookieHttpOnly μ κ°μ falseλ‘ μ€μ μ΄ λλλ°μ,
HttpOnly 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 ν μ€νΈ
μμμ μμ±ν μ€μ λ° μ½λλ₯Ό ν΅ν΄ κ°λ¨νκ² ν μ€νΈλ₯Ό μ§νν΄λ³΄λλ‘ νκ² μ΅λλ€.
// Controller
package com.example.securitycsrf.csrf;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RequestMapping("/csrf")
@RestController
public class CsrfController {
@GetMapping
public void csrfGet() {
log.info("GET");
}
@PostMapping
public void csrfPost() {
log.info("POST");
}
}
// Spring Security Config
package com.example.securitycsrf.csrf;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
GET νΈμΆ
μ€μ μμ csrfTokenRepository()λ₯Ό μ€μ νκ³ GET λ©μλλ₯Ό νΈμΆν΄λ³΄λ©΄ μμ κ°μ΄ μΏ ν€ μ 보λ₯Ό λ°μ μ μμ΅λλ€.
(κΈ°λ³Έμ μΌλ‘ GET λ°©μμ CSRF Token μ 보λ₯Ό λκΈ°μ§ μμλ λ©λλ€.)
POST νΈμΆ
GET μμ²μ ν΅ν΄ λ°μ μΏ ν€κ°μ Headerμ μ€μ ν΄μ POST νΈμΆμ μλνλ©΄ μ μμ μΌλ‘ 200 OKλ₯Ό λ겨λ°μ μ μμ΅λλ€.
(μ CookieCsrfTokenRepository ν΄λμ€μ HEADER_NAMEμ λν΄νΈ κ°μ΄ X-XSRF-TOKEN μ λλ€.)
λ§μ½ κΈ°μ‘΄μ Tokenκ³Ό λ€λ₯Έ κ°μ μ€μ νλ©΄ μμ κ°μ΄ 403 Forbidden μ€λ₯κ° λ°μν©λλ€.
π― μ CSRFμ λν΄ λΉνμ±νν κΉμ?
CSRF(Cross-Site Request Forgery)λ "μ¬μ΄νΈ κ° μμ²"μ΄ λ°μνκΈ° μ¬μ΄ μΉμ λν΄ μμ²ν λ νμν©λλ€.
μ΄λ¬ν μ ν리μΌμ΄μ μ λ³΄ν΅ ν νλ¦Ώ μμ§(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://itstory.tk/entry/CSRF-%EA%B3%B5%EA%B2%A9%EC%9D%B4%EB%9E%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-CSRF-%EB%B0%A9%EC%96%B4-%EB%B0%A9%EB%B2%95
- https://cheese10yun.github.io/spring-csrf/
- https://owasp.org/www-community/attacks/csrf
- https://rusyasoft.github.io/java/2019/02/15/spring-security-csrf-from-context/
- https://ncucu.me/120
- https://grooveshark.tistory.com/73
- https://www.cookiepro.com/knowledge/httponly-cookie/
- https://portswigger.net/web-security/csrf
- https://han.gl/8KTeB
λκΈ