์ค๋์ ์น ์ ํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์ ํ๋ฉด์ ๋ฌธ๋ ์๊ฐ์ด ๋ค์์ต๋๋ค.
"๋ด๊ฐ ๋ง๋ ์๋น์ค๋ ์์ ํ๊ฐ?"
์ด ์ง๋ฌธ์ ์์์ผ๋ก ๋ณด์ ๊ด๋ จ ๋ด์ฉ์ ์ฐพ์๋ณด๋ค๊ฐ CSRF(Cross-Site Request Forgery)๋ผ๋ ์ฉ์ด๋ฅผ ๋ค์ ํ๋ฒ ๋ ์ฌ๋ฆฌ๊ฒ ๋์์ต๋๋ค.
์ฌ์ค CSRF๋ ์ด๋ณด ๊ฐ๋ฐ์ ๋๋ ์ ์๋ฟ์ง ์์ง๋ง, ์ค๋ฌด์์๋ ๋งค์ฐ ์ค์ํ ๋ณด์ ์ด์์
๋๋ค. ๊ทธ๋์ ์ด๋ฒ ๊ธฐํ์ CSRF๊ฐ ๋ฌด์์ธ์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ๋ฐฉ์ดํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์ ๋ฆฌํด๋ณด๊ณ ์ ํฉ๋๋ค.
CSRF(Cross-Site Request Forgery)๋ ์ฌ์ฉ์๊ฐ ์์ง ๋ชปํ๋ ์ฌ์ด์, ๊ณต๊ฒฉ์๊ฐ ์ฌ์ฉ์์ ๊ถํ์ผ๋ก ํน์ ์์
์ ์คํํ๋๋ก ๋ง๋๋ ๊ณต๊ฒฉ ๊ธฐ๋ฒ์
๋๋ค.
์ฆ, ์ฌ์ฉ์๊ฐ ์๋ํ์ง ์์ ์์ฒญ์ ๊ฐ์ ๋ก ์คํํ๋ ๊ฒ์ด์ฃ . ์ด๋ฅผ ํตํด ๊ณต๊ฒฉ์๋ ๊ธ์ ๊ฑฐ๋, ๊ณ์ ๋ณ๊ฒฝ, ๋ฏผ๊ฐ ์ ๋ณด ๋
ธ์ถ ๋ฑ์ ์ ๋ฐํ ์ ์์ต๋๋ค.
๊ณต๊ฒฉ์๊ฐ ์
์์ ์ธ ์์ฒญ์ ์์ฑํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ๊ณต๊ฒฉ์๋ ์ฌ์ฉ์์ ์ํ ๊ณ์ข์์ ๋์ ์ด์ฒดํ๋ ์์ฒญ์ ๋ง๋ญ๋๋ค.
์ฌ์ฉ์๋ฅผ ์ ์ธํฉ๋๋ค.
๊ณต๊ฒฉ์๋ ์ด๋ฉ์ผ, ๋ฉ์์ง, ๊ด๊ณ ๋ฑ์ ํตํด ํผํด์๊ฐ ๋งํฌ๋ฅผ ํด๋ฆญํ๊ฑฐ๋ ์
์ฑ ์ฝ๋๋ฅผ ์คํํ๊ฒ ๋ง๋ญ๋๋ค.
์ฌ์ฉ์์ ๊ถํ์ผ๋ก ์คํ๋ฉ๋๋ค.
ํผํด์๊ฐ ํน์ ์น์ฌ์ดํธ์ ๋ก๊ทธ์ธ๋ ์ํ๋ผ๋ฉด, ๊ณต๊ฒฉ์์ ์์ฒญ์ ํผํด์์ ๊ถํ์ผ๋ก ์คํ๋ฉ๋๋ค.
์๋๋ ๊ณต๊ฒฉ์๊ฐ ๋ง๋ ์ ์์ ์ธ HTML ์ฝ๋์ ๋๋ค. ์ด ์ฝ๋๋ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ๋ ์ํ์์ ์คํ๋ ๊ฒฝ์ฐ, ํผํด์์ ์ํ ๊ณ์ข์์ ๊ณต๊ฒฉ์์ ๊ณ์ข๋ก ๋์ ์ด์ฒดํฉ๋๋ค.
<img src="http://bank.com/transfer?account=attacker&amount=1000" width="0" height="0" />
bank.com
์ ๋ก๊ทธ์ธ๋์ด ์ฟ ํค์ ์ธ์ฆ ์ ๋ณด๊ฐ ์ ์ฅ๋์ด ์์ต๋๋ค.src
URL์ ์์ฒญํฉ๋๋ค.CSRF ๋ฐฉ์ด์์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ CSRF ํ ํฐ์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
๊ฐ ์์ฒญ๋ง๋ค ์๋ฒ์์ ์์ฑ๋ ๊ณ ์ ํ ํฐ์ ํฌํจ์ํค๊ณ , ์ด๋ฅผ ์๋ฒ์์ ๊ฒ์ฆํฉ๋๋ค.
// ์๋ฒ์์ CSRF ํ ํฐ ์์ฑ
String csrfToken = UUID.randomUUID().toString();
session.setAttribute("CSRF_TOKEN", csrfToken);
// HTML ํผ์ CSRF ํ ํฐ ํฌํจ
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="${csrfToken}" />
<input type="text" name="account" placeholder="Recipient Account" />
<input type="number" name="amount" placeholder="Amount" />
<button type="submit">Transfer</button>
</form>
// ์๋ฒ์์ CSRF ํ ํฐ ๊ฒ์ฆ
String tokenFromRequest = request.getParameter("csrf_token");
String tokenFromSession = session.getAttribute("CSRF_TOKEN");
if (!tokenFromSession.equals(tokenFromRequest)) {
throw new SecurityException("Invalid CSRF token");
}
[์ด๋ฏธ์ง ์ฝ์ ]
CSRF ํ ํฐ์ ์ค๋ช ํ๋ ๋ค์ด์ด๊ทธ๋จ (๊ฒ์ ํค์๋: "CSRF token flow diagram")
์ฟ ํค์ SameSite
์์ฑ์ ์ถ๊ฐํ๋ฉด, ํฌ๋ก์ค ์ฌ์ดํธ ์์ฒญ ์ ์ฟ ํค๊ฐ ์ ์ก๋์ง ์๋๋ก ํ ์ ์์ต๋๋ค.
์ด ์ค์ ์ CSRF๋ฅผ ๋ฐฉ์ดํ๋ ๊ฐ๋จํ๋ฉด์๋ ๊ฐ๋ ฅํ ๋ฐฉ๋ฒ ์ค ํ๋์
๋๋ค.
Set-Cookie: sessionId=abc123; SameSite=Strict
์์ฒญ ํค๋์ Referer
๊ฐ์ ํ์ธํ์ฌ ์์ฒญ์ด ์ ๋ขฐํ ์ ์๋ ์ถ์ฒ์์ ์๋์ง๋ฅผ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
๋ค๋ง, ์ผ๋ถ ๋ธ๋ผ์ฐ์ ๋ ๋คํธ์ํฌ ํ๊ฒฝ์์๋ Referer
๊ฐ ๋น์์ง ์ ์์ผ๋, ๋ณด์กฐ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
์ค์ํ ์์ฒญ(์: ๊ณ์ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ, ๊ธ์ ์ด์ฒด ๋ฑ)์๋ CAPTCHA๋ฅผ ์ฌ์ฉํ์ฌ, ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ์์ฒญ์ ์๋ํ ๊ฒ์์ ํ์ธํ ์ ์์ต๋๋ค.
CSRF์ XSS(Cross-Site Scripting)๋ ์ข ์ข ํผ๋๋์ง๋ง, ๊ทธ ์๋ ๋ฐฉ์์ด ๋ค๋ฆ ๋๋ค:
๊ณต๊ฒฉ ์ ํ | ์ฃผ์ ๋ชฉํ | ์ฌ์ฉ์ ๊ฐ์ ํ์์ฑ |
---|---|---|
CSRF | ๊ถํ ํ์ทจ ๋ฐ ์์ฒญ ์กฐ์ | ํ์ํจ |
XSS | ์ ์ฑ ์คํฌ๋ฆฝํธ ์คํ | ํ์ ์์ |
[์ด๋ฏธ์ง ์ฝ์ ]
CSRF์ XSS์ ์ฐจ์ด๋ฅผ ์ค๋ช ํ๋ ํ๋ ๋ค์ด์ด๊ทธ๋จ (๊ฒ์ ํค์๋: "CSRF vs XSS")
CSRF๋ ๊ฐ๋จํ ๊ณต๊ฒฉ์ฒ๋ผ ๋ณด์ด์ง๋ง, ๊ทธ ์ํฅ์ ์ฌ๊ฐํ ์ ์์ต๋๋ค.
์ ์ ํ ๋ฐฉ์ด ์ ๋ต(์: CSRF ํ ํฐ, SameSite ์ฟ ํค ๋ฑ)์ ์ ์ฉํ๋ฉด ์ด๋ฌํ ๊ณต๊ฒฉ์ ํจ๊ณผ์ ์ผ๋ก ๋ฐฉ์งํ ์ ์์ต๋๋ค.
์ฌ๋ฌ๋ถ๋ ํ๋ก์ ํธ์์ CSRF ๋ฐฉ์ด๋ฅผ ์ฒ ์ ํ ์ ์ฉํด, ๋์ฑ ์์ ํ ์น ์๋น์ค๋ฅผ ๋ง๋ค์ด ๋ณด์ธ์! ๐