๐กSpring security๋ฅผ ์ค์ ํ๋ฉด์, ๋ ์น ๋ณด์์ ๋ํด ํ์ตํ๋ฉด์ ๊ณ์ ๊น๋จน๊ณ ๊ตฌ๊ธ๋งํ๊ฒ ๋์ด ํ์คํ๊ฒ ์ ๋ฆฌํด ๋๊ณ ์ ํฌ์คํ ์ ์์ฑํ๋ค.
์
์์ ์ธ ์ฌ์ฉ์๊ฐ ๊ณต๊ฒฉํ๋ ค๋ ์ฌ์ดํธ์ ์คํฌ๋ฆฝํธ๋ฅผ ๋ฃ๋ ๊ธฐ๋ฒ, html์ <script/>ํ๊ทธ๊ฐ ์ฌ์ฉ๋๋ค.
XSS ๊ณต๊ฒฉ์ Reflected XSS์ Stored XSS(๋๋ Persist XSS)๋ก ๋๋๋ค.
ํ ํฐ ๋ฑ์ ์ ์ฅํ ๋ ๋ก์ปฌ ์คํ ๋ฆฌ์ง ์ฌ์ฉ์ javascript์์ ์ง์ ์์ธ์ค ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ XSS์ ์ทจ์ฝํ๋ค.
XSS ์ทจ์ฝ์ ์ ํด๋ผ์ด์ธํธ ์ธก์ ์ํด ๋ฐ์ํ๋ค.
์ผ๋ฐ ์ ์ ์ ์์ฒญ(request)์ ์ ์ฑ์ฝ๋ ์ฌ์
๊ณต๊ฒฉ ์๋๋ฆฌ์ค
์๋ฒ์ธก(๊ฒ์ํ)์ ์ ์ฑ์ฝ๋ ์ฌ์
๊ณต๊ฒฉ ์๋๋ฆฌ์ค
<script>์ ์
๋ ฅ์ ์ฐจ๋จํ๋ฉด ๋๋ค. ํน์๋ฌธ์๋ฅผ ์นํ, ์ ๊ฑฐํ๊ฑฐ๋ ์ฌ์ฉ์ ์
๋ ฅ์ ๊ฒ์ฆํ๋ ๊ฒ์ด ํด๊ฒฐ์ฑ
์ด ๋ ์ ์๋ค.
์ฌ์ดํธ๊ฐ ์์ฒญ ์์กฐ์ ์ฝ์๋ก, ์ฌ์ฉ์๊ฐ ์ ๋ขฐํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํด ์ฌ์ฉ์์ ๊ถํ์ ๋์ฉํ์ฌ ์์น ์๋ ํ๋์ ์ํํ๊ฒ ๋ง๋๋ ๊ณต๊ฒฉ์ด๋ค. ๊ณต๊ฒฉ์๋ ์ฌ์ฉ์๊ฐ ์ด๋ฏธ ์ธ์ฆ๋ ์ธ์ ์ ๊ฐ์ง๊ณ ์๋ค๋ ์ ์ ์ ์ฉํ์ฌ, ์ฌ์ฉ์๊ฐ ์๋ํ์ง ์์ ์์ฒญ์ ์ํํ๋๋ก ํ๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ๋ ์ํ์์ ์ ์ฑ ์ฌ์ดํธ๋ฅผ ๋ฐฉ๋ฌธํ๋ฉด, ๊ทธ ์ฌ์ดํธ์์ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฒญ์ ๋ณด๋ผ ์ ์๋ค.
CSRF ์ทจ์ฝ์ ์ ์ฃผ๋ก ์๋ฒ ์ธก์ ์ํด ๋ฐ์ํ๋ค.
๊ณต๊ฒฉ์๊ฐ CSRF ๊ณต๊ฒฉ์ ํ๊ธฐ ์ํด์ ์๋ ์ ์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผํ๋ค.
๊ณต๊ฒฉ์์ ์ ์ฑ ์น ์ฌ์ดํธ
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Attacker Site</title>
</head>
<body>
<div id="wrap">
<img src="http://vulnerable-site/change?name=NameChangedByImageTag" style="width: 0px; height: 0px;"/>
</div>
</body>
</html>
๊ณต๊ฒฉ์๋ ์์ ๊ฐ์ ์
์ฑ ์ฌ์ดํธ๋ฅผ ๋ง๋ค์ด ํ๊ฒ์๊ฒ ์ ๋ฌํ๋ค. ํ๊ฒ์ด ํด๋น ์ฌ์ดํธ๋ฅผ ๋ฐฉ๋ฌธํ๋ฉด, imgํ๊ทธ(๋๋ aํ๊ทธ)์ ์๋ฒ๋ก GET์์ฒญ์ ๋ณด๋ธ๋ค.
width ๋ฐ height ๊ฐ์ด 0์ด๋ฏ๋ก ๋ธ๋ผ์ฐ์ ์ ๋ณด์ด์ง ์๋๋ค.
๊ณต๊ฒฉ์์ ์ ์ฑ ์น ์ฌ์ดํธ
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Attacker Site</title>
</head>
<body>
<div id="wrap">
<form action="http://vulnerable-site/change" method="POST">
<input type="hidden" id="memberName" name="memberName" value="NameChangedByFormSubmit"/>
</form>
<script>
setTimeout(function () {
document.forms[0].submit();
}, 1000);
</script>
</div>
</body>
</html>
<form/> ํ๊ทธ์ hidden ํ์
์ <input /> ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋ค. JavaScript๋ฅผ ์ด์ฉํด ํ์ด์ง ๋ ๋๋ง์ด ์ํ๋๊ณ 1์ด ๋ค ํผ ์ ์ก์ ์๋ํ๋ค.
String referer = request.getHeader("Referer");
String host = request.getHeader("host");
CSRF ํ ํฐ ๊ฒ์ฆ
ํด๋ผ์ด์ธํธ ๋ณ๋ก ๋ค๋ฅธ ํ ํฐ์ ์์ฑํ์ฌ hiddenํ์
inputํ๊ทธ๊ฐ ํฌํจ๋ ํ์ด์ง๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌ, ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ๋ณด๋ด๋ฉด ํด๋น ํ๊ทธ์ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ ๊ณ ์ ํ ํ ํฐ์ด ํฌํจ๋์ด ์์ด์ผ ํจ. ์๋ฒ๋ ํด๋น ํ ํฐ์ ์ธ์
๋ฑ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์์ด์ผ ํจ.
๋ง์ฝ ํ ํฐ์ด ๋๋ฝ๋์ด ์๋ค๋ฉด CSRF ๋ก ์์ฌํ ์ ์์.
Double submit cookie
์น๋ธ๋ผ์ฐ์ ์ Same Origin ์ ์ฑ
์ผ๋ก ๊ณต๊ฒฉ์๊ฐ ์ฟ ํค๊ฐ์๋ ์ ๊ทผํ ์ ์๋๊ฒ์ ์ด์ฉํ์ฌ
1.๋ธ๋ผ์ฐ์ ๋ get์์ฒญ์ผ๋ก ํ ํฐ๊ฐ๊ณผ _csrf(์ํฌ๋ฆฟํค)๊ฐ์ ์๋ฒ๋ก ๋ถํฐ ๋ฐ๋๋ค
2.๋ธ๋ผ์ฐ์ ์์ write์์ฒญ์ header์ ํ ํฐ๊ฐ์ ์ฃผ๊ณ ์ฟ ํค๊ฐ์ ์ํฌ๋ฆฟํค๋ฅผ ๋ถ์ฌํ์ฌ ์๋ฒ์ ๋ฏธ๋ค์จ์ด์์ ํ ํฐ๊ฐ์ decodeํด์ decodeํ ์ํฌ๋ฆฟํค๊ฐ ์ฟ ํค๊ฐ๊ณผ ์ผ์นํ๋์ง ์ฒดํฌํ๋ค
์๋ฒ๊ฐ ์์กฐ๋ ์์ฒญ์ ๋ณด๋ด๋๋ก ํ๋ ์ทจ์ฝ์ ์ด๋ค. Client-Side Request Forgery(CSRF, ํด๋ผ์ด์ธํธ ์ธก ์์ฒญ ์์กฐ) ๊ธฐ๋ฒ์์ ์์ฒญ์ ๋ณด๋ด๊ฒ ๋๋ ์ฃผ์ฒด๋ง ๋ธ๋ผ์ฐ์ (Client) ์์ ์๋ฒ(Server) ๋ก ๋ฐ๋์๋ค๊ณ ์ดํดํด๋ ์ข๋ค.
HTTP GET ์์ฒญ์ ํตํ SSRF ๊ณต๊ฒฉ ์์
GET /api/fetch?url=http://internal-service.local/admin HTTP/1.1
Host: example.com
url์ ์ธ๋ถ๋ง์์ ์ ์ ๋ถ๊ฐ๋ฅํ internal-service.local/admin๋ก์ ์์ฒญ์ ์๋ฒ๊ฐ ๋์ ๊ณต๊ฒฉ์์๊ฒ ์ ๋ฌํ๋๋ก ํ๋ ์์์ด๋ค.
์ทจ์ฝํ ๊ฒ์ํ ์๋ฒ๊ฐ ์๋ค๊ณ ํ์. ํด๋น ์๋ฒ๋ ๊ณต๊ฒฉ์๊ฐ ์ด๋ฏธ์ง๊ฐ ํฌํจ๋ ๊ฒ์๊ธ์ ์์ฑํ๋ฉด, ํด๋น ๊ฒ์๊ธ์ ์ด๋ํ๋ ์ ์ ๋ค์๊ฒ ํด๋น ์ด๋ฏธ์ง์ url์ ๊ฒ์ฆ ์์ด ๋ณด์ฌ์ค๋ค.
image_url์ ์ ์์ ์ธ url๋์ ์
์์ ์ธ url์ ์ฝ์
ํ๋ค. ์๋ฅผ ๋ค์ด, ๋ค์๊ณผ ๊ฐ์ ์์ฒญ์ ๋ณด๋ผ ์ ์๋ค.
POST /upload-image HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
image_url=http://localhost/admin
์ฌ๊ธฐ์ http://๋ถ๋ถ์ ์คํค๋ง(scheme)๋ก์จ, ์์ธํ ๋ด์ฉ์ ํ๋จ์ ํ์ ํ๋ค.localhost/admin์ผ๋ก์ ์์ฒญ์ ๋ณด๋ด๊ณ , ์๋ต์ ์ ๊ทผ์์๊ฒ ์๋นํ๋ค.๐ก์คํค๋ง๋?
URL์์ ์คํค๋ง(schema)๋ ํ๋กํ ์ฝ์ ์ ์ํ๋ฉฐ, ํด๋ผ์ด์ธํธ๊ฐ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ๋ฐฉ์์ ๊ฒฐ์ ํ๋ค. ์คํค๋ง๋ URL์ ๋งจ ์ ๋ถ๋ถ์ ์์นํ๋ฉฐ, ์ฝ๋ก ๊ณผ ๋ ๊ฐ์ ์ฌ๋์(://)๋ก ๋๋๋ค. ์คํค๋ง๋ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ด๋ป๊ฒ ํต์ ํด์ผ ํ๋์ง๋ฅผ ์ง์ ํ๋ค. ์๋ฅผ ๋ค์ด, HTTP, HTTPS, FTP, FILE ๋ฑ์ด ์๋ค.
์ ์์์์,http...๋์ ,file:///etc/passwd๋ฅผ ๋ฃ์ผ๋ฉด, ์๋ฒ ํ์ผ ์์คํ ์ ์ ์ ์ ๋ณด๋ฅผ ์ทจ๋ํ ์ ์๋ค.