6. Spring Security 악용 보호

p143

CorsPractice.java

package io.securitylecture.springsecuritylecture.config;

import org.springframework.context.annotation.Bean;
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.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
public class CorsPractice {

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        // CORS 설정 정의
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("http://localhost:3000"); // http://localhost:3000에서 오는 요청만 허용합니다. (프론트엔드 애플리케이션 URL로 변경 가능)
        configuration.addAllowedMethod("*"); // 모든 HTTP 메서드 허용
        configuration.addAllowedHeader("*"); // 모든 헤더 허용

        // URL에 CORS 설정 적용
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/", "/hello").permitAll() // 인증 없이 접근 허용
                        .anyRequest().authenticated()
                )
                .cors(cors -> cors.configurationSource(corsConfigurationSource())) // CORS 설정 적용
                //CORS 설정을 적용하여 corsConfigurationSource에서 정의한 CORS 정책을 필터 체인에 포함시킵니다.
                .csrf(csrf -> csrf.disable()); // CSRF 보호 비활성화 설정
                //REST API와 같이 상태가 없는 요청에서 필요하지 않으므로 간편하게 비활성화합니다.

        return http.build();
    }
}

cors_test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS Test</title>
</head>
<body>
<script>
    fetch("http://localhost:8080/hello")
        .then(response => response.text())
        .then(data => console.log(data))
        .catch(error => console.error("CORS Error:", error));
</script>
</body>
</html>

p151

CsrfConfig.java

package io.securitylecture.springsecuritylecture.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import static org.springframework.security.config.Customizer.withDefaults;

import java.util.Arrays;

@Configuration
public class CsrfConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated() // 모든 요청은 인증이 필요
                )
                .formLogin(withDefaults()) // 기본 로그인 설정
                .csrf(withDefaults()) // 기본 CSRF 보호 활성화
                .cors(cors -> cors.configurationSource(corsConfigurationSource())); // CORS 설정 추가

        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:63342")); // 허용할 출처 추가
        configuration.setAllowedMethods(Arrays.asList("GET", "POST")); // 허용할 HTTP 메서드
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type", "X-CSRF-TOKEN"));
        configuration.setAllowCredentials(true); // 자격 증명(쿠키 등) 허용 설정

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

crsf_test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CSRF Test</title>
</head>
<body>
<button onclick="submitForm()">Submit Form</button>

<script>
    async function submitForm() {
        const csrfResponse = await fetch("http://localhost:8080/csrf-token", { credentials: "include" });
        const csrfText = await csrfResponse.text();
        const csrfToken = csrfText.split(": ")[1]; // CSRF Token 추출

        const submitResponse = await fetch("http://localhost:8080/submit-csrf", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "X-CSRF-TOKEN": csrfToken
            },
            credentials: "include"
        });

        if (submitResponse.ok) {
            alert("Form submitted successfully with CSRF protection!");
        } else {
            alert("Failed to submit form. CSRF protection prevented the request.");
        }
    }
</script>
</body>
</html>

Last updated