๐ก๏ธ Next.js Content Security Policy(CSP) ์ค์ ๊ฐ์ด๋
๐ก๏ธ Next.js Content Security Policy(CSP) ์ค์ ๊ฐ์ด๋
Next.js ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณด์์ ๊ฐํํ๊ธฐ ์ํด **Content Security Policy(CSP)**๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํ์ต๋๋ค.
CSP๋ XSS(Cross-Site Scripting) ๋ฐ ์ฝ๋ ์ธ์ ์
๊ณต๊ฒฉ์ ๋ฐฉ์งํ๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
โ 1. CSP์ ๊ธฐ๋ณธ ๊ฐ๋
CSP(Content Security Policy)๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์ธ๋ถ ๋ฆฌ์์ค๋ฅผ ๋ก๋ํ๊ฑฐ๋ ์คํํ ๋ ํ์ฉ ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ ๋ณด์ ์ ์ฑ
์
๋๋ค.
์ด๋ฅผ ํตํด ์
์ฑ ์ฝ๋ ์คํ์ ๋ฐฉ์งํ๊ณ , ์ ๋ขฐํ ์ ์๋ ์์ค๋ง ํ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์
๋ณด์์ ๊ฐํํฉ๋๋ค.
โ 2. Next.js์์ ๊ธฐ๋ณธ CSP ์ค์
Next.js์์๋ next.config.js ํ์ผ์ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ CSP๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ ์ค์
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self';",
},
],
},
];
},
};
์ค์ ํด์
- default-src 'self': ํ์ฌ ๋๋ฉ์ธ์์๋ง ๋ฆฌ์์ค๋ฅผ ๋ก๋
- script-src 'self': ์คํฌ๋ฆฝํธ๋ ํ์ฌ ๋๋ฉ์ธ์์๋ง ํ์ฉ
- style-src 'self': ์คํ์ผ ์ญ์ ํ์ฌ ๋๋ฉ์ธ์์๋ง ํ์ฉ
- img-src 'self': ์ด๋ฏธ์ง๋ ๋์ผ ๋๋ฉ์ธ์์๋ง ๋ก๋
โ 3. ๋์ ์ฝํ ์ธ ๋ฅผ ์ํ Nonce ์ค์
์ผ๋ถ ๋์ ์คํฌ๋ฆฝํธ๋ ์คํ์ผ์ ํ์ฉํด์ผ ํ ๋๋ Nonce๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ์ธ๋ผ์ธ ์ฝํ ์ธ ๋ง ํ์ฉํ ์ ์์ต๋๋ค.
3.1. Nonce ์์ฑ ๋ฏธ๋ค์จ์ด ์ค์
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';
export function middleware(request: NextRequest) {
const nonce = crypto.randomBytes(16).toString('base64');
const csp = `
default-src 'self';
script-src 'self' 'nonce-${nonce}';
style-src 'self' 'nonce-${nonce}';
img-src 'self' data:;
connect-src 'self';
`.replace(/\s{2,}/g, ' ').trim();
const response = NextResponse.next();
response.headers.set('Content-Security-Policy', csp);
response.headers.set('x-nonce', nonce);
return response;
}
3.2. Nonce๋ฅผ ํ์ฉํ ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ์ค์
// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
render() {
const nonce = (this.props as any).nonce;
return (
<Html>
<Head>
<script nonce={nonce}>console.log('์์ ํ ์คํฌ๋ฆฝํธ');</script>
</Head>
<body>
<Main />
<NextScript nonce={nonce} />
</body>
</Html>
);
}
}
export default MyDocument;
โ 4. CSP ์ค์ ์ฃผ์์ฌํญ
๋์ ๋ฆฌ์์ค ๋ฌธ์
- React Strict Mode: ์ผ๋ถ ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ๊ฐ CSP์ ์ํด ์ฐจ๋จ๋ ์ ์์ต๋๋ค.
- External Script: ์ธ๋ถ์์ ์ ๊ณต๋๋ ์คํฌ๋ฆฝํธ๋ ๋ฐ๋์ script-src ์ค์ ์ ๋๋ฉ์ธ์ ๋ช ์ํด์ผ ํฉ๋๋ค.
๋ณด๊ณ ์ค์
CSP ์๋ฐ ๋ก๊ทธ๋ฅผ ๋ชจ๋ํฐ๋งํ์ฌ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ํ์ ํ ์ ์์ต๋๋ค.
Content-Security-Policy: default-src 'self'; report-uri /csp-report
์ค์๊ฐ ๋ชจ๋ํฐ๋ง
CSP ์๋ฐ ๋ณด๊ณ ๋ฅผ ์์งํ์ฌ ๋์ํ ์ ์๋๋ก ์ ์ก URL์ ์ค์ ํ ์ ์์ต๋๋ค.
โ 5. ์ค์ ์์
๊ฐํ๋ CSP ์ค์
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'unsafe-inline' https://apis.google.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data:;
connect-src 'self' https://api.example.com;
font-src 'self' https://fonts.gstatic.com;
frame-ancestors 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
`.replace(/\s{2,}/g, ' ').trim(),
},
],
},
];
},
};
ํด์ค
- script-src 'unsafe-inline': ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ํ์ฉ (๊ถ์ฅํ์ง ์์)
- style-src 'unsafe-inline': ์ธ๋ผ์ธ ์คํ์ผ ํ์ฉ
- connect-src https://api.example.com: ์ธ๋ถ API ํธ์ถ ํ์ฉ
- font-src https://fonts.gstatic.com: Google Fonts ํ์ฉ
- frame-ancestors 'none': ์ธ๋ถ์์ iframe์ผ๋ก ์ฝ์ ๋ถ๊ฐ
- object-src 'none': ํ๋์ ๊ฐ์ ๊ฐ์ฒด ์ฌ์ฉ ๊ธ์ง
โ 6. CSP ํ ์คํธ ๋ฐ ๋๋ฒ๊น
๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ ํ์ฉ
- ํ์ด์ง๋ฅผ ์ด๊ณ F12(๊ฐ๋ฐ์ ๋๊ตฌ) ํด๋ฆญ
- Console ํญ์์ CSP ์๋ฐ ๋ก๊ทธ ํ์ธ
- Content Security Policy ์๋ฌ ๋ฐ์ ์ ์ ์ฑ ์์
์๋ฐ ๋ก๊ทธ ํ์ธ
CSP ์๋ฐ ๋ณด๊ณ ์๋ฅผ ์๋ฒ๋ก ์ ์กํ์ฌ ๋ฌธ์ ๋ฅผ ์ค์๊ฐ์ผ๋ก ํ์ ํ ์ ์์ต๋๋ค.
Content-Security-Policy: default-src 'self'; report-uri /csp-report
โ ์์ฝ ์ ๋ฆฌ
ํญ๋ชฉ ์ค์ ๋ฐฉ๋ฒ ๋น๊ณ
๊ธฐ๋ณธ CSP ์ค์ | next.config.js์์ ์ค์ | ๋ชจ๋ ํ์ด์ง์ ์ ์ฉ |
Nonce ํ์ฉ | ๋ฏธ๋ค์จ์ด์์ ๋์ ์์ฑ | ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ ํ์ฉ |
์๋ฐ ๋ณด๊ณ ์ค์ | report-uri๋ก ๋ก๊ทธ ์ ์ก | ์ค์๊ฐ ๋ชจ๋ํฐ๋ง ๊ฐ๋ฅ |
ํ ์คํธ ๋ฐ ๋๋ฒ๊น | ๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ ํ์ฉ | CSP ์ค๋ฅ ํ์ ์ฉ์ด |
Next.js, Content Security Policy, CSP, ์น ๋ณด์, XSS ๋ฐฉ์ง, nonce, ๋ณด์ ํค๋, ์น ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์, Next.js ๋ณด์, ์๋ฒ ๋ณด์ ์ค์