...

helmet ๋ชจ๋
helmet ๋ชจ๋์ ์๋ฒ์์ ๋ค์ํ HTTP ํค๋๋ฅผ ์๋ ์ค์ ์ ํตํด ์๋ฒ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ณด์์ ๊ฐํํด์ฃผ๋ ๋ํ์ ์ธ ๋ ธ๋ ๋ณด์ ๋ชจ๋์ด๋ค.
ํฌ๋ฉง์ ์จ์ ๋ด ๋จธ๋ฆฌ๋ฅผ ๋ณดํธํ๋ฏ์ด, ๋ด ์น์๋ฒ๋ฅผ ์ธ๋ถ์ ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ๋ณดํธํด์ค๋ค.
helmet ์ฌ์ฉ๋ฒ
์ฌ์ฉํ๋ ์๋ํฐ์ ํฐ๋ฏธ๋์์ ์๋์ ๊ฐ์ด helmet ๋ชจ๋์ ์ค์นํด์ฃผ๊ณ ๊ฐ๋จํ๊ฒ ์ ์ธ๋ง ํด์ฃผ๋ฉด ๋๋ค. (๋ฐฐํฌ์ฉ ๋ชจ๋)
helmet
help secure Express/Connect apps with various HTTP headers. Latest version: 5.1.0, last published: 7 days ago. Start using helmet in your project by running `npm i helmet`. There are 3266 other projects in the npm registry using helmet.
www.npmjs.com
> npm install helmet # ํฌ๋ฉง ์ค์น
// helmet์ ๊ฐ๋ฐํ๊ฒฝ์์๋ ํ์์๋ ๋ชจ๋์ด๋ ๋ฐฐํฌํ๊ฒฝ์์๋ง ํ์ฑ
if (process.env.NODE_ENV === 'production') {
app.use(morgan('combined'));
app.use(helmet( { contentSecurityPolicy: false } )); // contentSecurityPolicy๋ ๊ฝค ๋ณต์กํ ์ค์ ์ด๊ธฐ ๋๋ฌธ์ ์ผ๋จ ๊บผ๋๋ค. (ํ์ )
} else {
app.use(morgan('dev'));
}
helmet ๋ฏธ๋ค์จ์ด ๊ธฐ๋ฅ
Helmet์ Express์ ๋ฏธ๋ค์จ์ด ๋ชจ๋์ด๋ฉฐ, Helmet ๋ํ ์ฌ๋ฌ ๋ฏธ๋ค์จ์ด ๋ชจ๋์ ํฉ์ณ ๋์ ๋ฏธ๋ค์จ์ด ํจํค์ง ๋ชจ๋์ด๋ค.
์ ํํ ๋งํ์๋ฉด, ์ต์คํ๋ ์ค ๊ธฐ๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ์์ HTTP response header๋ฅผ ์ค์ ํ๋ ์ฌ๋ฌ๊ฐ์ ์์ ๋ฏธ๋ค์จ์ด ํจ์ ์ ํ ๋ชจ์์ธ ๊ฒ์ด๋ค.
๊ฐ ๋ฏธ๋ค์จ์ด ํจ์๋ค์ ๊ธฐ๋ฅ์ ์๋์ ๊ฐ๋ค.
const express = require("express");
const helmet = require("helmet");
const app = express();
// ๊ธฐ๋ณธ ์ค์ ๊ธฐ๋ฅ ์ฌ์ฉ
app.use(helmet());
// or
// ํน์ ์ธ๋ถ ๊ธฐ๋ฅ ํ๋ํ๋ ์ค์ ํ ๋ ์ฌ์ฉ
app.use(helmet.contentSecurityPolicy());
app.use(helmet.crossOriginEmbedderPolicy());
app.use(helmet.crossOriginOpenerPolicy());
app.use(helmet.crossOriginResourcePolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.expectCt());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.originAgentCluster());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());
๋ชจ๋๋ช | ๊ธฐ๋ฅ |
contentSecurityPolicy (CSP) | ์ฝํ
์ธ ๋ณด์ ์ ์ฑ
์ค์ ๋ฐ ๊ตฌ์ฑ์ ํตํด ์๋ํ์ง ์์ ๋ด์ฉ์ด ํ์ด์ง์ ์ฝ์
๋๋ ๊ฒ์ ๋ฐฉ์ง Content-Security-Policy ํค๋๋ฅผ ์ค์ ํด์ XSS๋ ๊ต์ฐจ์ฌ์ดํธ ์ธ์ ์ ๋ฑ์ ๋ฐฉ์งํ๋ค. ๋ค๋ฅธ์ฌ์ดํธ์ script๋ฅผ ๋ถ๋ฌ์ค๋๊ฒ๋ ๋ง๊ธฐ๋๋ฌธ์ ํฌ๋ฉง ์ ์ฉ์ ๋ณ๋๋ก ์ค์ ์ด ํ์ํ๋ค. |
crossOriginEmbedderPolicy | Cross-Origin-Embedder-Policy ํค๋๋ฅผ require-corp๋ก ์ค์ |
crossOriginOpenerPolicy | Cross-Origin-Opener-Policy ํค๋๋ฅผ ์ค์ |
crossOriginResourcePolicy | Cross-Origin-Resource-Policy ํค๋๋ฅผ ์ค์ |
dnsPrefetchControl | ๋๋ฉ์ธ์ด ๋ฏธ๋ฆฌ๋ก๋ฉ๋๋ Prefetch์ ๋ํด ์ปจํธ๋กค ํ๊ธฐ์ํด X-DNS-Prefetch-Control ํค๋๋ฅผ ์ค์ ๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ๋ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํด์ ํ์ด์ง์ ๋งํฌ์ ๋ํ DNS ๋ ์ฝ๋(record)๋ฅผ ๋ฏธ๋ฆฌ ์ถ์ถ(prefetch)ํ๋ค. โ ์ด๋ฌํ ๋ฐฉ์์ผ๋ก์จ ์ฌ์ฉ์๊ฐ ๋งํฌ๋ฅผ ํด๋ฆญํ ๋ ๋์์ ๋ํ IP๊ฐ ์ด๋ฏธ ์๋ ค์ ธ์๋ค. โ ์ด๋ก ์ธํด ์ ๋ฐ๋ ์ ์๋ ์ํฉ์ DNS ์๋น์ค๊ฐ ๊ณผ๋ํ๊ฒ ์ฌ์ฉ๋๋ ๊ฒ (ํฐ ์น ์ฌ์ดํธ๋ฅผ ์์ ํ๊ณ ์๋ ๊ฒฝ์ฐ, ์๋ฐฑ๋ง ๋ช ์ด ๋ฐฉ๋ฌธํ๋ ๊ฒฝ์ฐ ๋ฑ), ๊ฐ์ธ ์ ๋ณด ๋ณดํธ ๋ฌธ์ (๋์ฒญ์ ํ ๋ช ์ด ๋น์ ์ด ํน์ ํ์ด์ง์ ์๋ค๊ณ ์ถ๋ก ํ ์ ์์), ํ์ด์ง ํต๊ณ ๋ณ๊ฒฝ (์ผ๋ถ ๋งํฌ๋ ๋ฐฉ๋ฌธ๋์ง ์์ ๊ฒฝ์ฐ์๋ ๋ฐฉ๋ฌธ๋ ๊ฒ์ผ๋ก ๋ํ๋ ์ ์์) โ ์ด ์๋ค. ์ฆ, ๋ณด์ ์๊ตฌ๊ฐ ๋์ ๊ฒฝ์ฐ์๋ ์ฑ๋ฅ ์ ํ๋ฅผ ๊ฐ์ํ๊ณ ์ DNS ํ๋ฆฌํ์น๋ฅผ ๋นํ์ฑํํ ์ ์๋ค. |
exprectCt | Expect-CT ํค๋๋ฅผ ์ค์ ํ์ฌ SSL ์ธ์ฆ์ ์ค๋ฐ๊ธ์ ์๋ฐฉ |
frameguard | X-Frame-Options ํค๋๋ฅผ ์ค์ ํ์ฌ ํด๋ฆญ์ฌํน์ ๊ณต๊ฒฉ์ ๋ฐฉ์ง ํด๋ฆญ์ฌํน์ด๋ ์ฌ์ฉ์๊ฐ ์์ ์ด ํด๋ฆญํ๊ณ ์๋ค๊ณ ์ธ์งํ๋ ๊ฒ๊ณผ ๋ค๋ฅธ ๊ฒ์ ํด๋ฆญํ๋๋ก ํ์ฌ ์์ด๋ ํดํน ๊ธฐ๋ฒ์ด๋ค. ์์ด๊ธฐ ์ํด ๋ณด์ด์ง ์๋ ๋ ์ด์ด์ ๋ณด์ด์ง ์๋ ๋ฒํผ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ด ์๋ค. ์๋ฒ๊ฐ ์ ๊ณตํ ํ์ด์ง์ ์๋ฒ์ ํ๋ฝ์์ด ์ ์ ์ ์ธ ํ์ด์ง๋ฅผ ๋ก๋ํ frame ํน์ iframe ํ๊ทธ๋ฅผ ๋ฃ์ ์ ์๊ธฐ ๋๋ฌธ์. ์ ์์ ์ธ ์ปจํ ์คํธ(context)์์ ํ์ด์ง์ ํด๋ฆญ์ฌํน์ ์คํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ฌํ context์์, ํด์ปค๋ ์๋ฒ๊ฐ ์ต์ด์ ์ ๊ณตํ ํ์ด์ง ์์๋ค๊ฐ ์จ๊ฒจ์ง ๋ ์ด์ด๋ฅผ ๋ฃ์ ์ ์๋ค. ๊ทธ๋์ ์จ๊ฒจ์ง ๋ฒํผ์ ์ฌ์ฉํ์ฌ ๋์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ์๋ ์๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ iframe์ ๋ฃ๋ ํ์ฉ๋ ์ฌ์ดํธ๋ง์ ์ ํํ๋ ๊ฐ๋ ์ด๋ค. |
hidePoweredBy | ์๋ต ํค๋์ ์๋ X-Powerd-By์ ์๋ฒ ์ํํธ์จ์ด๊ฐ ํ๊ธฐ๋๋๋ฐ ์ด๋ฅผ์จ๊ฒจ์ค๋ค. ์ด ์ ๋ณด๋ ์ ์์ ์ผ๋ก ํ์ฉ๋ ๊ฐ๋ฅ์ฑ์ด ๋๊ธฐ์ ํฌ๋ฉง์ ํตํด์ ์ ๊ฑฐํด ์ฃผ๋ ๊ฒ์ด ์ข๋ค. |
hsts | ์๋ฒ์ ๋ํ ์์ ํ(SSL/TLS๋ฅผ ํตํ HTTP) ์ฐ๊ฒฐ์ ์ ์ฉํ๋ Strict-Transport-Security ํค๋๋ฅผ ์ค์ . ๋ธ๋ผ์ฐ์ ์๊ฒ HTTPS๋ง์ ํตํด์ ์ฌ์ดํธ์ ์์ธ์ค ํ ์ ์๋๋ก ์์ฒญ (๊ธฐ๋ณธ false) ์ฌ์ฉ์๊ฐ ํน์ ์ฌ์ดํธ์ ์ ์ํ ๋ ํด๋น ์ฌ์ดํธ๊ฐ HTTPS๋ฅผ ์ง์ํ๋์ง, ํ์ง ์๋์ง๋ฅผ ๋ฏธ๋ฆฌ ๋ชจ๋ฅด๋ ๊ฒฝ์ฐ๊ฐ ๋๋ถ๋ถ์ด๋ค. ๊ทธ๋ ๊ธฐ์ ๋ธ๋ผ์ฐ์ ๋ ๋ํดํธ๋ก HTTP๋ก ๋จผ์ ์ ์์ ์๋ํ๋ค. ์ด๋ HTTPS๋ก ์ง์๋๋ ์ฌ์ดํธ์๋ค๋ฉด 301Redirect๋ 302 Redirect๋ฅผ ์๋ตํ์ฌ HTTPS๋ก ๋ค์ ์ ์ํ๋๋ก ํ๋ค. ํ์ง๋ง ์ด๋ ํด์ปค๊ฐ ์ค๊ฐ์ ๊ณต๊ฒฉ์ ํ์ฌ, ์ค๊ฐ์ ํ๋ก์ ์๋ฒ๋ฅผ ๋๊ณ , [๋] โ [ํด์ปค] ์ฌ์ด์์๋ HTTP ํต์ ์ ํ๊ณ [ํด์ปค] โ [์น์ฌ์ดํธ] ์ฌ์ด์์ HTTPS ํต์ ์ ํ๋ค๋ฉด, ๊ฐ์ธ์ ๋ณด๊ฐ HTTP ํ๋กํ ์ฝ์ ํตํด ํด์ปค์๊ฒ๋ก ์ ํด์ง๋ ์ฐธ์ฌ๊ฐ ์ผ์ด๋๋ค. ์ด๋ฌํ ๊ณต๊ฒฉ์ SSL Stripping์ด๋ผ๊ณ ํ๋ฉฐ ์ด๋ฐ ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด HSTS๋ฅผ ์ค์ ํ๋ค. |
ieNoOpen | X-Download-Options ํค๋๋ฅผ ์ค์ ํ์ฌ ie8 ์ด์์์๋ง ์ฌ์ฉํ ์ ์๋๋ก |
noSniff | X-Content-Type-Options ๋ฅผ ์ค์ ํ์ฌ ์ ์ธ๋ ์ฝํ
์ธ ์ ํ์ผ๋ก๋ถํฐ ๋ฒ์ด๋ ์๋ต์ ๋ํ ๋ธ๋ผ์ฐ์ ์ MIME ์ค๋ํ์ ๋ฐฉ์ง MIME์ด๋ Multipurpose Internet Mail Extensions์ ์ฝ์๋ก ํด๋ผ์ด์ธํธ์๊ฒ ์ ์ก๋ ๋ฌธ์์ ๋ค์์ฑ์ ์๋ ค์ฃผ๊ธฐ ์ํ ํฌ๋งท์ด๋ค. ๋ธ๋ผ์ฐ์ ๋ ๋ฆฌ์์ค๋ฅผ ๋ด๋ ค๋ฐ์ ๋ MIME ํ์ ์ ๋ณด๊ณ ๋์ํ๊ธฐ์ ์ ํํ ์ค์ ์ด ์ค์ํ๋ค. MIME ์ค๋ํ์ด๋ ๋ธ๋ผ์ฐ์ ๊ฐ ํน์ ํ์ผ์ ์ฝ์ ๋ ํ์ผ์ ์ค์ ๋ด์ฉ๊ณผ Content-Type์ ์ค์ ๋ ๋ด์ฉ์ด ๋ค๋ฅด๋ฉด ํ์ผ๋ก ๋ถํฐ ํ์์ ์ถ์ธกํ์ฌ ์คํํ๋ ๊ฒ์ธ๋ฐ, ํธ๋ฆฌํจ์ ์ํ ๊ธฐ๋ฅ์ด์ง๋ง ๊ณต๊ฒฉ์์๊ฒ ์ ์ฉ ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค. |
originAgentCluster | Origin-Agent-Cluster ํค๋๋ฅผ ์ค์ ํ์ฌ ์ค๋ฆฌ์ง๊ฐ ๋ฌธ์๋ฅผ ๋ณ๋ ์์ด์ ํธ ํด๋ฌ์คํฐ๋ก ๋ถ๋ฆฌ |
permittedCrossDomainPolicies | X-Permitted-Cross-Domain-Policies ํค๋๋ฅผ ์ค์ ํ์ฌ ํฌ๋ก์ค๋๋ฉ์ธ ์ปจํ
์ธ ์ ์ฑ
์ ์ค์ X-Permitted-Cross-Domain-Policies ํค๋๋ ์ผ๋ถ ํด๋ผ์ด์ธํธ(๋๋ถ๋ถ Adobe ์ ํ)์ ๋๋ฉ์ธ ๊ฐ ์ฝํ ์ธ ๋ก๋์ ๋ํ ๋๋ฉ์ธ ์ ์ฑ ์ ์ฒ๋ฆฌํ๋ค. |
referrerPolicy | ์ฐธ์กฐ referrer ํค๋๋ฅผ ์จ๊น |
xssFilter | X-XSS-Protection ํค๋๋ฅผ 0์ผ๋ก ์ค์ ํ์ฌ XSS(Cross Site Scripting) ๊ณต๊ฒฉ ์คํฌ๋ฆฝํธ๋ฅผ ๋นํ์ฑํ์ฌ ์๋ฐฉ |
noCache ๋ฏธ๋ค์จ์ด๋ (ํ์ฌ Helmet 5์์ deprecated ๋จ)
SSL (Secure Socket Layer) ๋ณด์ ์์ผ ๊ณ์ธต
์น์ฌ์ดํธ์ ๋ธ๋ผ์ฐ์ ( ํน์ ๋ ์๋ฒ ) ์ฌ์ด์ ์ ์ก๋ ๋ฐ์ดํฐ๋ฅผ ์ํธํํ์ฌ ์ธํฐ๋ท ์ฐ๊ฒฐ์ ์ ์งํ๋ ํ์ค ๊ธฐ์ .
์ด๋ ํด์ปค๊ฐ ์ ๋ณด ๋ฐ ๊ธ์ต ์ ๋ณด๋ฅผ ํฌํจํ ์ ์ก๋๋ ๋ชจ๋ ์ ๋ณด๋ฅผ ์ด๋ํ๊ฑฐ๋ ํ์น๋ ๊ฒ์ ๋ฐฉ์งํ๋ค.
TLS (์ ์ก ๊ณ์ธต ๋ณด์ Transport Layer Security, TLS)
TLS๋ ๊ฐ์ฅ ์ต์ ๊ธฐ์ ๋ก ๋ ๊ฐ๋ ฅํ ๋ฒ์ ์ SSL ์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
๊ทธ๋ฌ๋ SSL์ด ๋ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ฉ์ด์ด๊ธฐ์, ์ฌ์ ํ ๋ณด์ ์ธ์ฆ์๋ SSL์ด๋ผ ๋ถ๋ฆฐ๋ค.
XSS (Cross Site Scripting) ๊ณต๊ฒฉ
์ฌ์ดํธ์ ๋จ๋ชฐ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ ํด ๊ทธ๋ฆ๋ ์คํฌ๋ฆฝํธ ์คํ์ผ๋ก ๊ณต๊ฒฉ์ ํ๋ ํ์.
์๋ฅผ ๋ค์ด ์ฟ ํค๋ ์ธ์ ํ ํฐ ๋ฑ์ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ํ์ทจ.
Strict-Transport-Security
์น ์ฌ์ดํธ๋ฅผ ์ ์ํ ๋ ๊ฐ์ ์ ์ผ๋ก https ํ๋กํ ์ฝ๋ก๋ง ์ ์ํ๊ฒ ํ๋ ๊ธฐ๋ฅ.
helmet default
๊ธฐ๋ณธ์ ์ผ๋ก helmet์ ์ธ๋ถ์ ์ธ ์ฌ๋ฌ ๋ฏธ๋ค์จ์ด ํจ์๋ฅผ ํฌํจํ๊ณ ์์ง๋ง, ๋จ์ํ helmet()์ผ๋ก ํธ์ถํ๊ฒ ๋๋ฉด default๋ก ์ง์ํ๋ ๋ชจ๋๋ค๋ก ์ค์ ๋๋ค.
// ๊ธฐ๋ณธ ์ค์ ๊ธฐ๋ฅ ์ฌ์ฉ
app.use(helmet());
default ๋ชจ๋ ์ข ๋ฅ๋ ์๋์ ๊ฐ๋ค.

๋ง์ผ default ๋ฏธ๋ค์จ์ด์์ ๋ช๊ฐ๋ง ์ง์ฐ๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ๋ค.
app.use(helmet({
frameguard: false
}))
CSP (contentSecurityPolicy)
CSP๋ฅผ ์ฌ์ฉํ๋ฉด ์น์ฌ์ดํธ์ http์๋ต์ CSP ํค๋๊ฐ ์ถ๊ฐ๋๋ค.
CSP ํค๋๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ, ๋ธ๋ผ์ฐ์ ๋ CSP ํค๋์ ์ธ๊ธ๋์ง ์์ ๋ฆฌ์์ค๋ค์ ๋ค์๊ณผ ๊ฐ์ด ๋ก๋ํ์ง ์๊ฒ๋๋ค.
Helmet์ CSP ๊ธฐ๋ณธ์ค์ ์ โselfโ ์ฆ, ์์ ์ ์น์ฌ์ดํธ์ ์กด์ฌํ๋ ๋ฆฌ์์ค๋ค๋ง์ ํ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ ๋ด ์น์๋น์ค์ CDN ๋ฑ์ ์ธ๋ถ ์ฌ์ดํธ์ ์์ค๋ฅผ ์ด์ฉํ ๊ฒฝ์ฐ, ๋ค๋ฅธ ์น์ฌ์ดํธ์์ ์ด๋ฏธ์ง ๋ก๋ํ๋ ๊ฒฝ์ฐ, ์ฌ์ง์ด ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ๋ก ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์์ฑํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ๋ชจ๋ ์๋ฌ๋ฅผ ๋ฐ์์ํค๊ฒ ๋๋ค.

๋ฏธ๋ฆฌ ์์์ helmet() ์ต์ ์ผ๋ก contetnSecurityPolicy๋ฅผ false๋ก ํด๋๋ผ๊ณ ๊ถ๊ณ ํ๋๋ฐ, ์๋ํ๋ฉด csp๋ฅผ ์ธ์ธํ๊ฒ ์ค์ ํ์ง ์์ผ๋ฉด ์ฝ์์์ ๋ง๊ตฌ ์ค๋ฅ๋ฅผ ๋ด๋ฟ์ด ์ด๋ณด์ ์ ์ฅ์์ ํผ๋์ ์ค์ ์๊ธฐ ๋๋ฌธ์ ๊ทธ๋ ๋ค.
CSP ๋ ์ ๋ขฐํ ์ ์๋ ์ปจํ ์ธ ์์ค์ ํ์ดํธ๋ฆฌ์คํธ๋ฅผ ์ ์ํ๋ ๋ฐฉ์์ผ๋ก ์๋ํ๋ค.
์น ํ์ด์ง์ ํ์ํ ๊ฐ ๋ฆฌ์์ค ์ ํ(์คํฌ๋ฆฝํธ, ์คํ์ผ์ํธ, ๊ธ๊ผด, ํ๋ ์, ๋ฏธ๋์ด ๋ฑ)์ ๋ํด ๊ตฌ์ฑํ ์ ์๋ค.
์ฌ์ฉ ๊ฐ๋ฅํ ์ง์นจ(directive)์ด ์ฌ๋ฌ ๊ฐ ์์ผ๋ฏ๋ก, ์น ์ฌ์ดํธ ์์ ์๋ ์ธ๋ถ์ ์ธ ์ ์ด๋ฅผ ํ ์ ์๋ค.
๋ฐ๋ผ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์น ํ์ด์ง๊ฐ ์ฌ์ฉํ ์ ๋ขฐํ ์ ์๋ ๋ฆฌ์์ค๋ค์ ๋๋ฉ์ธ์ CSP ํค๋์ ์ถ๊ฐํด์ผ ํ๋ค.
contentSecurityPolicy ์ต์ ์ค์ ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
const cspOptions = {
directives: {
// ํฌ๋ฉง ๊ธฐ๋ณธ ์ต์
๊ฐ์ ธ์ค๊ธฐ
...helmet.contentSecurityPolicy.getDefaultDirectives(), // ๊ธฐ๋ณธ ํฌ๋ฉง ์ค์ ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ ํจ์๋ฅผ ๋ฐ์ ์ ๊ฐ ์ฐ์ฐ์๋ก ์ฝ์
/*
none : ์ด๋ณ ๊ฒ๋ ํ์ฉํ์ง ์์
self : ํ์ฌ ์ถ์ฒ์์๋ ํ์ฉํ์ง๋ง ํ์ ๋๋ฉ์ธ์์๋ ํ์ฉ๋์ง ์์
unsafe-inline : ์ธ๋ผ์ธ ์๋ฐ์คํฌ๋ฆฝํธ, ์ธ๋ผ์ธ ์คํ์ผ์ ํ์ฉ
unsafe-eval : eval๊ณผ ๊ฐ์ ํ
์คํธ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฉ์ปค๋์ฆ์ ํ์ฉ
*/
// ๊ตฌ๊ธ API ๋๋ฉ์ธ๊ณผ ์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ, eval ์คํฌ๋ฆฝํธ๋ฅผ ํ์ฉ
"script-src": ["'self'", "*.googleapis.com", "'unsafe-inline'", "'unsafe-eval'"],
// ๋ค์๊ณผ ์นด์นด์ค์์ ์ด๋ฏธ์ง ์์ค๋ฅผ ํ์ฉ
'img-src': ["'self'", 'data:', '*.daumcdn.net', '*.kakaocdn.net'],
// ์์ค์ https์ http ํ์ฉ
"base-uri" : ["/", "http:"],
}
}
// Helmet์ ๋ชจ๋ ๊ธฐ๋ฅ ์ฌ์ฉ. (contentSecurityPolicy์๋ custom option ์ ์ฉ)
app.use(helmet({
contentSecurityPolicy: cspOptions,
}));
Helmet์ด ์ง์ํ๋ ์ด๋ฆ ์ง์ ์คํ์ผ(naming style)์ defaultSrc ๋ฐ default-src ์ ๋ชจ๋ ํ์ฉํ๋ค. (camel, snake ํ๊ธฐ๋ฒ์ ๋ชจ๋ ์ง์)
kebab-cased | camel-cased | description |
base-uri | baseUri | document์ <base> ์์์ ์ฌ์ฉํ ์ ์๋ URL์ ์ ํ |
block-all-mixed-content | blockAllMixedContent | HTTPS๋ฅผ ์ฌ์ฉํ์ฌ ํ์ด์ง๊ฐ ๋ก๋๋ ๋ HTTP๋ฅผ ์ฌ์ฉํ์ฌ asset์ ๋ก๋ํ๋ ๊ฒ์ ๋ฐฉ์ง |
child-src | childSrc | <frame>์ด๋ <iframe>๊ณผ ๊ฐ์ ์์๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋๋ ์น ์์ ์๋ ๋ดํฌ๋ ๊ฒ์ ์ปจํ ์คํธ์์ ๊ฐ๋ฅํ ์์ค๋ฅผ ์ ์ |
connect-src | connectSrc | ์คํฌ๋ฆฝํธ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋ํ ์ ์๋ URL ์ ํ |
default-src | defaultSrc | ๋ค๋ฅธ fetch directive๋ค์ fallback์ผ๋ก ์ ๊ณต๋จ |
font-src | fontSrc | @font-face๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋ํ ์ ์๋ ์์ค๋ฅผ ์ง์ |
form-action | formAction | ์ง์ ๋ ์ปจํ ์คํธ์์ ์ ์ถ ๋์์ผ๋ก ์ฌ์ฉ๋ ์ ์๋ URL์ ์ ํ |
frame-ancestors | frameAncestors | <frame>, <object>, <embed> ํน์ <applet>์ ์ฌ์ฉํ์ฌ ์๋ฒ ๋๋ ๋ ํ์ด์ง์ ๋ถ๋ชจ๋ฅผ ์ง์ |
frame-src | frameSrc | <frame>๊ณผ <iframe>๊ณผ ๊ฐ์ ์์๋ฅผ ์ฌ์ฉํ์ฌ ๋ดํฌ๋ ๊ฒ์ ์ปจํ ์คํธ๋ฅผ ๋ก๋ํ๋๋ฐ ์ฌ์ฉ ๊ฐ๋ฅํ ์์ค๋ฅผ ์ง์ |
img-src | imgSrc | ๊ฐ๋ฅํ ์ด๋ฏธ์ง์ favicon ์์ค ์ง์ |
manifest-src | manifestSrc | ๊ฐ๋ฅํ manifest ์์ค ์ง์ |
media-src | mediaSrc | <audio>, <video>, <track> ์์๋ค์ ์ฌ์ฉํ์ฌ ๋ก๋ํ ์ ์๋ ๊ฐ๋ฅํ ์์ค๋ฅผ ์ง์ |
object-src | objectTypes | <object>, <embed>, ๊ทธ๋ฆฌ๊ณ <applet> |
plugin-types | pluginTypes | ๋ก๋ํ ์ ์๋ ๋ฆฌ์์ค ํ์ ๋ค์ ์ ํํ์ฌ document์ ํฌํ ๋ ์ ์๋ ํ๋ฌ๊ทธ์ธ ์ค์ ์ ์ ํ |
prefetch-src | prefetchSrc | prefetch ํน์ prerendered๋ ์ ์๋ ์์ค๋ค์ ์ง์ |
report-to | reportTo | SecurityPolicyViolationEvent ์คํ |
report-uri | reportUri | ์ปจํด์ธ ๋ณด์ ์ ์ฑ ์๋ฐ ์๋๋ฅผ ๋ณด๊ณ ํ๋๋ก user agent์ ์ง์. ๋ณด๊ณ ์๋ HTTP POST ์์ฒญ์ ํตํด ์ง์ ๋ URI๋ก JSON ๋ฌธ์๋ก ์ ์ก |
require-sri-for | requireSriFor | ํ์ด์ง์ ์คํฌ๋ฆฝํธ๋ ์คํ์ผ์ SRI ์ฌ์ฉ์ ์์ฒญ |
sandbox | sandbox | <iframe> sandbox ์์ฑ๊ณผ ์ ์ฌํ๊ฒ ๋ฆฌ์์ค ์์ฒญ์ ๋ํ ์๋๋ฐ์ค ํ์ฑํ |
script-src | scriptSrc | ๊ฐ๋ฅํ javascript ์์ค ์ง์ |
style-src | styleSrc | ๊ฐ๋ฅํ style ์์ค ์์ |
upgrade-insecure-requests | upgradeInsecureRequests | ์ฌ์ดํธ์ ๋ชจ๋ ๋ถ์์ ํ URL(HTTP)๋ค์ ๋ณด์ URL(HTTPS)๋ก ๊ต์ฒดํ ๊ฒ์ฒ๋ผ ์ฒ๋ฆฌํ๋๋ก use agent์ ์ง์ |
worker-src | workerSrc | ๊ฐ๋ฅํ Worker, SharedWorker, ServiceWorker ์์ค ์ง์ |
๋ณด๋ค ์์ธํ ์ฌ์ฉ๋ฒ์ ๋ค์ ์ฌ์ดํธ๋ฅผ ์ฐธ๊ณ ํ๊ธธ ๋ฐ๋๋ค.
Helmet - Front Study
ํค๋๊ฐ ํ์คํ๊ฒ ์ค์ ๋๋ก๋ก ๋ฏธ๋ค์จ์ด ์คํ์ ์๋ถ๋ถ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข๋ค. ์ต์คํ๋์ค 3์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, app.router ์ ์ ๋ฏธ๋ค์จ๋ฌ๋ฅผ ๋์ดํด์ผ ํฉ๋๋ค. ๊ฐ๋ณ์ ์ผ๋ก ์ฌ์ฉํ ์๋ ์๋ค. defau
hangem-study.readthedocs.io
helemt์ ์ด์ฉํ ์น ์ ๋ณด๋ณด์ ์ค์ ์์
์๋ฒ ์ํํธ์จ์ด ์จ๊ธฐ๊ธฐ
๋จผ์ ์ ์ฉ ์ ์ Response Headers ์ฌ์ง์ ์ดํด๋ณด๋ฉด, Server์ X-Powered-By ํค๋๊ฐ ์ ๋๋ผํ๊ฒ ๋
ธ์ถ๋์ด ์๋ฒ์์ ์ฌ์ฉ์ค์ธ ์น ์๋ฒ ์ํํธ์จ์ด์ ์ข
๋ฅ๋ฅผ ์ ์ ์๊ฒ ๋๋ค.
๋ง์ฝ nginx/1.15.5์ ์ทจ์ฝ์ ์ ๋ณด๊ฐ ๊ณต๊ฐ๋๋ค๋ฉด, ํด์ปค๊ฐ ์ทจ์ฝ์ ์ ์ด์ฉํด ์ฝ๊ฒ ์ฐ๋ฆฌ์ ์๋ฒ์ ์นจํฌํ ์ ์๋ค.

๋ค์์ helmet ์ ์ฉ ํ์ Response Headers ์ฌ์ง์ด๋ค.
Server์ X-Powered-By ํค๋์ ์ ๋ณด๊ฐ ์ฌ๋ผ์ง๋ฉด์, ํด์ปค์๊ฒ ์ ๊ณต๋ ์ ์์๋ ์๋ฒ์ ๋ํ ์ฃผ์ ์ ๋ณด๊ฐ ๋๋ถ๋ถ ์ฌ๋ผ์ก์์ ์ ์ ์๋ค.
app.use(helmet.hidePoweredBy())

ํค๋๋ฅผ ๋ณด์ง๋ชปํ๋ค๊ณ ํด์ ์๋ฌด๋ ์ทจ์ฝ์ ์ ์ ์ฉํ ์ ์๊ฒ๋๋ ๊ฒ์ด ์๋๋ค.
๊ณต๊ฒฉ์๋ก ํ์ฌ๊ธ ์ฌ์ดํธ์ ์ ์ฌ์ ์ผ๋ก ์ทจ์ฝํ ๊ธฐ์ ์ด ์๋์ง ํ์ ํ๊ธฐ ์ฝ๊ฐ ์ด๋ ต๊ฒํจ์ผ๋ก์จ ๊ณต๊ฒฉ์ ์ ์ฒด์ ์ธ ์งํ์ ์ฝ๊ฐ ๋ฆ์ถ๊ฑฐ๋ ๊ฒ์ผ๋ฅธ ํด์ปค๋ฅผ ๋ง๋ ์ ๋์ ํจ๊ณผ๋ฅผ ๋ฐํํ๋ค.
๋ํ x-powered-by ํค๋๋ฅผ ์ง์ฐ๋ ๋์ , ๊ธฐ์กด๊ฐ์ ๋ค๋ฅธ ๊ฒ์ผ๋ก ๋ฐ๊ฟ์๋ ์๋ค (๋์)
app.use (helmet.hidePoweredBy ({setTo : 'PHP 4.2.0'}))

์ฌ์ฉ์์ HTTPS ์ฐ๊ฒฐ ์ ์งํ๊ธฐ
HTTPS๋ก ์ ์ํ์ ๋ HTTP๋ก ๊ฐ์ง ์๊ฒ ํ๊ธฐ์ํด HSTS(HTTP Strict Transport Security)๋ผ๋ ๊ธฐ๋ฅ์ ์ง์ํ๋ค.
HSTS ๋ ํ๋กํ ์ฝ ๋ค์ด๊ทธ๋ ์ด๋ ๊ณต๊ฒฉ(protocol downgrade attacks)๊ณผ ์ฟ ํค ํ์ด์ฌํน(cookie hijacking)๋ก๋ถํฐ ์น์ฌ์ดํธ๋ฅผ ๋ณดํธํ๋๋ฐ ๋์์ด๋๋ ์น ๋ณด์ ์ ์ฑ
์ด๋ค. โ
HTTPS๋ฅผ ํตํด ์น ์ฌ์ดํธ์ ์ก์ธ์คํ ์ ์๋ ๊ฒฝ์ฐ ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์๊ฒ ์์ ํ์ง ์์ HTTP๋ฅผ ์ฌ์ฉํ์ง ์๋๋ก ์์ฒญํ ์ ์๋ค. โ
Strict-Transport-Security ํค๋๋ฅผ ์ค์ ํจ์ผ๋ก์จ ๋ธ๋ผ์ฐ์ ์๊ฒ ์ง์ ๋ ์๊ฐ ๋ด์ ํฅํ ์์ฒญ์ ๋ํด HTTPS๋ฅผ ์ฌ์ฉํ๋๋ก ์ง์ํ๋ค.
์ฆ, ์ด๊ฒ์ ์ด๊ธฐ ์์ฒญ ์ดํ์ ์ค๋ ์์ฒญ์ ์ ์ฉ๋๋ฏ๋ก ์ดํ ์์ฒญ์ ๋ํด์๋ง ํจ๊ณผ๊ฐ ์๋ค.
app.use(helmet.hsts({
maxAge: 90*24*60*60, // 90์ผ๋์ https ์ฌ์ฉํ๋๋ก
force: true, // ๊ธฐ๋ณธ ์ค์ ์ ๋ฌด์
includeSubDomains: true,
preload: true
}));
XSS (๊ต์ฐจ ์ฌ์ดํธ ์คํฌ๋ฆฝํ ) ๋ฐฉ์ด
XSS(๊ต์ฐจ ์ฌ์ดํธ ์คํฌ๋ฆฝํ ) ๊ณต๊ฒฉ์ ๊ฒ์ํ์ด๋ ์น ๋ฉ์ผ ๋ฑ์ ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ฐ์ ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์ฝ์ ํด ๊ฐ๋ฐ์๊ฐ ๊ณ ๋ คํ์ง ์์ ๊ธฐ๋ฅ์ด ์๋ํ๊ฒ ํ๋ ๊ณต๊ฒฉ์ด๋ค.
<!-- ์๋ฐ์คํฌ๋ฆฝํธ ๋งํฌ -->
<a href="javascript:alert('XSS')">XSS</a>
<!-- ์ด๋ฒคํธ ์์ฑ -->
<img src="#" onerror="alert('XSS')">
app.use(helmet.xssFilter()); // X-XSS-Protection ๋ณด์ํค๋ ์์ฑ.
XSS ๊ณต๊ฒฉ์ ๋ง๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ ํจ์ฑ ๊ฒ์ฌ, ๊ธด ์ ๋ ฅ ๋ถ๊ฐ
- htmlํ๊ทธ ๋ฌด๋ ฅํ (escape ์ฒ๋ฆฌ)
- encodeURI ํจ์๋ฅผ ์ฌ์ฉํด ์ ๋ ฅ๋ฐ์ URL์ด ์์ ํ์ง ํ์ธ
- html ์์ฑ์ ์ฌ์ฉ์๊ฐ ์๋ฐ์ดํ(")๋ฅผ ๋ฃ์ ์ ์๋์ง ํ์ธ. ๋ํ DB์ ๋ฃ๊ธฐ ์ ๊ฒ์ฌ
- HTTPํค๋๋ก ์ํ
์ถ๊ฐ๋ก, XSS ๊ณต๊ฒฉ์ ์๋ฒฝํ ๋ฐฉ์ดํ๊ธฐ ์ํด์ sanitze-html ๋ชจ๋๋ ๊ฐ์ด ์จ์ฃผ์ด์ผ ํ๋ค.
- helmet์ src๋ href, ์ธ๋ผ์ธ ์๋ฐ์คํฌ๋ฆฝํธ XSS ๊ณต๊ฒฉ์ ๋ง๋ ์ฉ๋๋ก
- sanitize-html๋ <script> ํ๊ทธ ์์ฑ ๊ณต๊ฒฉ์ ๋ง๋ ์ฉ๋๋ก ์ด๋ค.
[NODE / ๋ณด์] ๐ sanitize-html ๋ชจ๋ ์ฌ์ฉ๋ฒ
sanitize-html ๋ชจ๋ sanitize(์๋ ) ์ html์ input ๋๋ textarea ๋๋ ๊ธฐํ๋ฑ๋ฑ์ ์ฌ์ฉ์ ์ ๋ ฅ์ ๋ณด์ ์ด๋ ๋ฌธ์์ด์ ์ ์์, ์น๋ธ๋ผ์ฐ์ ์์ ๋ฌธ์์ด์ด txt๊ฐ ์๋ script ๊ธฐ์ ๋ก ๋ฐ์๋ค์ฌ์ ์๊ธฐ๋ ๋ฌธ์ ๋ฅผ
inpa.tistory.com
ํด๋ฆญ์ฌํน ๋ฐฉ์ด
ํด๋ฆญ์ฌํน์ ์ฌ์ฉ์์ ํด๋ฆญ์ ๊ฐ๋ก์ฑ ๋ค๋ฅธ ๊ฒ์ ๋๋ฅด๊ฒ ํ๋ ๋ฐฉ์์ด๋ค.
์ ํ์ ์ธ X-Frame-Option์ ๋ณด๋ด์ ๋ธ๋ผ์ฐ์ ๊ฐ ๋์ด์ ์ด ํ๋ ์์ ๋ก๋ํ์ง ์๋๋ก ํด ๋์ํ ์ ์๋ค.
// X-Frame-Option
app.use(helmet.frameguard("deny"));
X-Frame-Option
- deny : ํ๋ ์ ๋ด์ ์ฐ๋ฆฌ ์ฌ์ดํธ๋ฅผ ๋ฃ์ง ๋ชปํ๊ฒ ํจ
- sameorigin : ๋ค๋ฅธ ์ฌ๋์ด ํ๋ ์ ๋ด์ ์ฐ๋ฆฌ ์ฌ์ดํธ๋ฅผ ๋ฃ์ง ๋ชปํ์ง๋ง ์ฐ๋ฆฌ ์ฌ์ดํธ์์๋ ํ์ฉ
- allow-from : ์ง์ ํ ์ฌ์ดํธ์์ ํ๋ ์์ ํ์ํ ์ ์์
MIME ์ค๋ํ ์ฐจ๋จ
MIME(Multipurpose Internet Mail Extensions) ํ์ ์ด ์์ ๋, ํน์ ํด๋ผ์ด์ธํธ๊ฐ ํ์ ์ด ์๋ชป ์ค์ ๋๋ค๊ณ ํ๋จํ ์ด๋ค ๋ค๋ฅธ ๊ฒฝ์ฐ์, ๋ธ๋ผ์ฐ์ ๋ค์ MIME ์ค๋ํ(sniffing)์ ์๋ํ ์๋ ์๋๋ฐ, ์ด๋ ๋ฆฌ์์ค๋ฅผ ํ์ด๋ณด๊ณ ์ ํํ MIME ํ์ ์ ์ถ์ธกํ๋ ๊ฒ์ด๋ค.
๋ง์ ๋ธ๋ผ์ฐ์ ์์ ํ์ผ ํ์์ด ์๋ฐ์คํฌ๋ฆฝํธ์ฉ์ด ์๋๋ผ๋ฉด ์คํํ๋๋ก ํ์ฉํ๋๋ฐ, ๋ง์ฝ ๊ต์ฅํ ์ํํ ํ์ผ์ด html๊ณผ ์ ์ฌํ๋ค๋ฉด ์ด ํ์ผ์ html๋ก ํด์ํ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ MIME ์ค๋ํ์ X-Content-Type-Options ํค๋๋ฅผ nosniff๋ผ๋ ์ต์ ์ผ๋ก ์ค์ ํด ์ฐจ๋จํ ์ ์๋ค.
// Helmet์ ์ฌ์ฉํ HTTP ํค๋๋ฅผ ์ค์
app.use(helmet.noSniff());
์ด ๊ธ์ด ์ข์ผ์ จ๋ค๋ฉด ๊ตฌ๋ & ์ข์์
์ฌ๋ฌ๋ถ์ ๊ตฌ๋
๊ณผ ์ข์์๋
์ ์์๊ฒ ํฐ ํ์ด ๋ฉ๋๋ค.