๐Ÿงฉ CORS & CSRF ๋ณด์•ˆ ๊ธฐ์ดˆ - ์š”์ฒญ ๋ณดํ˜ธ ๊ธฐ์ดˆ: CSRF์™€ CORS ๊ตฌ์กฐ

okorionยท2025๋…„ 10์›” 6์ผ

๐Ÿ” Spring Security 6

๋ชฉ๋ก ๋ณด๊ธฐ
5/10

๐Ÿ“– ๋“ค์–ด๊ฐ€๋ฉฐ

์ด์ œ๋ถ€ํ„ฐ๋Š” ์›น ๋ณด์•ˆ์˜ ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ๋‘ ๊ฐ€์ง€ ์ฃผ์ œ, CORS์™€ CSRF๋ฅผ ๋‹ค๋ฃฌ๋‹ค.

์ด ๋‘˜์€ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋– ๋‚˜ ๋ชจ๋“  ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ทจ์•ฝ์ ์ด๋ฉฐ,

Spring Security์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•„์ˆ˜ ๊ฐœ๋…์ด๋‹ค.

์ด๋ฒˆ ํŒŒํŠธ์—์„œ๋Š”

  • ๋ธŒ๋ผ์šฐ์ € ๋‹จ์—์„œ์˜ ์š”์ฒญ ๋ณดํ˜ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜(CORS)
  • ์„œ๋ฒ„ ๋‹จ์—์„œ์˜ ์œ„์กฐ ๋ฐฉ์ง€ ํ† ํฐ ๊ฒ€์ฆ(CSRF)
  • ๊ทธ๋ฆฌ๊ณ  Spring Security ๋‚ด ์‹ค์ œ ๊ตฌํ˜„ ๊ตฌ์กฐ ๊นŒ์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณธ๋‹ค.

#1. CORS๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

โœ… Cross-Origin Resource Sharing (๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ )

๋ธŒ๋ผ์šฐ์ €๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜(origin) ๊ฐ„์˜ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฐจ๋‹จํ•œ๋‹ค.

์ด ์ •์ฑ…์ด ๋ฐ”๋กœ Same-Origin Policy(SOP)์ด๋ฉฐ,

CORS๋Š” SOP์˜ ์˜ˆ์™ธ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์—ด์–ด์ฃผ๋Š” ๋ช…์‹œ์  ํ—ˆ์šฉ ๊ทœ์น™์ด๋‹ค.

๐ŸŒ ์ถœ์ฒ˜(Origin)์˜ ๊ตฌ์„ฑ ์š”์†Œ

๊ตฌ์„ฑ ์š”์†Œ์˜ˆ์‹œ
ํ”„๋กœํ† ์ฝœhttp / https
๋„๋ฉ”์ธlocalhost / example.com
ํฌํŠธ๋ฒˆํ˜ธ8080 / 4200

์ฆ‰, ๋‹ค์Œ์€ ์„œ๋กœ ๋‹ค๋ฅธ ์ถœ์ฒ˜์ด๋‹ค.

http://localhost:8080   โ‰    http://localhost:4200

โš™๏ธ ๊ธฐ๋ณธ ๋™์ž‘

  • UI (http://localhost:4200) โ†’ Backend (http://localhost:8080) ์š”์ฒญ์ด ๋‹ค๋ฅธ ์ถœ์ฒ˜ ๊ฐ„์— ์ด๋ฃจ์–ด์ง€๋ฏ€๋กœ, ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด๋ฅผ โ€œ์˜์‹ฌ์Šค๋Ÿฌ์šด ์š”์ฒญโ€์œผ๋กœ ํŒ๋‹จํ•˜๊ณ  ์ฐจ๋‹จํ•œ๋‹ค.

์ด๋•Œ ๋ฐฑ์—”๋“œ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ—ค๋”๋ฅผ ์‘๋‹ต์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €๋Š” ์š”์ฒญ์„ ํ—ˆ์šฉํ•œ๋‹ค.

Access-Control-Allow-Origin: http://localhost:4200
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

๐Ÿงญ ์‹ค์ œ ์˜ˆ์‹œ (Mermaid ๊ตฌ์กฐ๋„)


๐Ÿงฑ Spring Security ๊ธฐ๋ฐ˜ CORS ์„ค์ •

http.cors(cors -> cors.configurationSource(request -> {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("http://localhost:4200"));
    config.setAllowedMethods(List.of("*"));
    config.setAllowedHeaders(List.of("*"));
    config.setAllowCredentials(true);
    config.setMaxAge(3600L);
    return config;
}));
  • preflight ์š”์ฒญ(OPTIONS) ์‹œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋จผ์ € ๋ฐฑ์—”๋“œ์˜ ์ •์ฑ…์„ ํ™•์ธ
  • ํ—ˆ์šฉ๋œ ๊ฒฝ์šฐ ์‹ค์ œ API ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰
  • ํ—ค๋”๋Š” Access-Control-Allow-* ๊ฐ’์œผ๋กœ ์ž๋™ ์ฃผ์ž…๋œ๋‹ค

๐Ÿงฉ ์‹ค๋ฌด์—์„œ ์ฃผ์˜ํ•  ์ 

๊ตฌ๋ถ„์„ค๋ช…
โŒ *(๋ณ„ํ‘œ) ๋‚จ์šฉ๊ฐœ๋ฐœ ํŽธ์˜๋Š” ๋†’์ง€๋งŒ, ๋ณด์•ˆ์ƒ ์œ„ํ—˜ํ•จ
โœ… ๊ตฌ์ฒด์  Origin ๋ช…์‹œํ•„์š” ์ถœ์ฒ˜๋งŒ ํ—ˆ์šฉ (localhost, ๋ฐฐํฌ ๋„๋ฉ”์ธ ๋“ฑ)
โœ… preflight ์บ์‹œmaxAge๋กœ ์„ฑ๋Šฅ ๊ฐœ์„  (๋ณดํ†ต 3600์ดˆ~24์‹œ๊ฐ„)

#2. CSRF๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

โœ… Cross-Site Request Forgery (์‚ฌ์ดํŠธ ๊ฐ„ ์š”์ฒญ ์œ„์กฐ)

CSRF๋Š” ํ•ด์ปค๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์„ธ์…˜์„ ์•…์šฉํ•˜์—ฌ,

์˜๋„ํ•˜์ง€ ์•Š์€ ์š”์ฒญ์„ ์„œ๋ฒ„์— ๋ณด๋‚ด๋Š” ๊ณต๊ฒฉ์ด๋‹ค.

์ฆ‰, ํ•ด์ปค๊ฐ€ ์‚ฌ์šฉ์ž ๋Œ€์‹  โ€œ์ •์ƒ ์š”์ฒญโ€์„ ๋ณด๋‚ด๋„๋ก ์†์ด๋Š” ํ–‰์œ„์ด๋‹ค.


๐Ÿ“– ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค

โžก ๋ธŒ๋ผ์šฐ์ €๋Š” ๋™์ผํ•œ ์„ธ์…˜ ์ฟ ํ‚ค(JSESSIONID)๋ฅผ ์ฒจ๋ถ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์—,

์„œ๋ฒ„๋Š” โ€œ์ •์ƒ์ ์ธ ์š”์ฒญโ€์œผ๋กœ ์˜ค์ธํ•˜๊ฒŒ ๋œ๋‹ค.


โš”๏ธ ๋ฐฉ์–ด ์›๋ฆฌ: CSRF ํ† ํฐ

Spring Security๋Š” CSRF Token์ด๋ผ๋Š” 1ํšŒ์šฉ ์ธ์ฆ ํ† ํฐ์„ ์ƒ์„ฑํ•˜์—ฌ

์š”์ฒญ ๊ฒ€์ฆ ์‹œ โ€œ์ฟ ํ‚ค ๊ฐ’ + ์š”์ฒญ ํ—ค๋” ๊ฐ’โ€์˜ ์ผ์น˜ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•œ๋‹ค.

๊ตฌ๋ถ„์„ค๋ช…
์ฟ ํ‚คXSRF-TOKEN โ€“ ๋ฐฑ์—”๋“œ๊ฐ€ ์ƒ์„ฑํ•ด ํ”„๋ก ํŠธ์— ์ „๋‹ฌ
์š”์ฒญ ํ—ค๋”X-XSRF-TOKEN โ€“ ํ”„๋ก ํŠธ์—์„œ ์ฟ ํ‚ค ๊ฐ’์„ ์ฝ์–ด ํ—ค๋”์— ์‚ฝ์ž…
๊ฒ€์ฆ ๋ฐฉ์‹๋‘ ๊ฐ’์ด ์ผ์น˜ํ•˜๋ฉด ์š”์ฒญ ํ—ˆ์šฉ, ๋ถˆ์ผ์น˜ ์‹œ 403 ๋ฐ˜ํ™˜

๐Ÿ’ก ๊ฒ€์ฆ ํ”Œ๋กœ์šฐ


๐Ÿง  Spring Security ๋‚ด๋ถ€ ๋™์ž‘ ์š”์•ฝ

๊ตฌ์„ฑ ์š”์†Œ์—ญํ• 
CsrfToken ์ธํ„ฐํŽ˜์ด์Šคํ† ํฐ์˜ ๊ตฌ์กฐ ์ •์˜
CookieCsrfTokenRepository์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ์ €์žฅ์†Œ ๊ตฌํ˜„
CsrfFilter์š”์ฒญ ์‹œ ํ† ํฐ ๊ฒ€์ฆ ์ˆ˜ํ–‰
OncePerRequestFilter์š”์ฒญ๋‹น 1ํšŒ ํ•„ํ„ฐ ์‹คํ–‰
CsrfTokenRequestAttributeHandler์š”์ฒญ ํ—ค๋”์˜ ํ† ํฐ ๊ฐ’์„ ์ถ”์ถœ

๐Ÿงฑ Spring Security ์„ค์ • ์˜ˆ์‹œ

http.csrf(csrf -> csrf
    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
    .ignoringRequestMatchers("/register", "/contact")
);

http.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class);
  • withHttpOnlyFalse() : JS๊ฐ€ ์ฟ ํ‚ค ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •
  • CsrfCookieFilter : ํ† ํฐ์„ ์ง€์—ฐ ์ƒ์„ฑ ํ›„ ์ฟ ํ‚ค ๋ Œ๋”๋ง
  • /register, /contact : ๊ณต์šฉ API๋Š” CSRF ๋ณดํ˜ธ ์ œ์™ธ

#3. Angular/React UI ์ธก ์ฒ˜๋ฆฌ ํ๋ฆ„

// ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ์ฟ ํ‚ค ์ฝ๊ธฐ
const xsrf = getCookie('XSRF-TOKEN');
sessionStorage.setItem('XSRF-TOKEN', xsrf!);

// ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ํ—ค๋” ์ถ”๊ฐ€
if (xsrf) {
  req = req.clone({
    headers: req.headers.set('X-XSRF-TOKEN', xsrf),
    withCredentials: true
  });
}
  • getCookie() โ†’ ์ฟ ํ‚ค ๊ฐ’ ์ถ”์ถœ
  • sessionStorage์— ๋ณด๊ด€
  • ์ดํ›„ ๋ชจ๋“  API ์š”์ฒญ ์‹œ ํ—ค๋”์— ์ž๋™ ํฌํ•จ

#4. BCryptPasswordEncoder ๋””๋ฒ„๊น… ์˜ˆ์‹œ

CSRF ๋ณดํ˜ธ์™€ ํ•จ๊ป˜ ์ž์ฃผ ์“ฐ์ด๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ๋„ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž.

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String raw = "EazyBytes@12345";
String encoded = encoder.encode(raw);

System.out.println(encoded);
System.out.println(encoder.matches(raw, encoded)); // true
๊ตฌ๋ถ„๋‚ด์šฉ
์•Œ๊ณ ๋ฆฌ์ฆ˜bcrypt (adaptive hash, salt ์ž๋™ ์ถ”๊ฐ€)
๊ฐ•์ ๋А๋ฆฐ ๊ณ„์‚ฐ์œผ๋กœ ๋ฌด์ฐจ๋ณ„ ๋Œ€์ž… ๊ณต๊ฒฉ ๋ฐฉ์–ด
Salt ์ €์žฅํ•ด์‹œ ๋‚ด๋ถ€์— ํฌํ•จ๋จ (๋น„๊ต ์‹œ ์ž๋™ ์ถ”์ถœ)


#5. ์š”์•ฝ ๋น„๊ตํ‘œ

ํ•ญ๋ชฉCORSCSRF
๊ณต๊ฒฉ ๋Œ€์ƒ๋ธŒ๋ผ์šฐ์ € ์ •์ฑ…์„œ๋ฒ„ ์š”์ฒญ ์œ„์กฐ
์ฃผ์ฒด๋ธŒ๋ผ์šฐ์ € ๋ณด์•ˆ ์ •์ฑ…ํ•ด์ปค์˜ ์œ„์กฐ ์š”์ฒญ
์ฃผ์š” ์›์ธ๋‹ค๋ฅธ Origin ๊ฐ„ ์š”์ฒญ๋™์ผ ์ฟ ํ‚ค์˜ ์ž๋™ ์ฒจ๋ถ€
๋ฐฉ์–ด ๋ฐฉ์‹Access-Control-Allow-OriginCSRF Token (์ฟ ํ‚ค + ํ—ค๋”)
Spring Security ์„ค์ •http.cors()http.csrf()
ํ”„๋ก ํŠธ ์ฐธ์—ฌ ํ•„์š”โŒ ์—†์Œโœ… ์ฟ ํ‚ค ์ฝ์–ด ํ—ค๋” ์ „์†ก

#6. ์ •๋ฆฌ

CORS๋Š” โ€œ๋ˆ„๊ฐ€ ๋‚˜์—๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€โ€,

CSRF๋Š” โ€œ๋ˆ„๊ฐ€ ๋‚˜๋ฅผ ๋Œ€์‹ ํ•ด์„œ ํ–‰๋™ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€โ€์— ๋Œ€ํ•œ ๋ฌธ์ œ๋‹ค.

  • CORS = ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ง€์ผœ์ฃผ๋Š” ๋ฐฉํ™”๋ฒฝ
  • CSRF = ์„œ๋ฒ„๊ฐ€ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉํŒจ๋ง‰์ด

์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ๋‘ ๊ฐ€์ง€๋ฅผ ๋™์‹œ์— ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

Spring Security๋Š” ์ด ๋‘ ๊ณ„์ธต์„ ๋ชจ๋‘ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š”

๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ณด์•ˆ ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ํ•˜๋‚˜๋‹ค.

profile
Tech Archive 2026

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