๐ 1. CSRF(Cross-Site Request Forgery)๋?
- ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ๋ ์ํ์์ ์ ์ฑ ์ฌ์ดํธ๋ฅผ ํตํด ์์น ์๋ ์์ฒญ์ ๋ณด๋ด๋๋ก ์ ๋ํ๋ ๊ณต๊ฒฉ ๋ฐฉ์
- ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ์ํ ์น์ฌ์ดํธ์ ๋ก๊ทธ์ธํ ์ํ์์ ์ ์ฑ ์คํฌ๋ฆฝํธ๊ฐ ๊ณ์ข์ด์ฒด ์์ฒญ์ ๋ณด๋ด๋ฉด ๊ณต๊ฒฉ์ด ์ฑ๊ณตํ ์ ์์
โ 1-1. Spring Security์์ CSRF ์ค์ ๋ฐฉ๋ฒ
๐น CSRF ๋ฐฉ์ด ํ์ฑํ (๊ธฐ๋ณธ๊ฐ)
- Spring Security์์๋ CSRF ๋ณดํธ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋จ
- ํผ ๊ธฐ๋ฐ ๋ก๊ทธ์ธ(formLogin())์ ์ฌ์ฉํ ๋ CSRF ๋ฐฉ์ด๊ฐ ํ์ํจ
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.enable()) // ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.build();
}
๐น CSRF ๋นํ์ฑํ (API ์๋ฒ์์๋ ์ฃผ๋ก ๋นํ์ฑํ)
- REST API์์๋ ์ผ๋ฐ์ ์ผ๋ก CSRF ๋ณดํธ๊ฐ ํ์ ์์ → API ํธ์ถ ์ ์ธ์ ์ ์ฌ์ฉํ์ง ์๊ณ JWT ๋ฑ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ๋ฌธ
- POST, PUT, DELETE ์์ฒญ์ด ์คํจํ๋ฉด 403 Forbidden ์ค๋ฅ ๋ฐ์ํ ์ ์์ → CSRF ๋นํ์ฑํ ํ์
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable()) // API ์๋ฒ๋ผ๋ฉด ๋ณดํต ๋นํ์ฑํ
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
)
.build();
}
โ CSRF ์ค์ ์ ํ์ง ์์์ ๋ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ:
- 403 Forbidden: CSRF ํ ํฐ์ด ํ์ํ์ง๋ง ์ ๊ณต๋์ง ์์๊ฑฐ๋ ์๋ชป๋จ
๐ 2. CORS(Cross-Origin Resource Sharing)๋?
- ํ ๋๋ฉ์ธ์์ ๋ค๋ฅธ ๋๋ฉ์ธ์ผ๋ก ์์ฒญ์ ๋ณด๋ผ ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์ฐจ๋จํ๋ ๋ณด์ ์ ์ฑ
- ์๋ฅผ ๋ค์ด, http://localhost:3000์์ http://api.example.com๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด CORS ์ ์ฑ ์๋ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์
โ 2-1. Spring Security์์ CORS ์ค์ ๋ฐฉ๋ฒ
๐น CORS ํ์ฉ ์ค์ (Spring MVC - Web)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.cors(withDefaults()) // CORS ์ค์ ์ ์ฉ
.csrf(csrf -> csrf.disable()) // ๋ณดํต API ์๋ฒ์์๋ CSRF ๋นํ์ฑํ
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
)
.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000")); // ํ์ฉํ ์ถ์ฒ
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
๐น CORS ํ์ฉ ์ค์ (Spring WebFlux)
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // CORS ์ค์ ์ ์ฉ
.csrf(csrf -> csrf.disable())
.authorizeExchange(exchange -> exchange
.anyExchange().permitAll()
)
.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
โ CORS ์ค์ ์ ํ์ง ์์์ ๋ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ:
- CORS policy ์ค๋ฅ (Access to fetch at 'http://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy)
- Preflight ์์ฒญ ์คํจ (OPTIONS ์์ฒญ์ด ์ฐจ๋จ๋จ)
๐ฅ3. CSRF vs CORS ๋น๊ต ์ ๋ฆฌ
๊ฐ๋ | CSRF (Cross-Site Request Forgery) | CORS (Cross-Origin Resource Sharing) |
๋ฌธ์ ๋ฐ์ ์์ธ | ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ ์ธ์ ์ ์ ์ฉํ ์์ฒญ ์์กฐ | ๋ค๋ฅธ ๋๋ฉ์ธ์์ API ์์ฒญ์ ๋ณด๋ผ ๋ ๋ธ๋ผ์ฐ์ ์ฐจ๋จ |
๋ณดํธ ๋์ | ์๋ฒ (๋ฐฑ์๋) | ํด๋ผ์ด์ธํธ (๋ธ๋ผ์ฐ์ ) |
๊ณต๊ฒฉ ์์ | ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ๋ ์ํ์์ ์ ์ฑ ์์ฒญ์ด ์ ์ก๋จ | localhost:3000์์ api.example.com์ผ๋ก ์์ฒญ ์ ์ฐจ๋จ๋จ |
Spring Security ๊ธฐ๋ณธ ์ค์ | โ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋จ | โ ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ๋จ |
๋นํ์ฑํ ์ ๋ฐ์ํ ๋ฌธ์ | 403 Forbidden ์ค๋ฅ | ๋ธ๋ผ์ฐ์ ์์ CORS policy ์ค๋ฅ ๋ฐ์ |
์ค์ ๋ฐฉ๋ฒ | csrf().disable() | cors().configurationSource() |
๐ ์ ๋ฆฌ
โ
CSRF๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋จ → API ์๋ฒ์์๋ ๋นํ์ฑํํ๋ ๊ฒ์ด ์ผ๋ฐ์
โ
CORS๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ๋จ → ํด๋ผ์ด์ธํธ์์ API๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ ๋ฐ๋์ ์ค์ ํ์
'Backend > spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[error] getOutputStream() has already been called for this response (0) | 2025.02.23 |
---|---|
[JAVA] ์ฟ ํค(Cookie) ์ค์ ๋ฐฉ๋ฒ (0) | 2025.02.23 |
[error] conversionServicePostProcessor Bean ์ค๋ณต ์ค๋ฅ (0) | 2025.02.23 |
[Spring Security] Web vs WebFlux Spring Security ์ค์ (0) | 2025.02.23 |
web vs webflux (0) | 2025.02.23 |