๐Ÿ“Œ Spring Security - CSRF & ์ธ์ฆ ์„ค์ • ์‹ค์Šต

My Pale Blue Dotยท2025๋…„ 4์›” 30์ผ
0

SPRING

๋ชฉ๋ก ๋ณด๊ธฐ
31/36
post-thumbnail

๐Ÿ“… ๋‚ ์งœ

2025-04-30


๐Ÿ“ ํ•™์Šต ๋‚ด์šฉ

1๏ธโƒฃ Spring Security ์ „์ฒด ์„ค์ • ํ๋ฆ„

โœ… SecurityConfig ๊ตฌ์„ฑ ์š”์•ฝ

@Configuration
@EnableWebSecurity  // Spring Security ํ™œ์„ฑํ™”
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // CSRF ๋น„ํ™œ์„ฑํ™” (ํ…Œ์ŠคํŠธ์šฉ)
        http.csrf().disable();

        // ๊ถŒํ•œ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ์„ค์ •
        http.authorizeRequests()
            .antMatchers("/", "/join").permitAll()
            .antMatchers("/user").hasRole("USER")
            .antMatchers("/manager").hasRole("MANAGER")
            .antMatchers("/admin").hasRole("ADMIN")
            .anyRequest().authenticated(); // ๊ธฐํƒ€ ์š”์ฒญ์€ ์ธ์ฆ ํ•„์š”

        // ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ํ—ˆ์šฉ
        http.formLogin().permitAll();
        http.logout().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user").password("{noop}1234").roles("USER")
            .and()
            .withUser("manager").password("{noop}1234").roles("MANAGER")
            .and()
            .withUser("admin").password("{noop}1234").roles("ADMIN");
    }
}

๐Ÿ”’ {noop}์€ ๋น„์•”ํ˜ธํ™”๋œ ํ‰๋ฌธ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •์ด๋ฏ€๋กœ, ์‹ค๋ฌด์—์„œ๋Š” ํ•ด์‹ฑ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.


2๏ธโƒฃ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” ์ ์šฉ (BCrypt)

โœ… PasswordEncoder Bean ๋“ฑ๋ก ๋ฐ ์—ฐ๋™

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

@Autowired
private PasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user")
        .password(passwordEncoder.encode("1234"))  // ํ•ด์‹ฑ๋œ ์•”ํ˜ธ ์ €์žฅ
        .roles("USER");
}

๐Ÿ”น ์˜ˆ์‹œ ๋น„๊ต

์ž…๋ ฅ ๋น„๋ฐ€๋ฒˆํ˜ธ๋‚ด๋ถ€ ์ €์žฅ๊ฐ’
1234$2a$10$92Ex... (BCrypt ํ•ด์‹œ)

3๏ธโƒฃ CSRF(Cross-Site Request Forgery) ๊ฐœ๋… ์ •๋ฆฌ

  • ์ •์˜: ์‚ฌ์šฉ์ž๊ฐ€ ์˜๋„ํ•˜์ง€ ์•Š์€ ์š”์ฒญ์„ ํŠน์ • ์‚ฌ์ดํŠธ๋กœ ๋ณด๋‚ด๊ฒŒ ํ•˜๋Š” ๊ณต๊ฒฉ
  • ๋ชฉ์ : ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋„์šฉํ•ด, ์˜๋„ํ•˜์ง€ ์•Š์€ '์ •๋ณด ๋ณ€๊ฒฝ, ์‚ญ์ œ'๋ฅผ ์ˆ˜ํ–‰
  • ์‹ค์ œ ์‚ฌ๋ก€: ์˜ฅ์…˜ ๊ฐœ์ธ์ •๋ณด ์œ ์ถœ ์‚ฌ๊ฑด์— ์‚ฌ์šฉ๋จ

โœ… ๊ณต๊ฒฉ ํ๋ฆ„ ์˜ˆ์‹œ (ํ…์ŠคํŠธ ๋„์‹)

[๊ณต๊ฒฉ ์‚ฌ์ดํŠธ] โ†’ (์‚ฌ์šฉ์ž ํด๋ฆญ) โ†’ [์ •์ƒ ์‚ฌ์ดํŠธ ์š”์ฒญ ์ „์†ก] โ†’ ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋กœ ์ˆ˜ํ–‰

4๏ธโƒฃ Spring Security์˜ CSRF ๋ณดํ˜ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜

โœ… ๊ธฐ๋ณธ ๋ฐฉ์‹: <input type="hidden"> ํ•„๋“œ ์ž๋™ ์‚ฝ์ž…

<input name="_csrf" type="hidden" value="ํ† ํฐ๊ฐ’">

ํผ ์ œ์ถœ ์‹œ ์ž๋™์œผ๋กœ ์„œ๋ฒ„๋กœ ์ „์†ก๋˜์–ด ๊ฒ€์ฆ๋จ

โœ… JSP ์ง์ ‘ ์‚ฝ์ž… ์˜ˆ์ œ

<form action="${pageContext.request.contextPath}/login" method="post">
    USERNAME : <input name="username"/><br>
    PASSWORD : <input name="password" type="password"/><br>
    <input type="hidden" name="_csrf" value="${_csrf.token}"/>
    <button>๋กœ๊ทธ์ธ</button>
</form>

GET /logout์€ CSRF ๋ณดํ˜ธ ํ™œ์„ฑํ™” ์‹œ ์ฐจ๋‹จ๋จ โ†’ POST ๋ฐฉ์‹ ์‚ฌ์šฉ ํ•„์š”


5๏ธโƒฃ CSRF ํ† ํฐ์„ ์ฟ ํ‚ค๋กœ ์ „์†กํ•˜๋Š” ๋ฐฉ๋ฒ•

โœ… ์„ค์ • ์ฝ”๋“œ

http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
  • XSRF-TOKEN์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ์ฟ ํ‚ค์— ์ €์žฅ๋จ
  • HttpOnly ๋น„ํ™œ์„ฑํ™”๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ† ํฐ ์ ‘๊ทผ ๊ฐ€๋Šฅ

โœ… ์‹ค์ œ ์ฟ ํ‚ค ์˜ˆ์‹œ

์ฟ ํ‚ค ์ด๋ฆ„์„ค๋ช…
JSESSIONID์„ธ์…˜ ์‹๋ณ„์ž
XSRF-TOKENCSRF ๋ณดํ˜ธ์šฉ ํ† ํฐ

๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ


6๏ธโƒฃ CSRF ํ† ํฐ์„ ํด๋ผ์ด์–ธํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ

โœ… JavaScript / fetch ์š”์ฒญ ์‹œ

fetch("/secure-api", {
  method: "POST",
  headers: {
    "X-XSRF-TOKEN": getCookie("XSRF-TOKEN"),
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ ... })
});

getCookie๋Š” ์ฟ ํ‚ค์—์„œ ํ† ํฐ์„ ๊บผ๋‚ด๋Š” ์œ ํ‹ธ ํ•จ์ˆ˜ ํ•„์š”

โœ… Postman ์‚ฌ์šฉ ์‹œ

  • Headers ํƒญ์— ๋‹ค์Œ ํ•ญ๋ชฉ ์ถ”๊ฐ€:
    • Key: X-XSRF-TOKEN
    • Value: (๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋ณต์‚ฌํ•œ ํ† ํฐ ๊ฐ’)

๐Ÿ”ฅ ์ •๋ฆฌ

  • Spring Security๋Š” CSRF ๋ณดํ˜ธ ๊ธฐ๋Šฅ์„ ๊ธฐ๋ณธ ์ œ๊ณตํ•˜๋ฉฐ, ํด๋ผ์ด์–ธํŠธ์—์„œ ํ† ํฐ์„ ์ ์ ˆํžˆ ์ œ์ถœํ•ด์•ผ ์ •์ƒ ์š”์ฒญ์œผ๋กœ ์ฒ˜๋ฆฌ๋จ
  • ํ…Œ์ŠคํŠธ๋‚˜ ๋น„๋™๊ธฐ ์š”์ฒญ ์‹œ์—๋Š” CookieCsrfTokenRepository๋กœ ์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ์ „์†ก์„ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜, .disable() ์„ค์ •์„ ํ†ตํ•ด ์ž„์‹œ ๋น„ํ™œ์„ฑํ™” ๊ฐ€๋Šฅ
  • ๋น„๋ฐ€๋ฒˆํ˜ธ ์ €์žฅ์€ ๋ฐ˜๋“œ์‹œ BCryptPasswordEncoder์™€ ๊ฐ™์€ ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ, {noop}์€ ํ•™์Šต ๋ชฉ์ ์—๋งŒ ์‚ฌ์šฉ
  • JSP ๋˜๋Š” SPA ํ™˜๊ฒฝ์—์„œ CSRF ๋ณดํ˜ธ๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ณ  ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•จ

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ


profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€