
CSP nonce와 hash는 언제 써야 할까? strict-dynamic까지 한 번에 정리
Content Security Policy(CSP)를 적용할 때 가장 많이 헷갈리는 부분 중 하나가 nonce와 hash를 언제 써야 하느냐다. 결론부터 말하면 서버가 응답마다 HTML을 동적으로 만들 수 있으면 nonce가 유지보수에 유리하고, 정적 파일이나 캐시되는 HTML이라면 hash가 더 잘 맞는다. W3C CSP Level 3, MDN, web.dev 문서는 이 두 방식을 모두 strict CSP의 핵심 수단으로 설명한다.
핵심 차이는 신뢰를 표시하는 방법이다. nonce는 응답마다 새로 만든 임의 값을 Content-Security-Policy 헤더와 <script> 또는 <style> 요소에 같이 넣는 방식이고, hash는 인라인 스크립트나 스타일의 정확한 내용에 대한 SHA-256, SHA-384, SHA-512 해시를 정책에 넣는 방식이다. 여기에 strict-dynamic까지 이해하면 실제 배포 판단이 훨씬 쉬워진다.
nonce는 정확히 무엇을 뜻하나?
MDN과 CSP 명세에 따르면 nonce는 한 번만 쓰는 난수 값이다. 서버는 각 HTTP 응답마다 예측하기 어려운 새 값을 생성하고, 그 값을 CSP 헤더의 'nonce-...' 형태와 허용할 <script>, <style> 요소의 nonce 속성에 같은 값으로 넣는다.
Content-Security-Policy:
script-src 'nonce-rAnd0mBase64Value';
object-src 'none';
base-uri 'none';
이 방식은 서버가 템플릿을 렌더링하는 환경에서 잘 맞는다. 응답을 만들 때마다 새 nonce를 생성해 각 스크립트 태그에 주입할 수 있기 때문이다. web.dev도 서버 렌더링 페이지에서는 nonce 기반 strict CSP를 우선적인 선택지로 설명한다.
hash 기반 CSP는 어떻게 동작하나?
hash 기반 CSP는 인라인 스크립트나 스타일의 내용을 해시해 정책에 넣는다. MDN 기준으로 사용할 수 있는 알고리즘은 sha256, sha384, sha512다. 브라우저는 문서를 받으면 인라인 블록의 내용을 해시하고, 정책에 적힌 값과 일치할 때만 실행을 허용한다.
Content-Security-Policy:
script-src 'sha256-Base64EncodedHashValue';
object-src 'none';
base-uri 'none';
이 방식은 정적으로 배포되는 HTML, 캐시가 중요한 페이지, 빌드 결과물이 고정된 SPA에 잘 맞는다. 대신 스크립트 내용이 공백 하나라도 바뀌면 해시를 다시 계산해야 한다. MDN은 이 때문에 동적 페이지에서는 nonce가 더 관리하기 쉽고, 반대로 정적 페이지에서는 hash가 잘 맞는다고 설명한다.
nonce와 hash 중 무엇을 먼저 고르면 되나?
실무에서는 페이지 생성 방식부터 보면 된다.
- 서버가 응답마다 HTML을 렌더링한다면
nonce가 보통 더 단순하다. - 정적 파일을 CDN에 올리거나 HTML 자체를 오래 캐시해야 한다면
hash가 더 자연스럽다. - 인라인 블록 내용이 자주 바뀌면 hash 유지 비용이 커진다.
- 응답마다 안전한 난수를 생성하고 템플릿에 주입할 수 없다면 nonce 방식은 맞지 않는다.
web.dev는 nonce는 매 응답마다 새 값이 필요하고, hash는 정적 페이지에 더 적합하다고 정리한다. 이 기준은 프레임워크와 무관하게 그대로 적용해도 무리가 적다.
strict-dynamic은 왜 같이 언급되나?
strict-dynamic은 nonce나 hash로 신뢰를 받은 스크립트가 추가로 불러오는 스크립트까지 신뢰를 전파하는 키워드다. MDN과 CSP Level 3는 이 키워드가 있으면 지원 브라우저에서 'self', 호스트 allowlist, 'unsafe-inline' 같은 일부 소스 표현식이 스크립트 로딩 판단에서 무시된다고 설명한다.
Content-Security-Policy:
script-src 'nonce-rAnd0mBase64Value' 'strict-dynamic';
object-src 'none';
base-uri 'none';
이 점 때문에 strict-dynamic은 서드파티 로더 스크립트나 런타임에 다른 스크립트를 붙이는 구조에서 자주 같이 나온다. 다만 W3C와 MDN, web.dev 모두 신뢰된 스크립트가 동적으로 붙이는 URL 자체가 공격자 입력에 오염되면 CSP가 그 부분까지 막아주지는 못한다고 경고한다.
지원 브라우저와 하위 호환은 어떻게 보나?
W3C CSP Level 3와 MDN 설명에 따르면 strict-dynamic은 하위 호환을 고려해 함께 배치할 수 있다. 예를 들어 MDN은 script-src 'unsafe-inline' https: 'nonce-abcdefg' 'strict-dynamic' 같은 정책이 CSP1, CSP2, CSP3 지원 수준에 따라 다르게 해석될 수 있다고 설명한다. 즉 최신 브라우저에서는 nonce와 strict CSP 중심으로 동작하고, 오래된 브라우저에서는 제한적으로 fallback이 쓰인다.
다만 fallback을 넣는 목적은 구형 브라우저 대응이지, 최신 브라우저에서 정책을 느슨하게 만들기 위해서가 아니다. 최신 엔진은 strict-dynamic을 이해하면 그에 맞는 우선순위로 정책을 처리한다.
nonce를 쓸 때 주의할 점은?
- nonce 값은 응답마다 새로 생성해야 한다.
- 예측하기 어려운 난수여야 한다.
- 허용할 요소와 CSP 헤더에 같은 값을 넣어야 한다.
- nonce는
<script>와<style>같은 요소에 적용하는 방식이다.
MDN은 최근 브라우저에서 nonce 값을 단순한 속성 조회로 숨기는 동작도 설명한다. 이는 CSS 선택자 같은 우회 경로로 nonce가 새어 나가는 위험을 줄이기 위한 동작이다.
hash를 쓸 때 주의할 점은?
- 해시는 스크립트나 스타일의 실제 내용과 정확히 일치해야 한다.
- 공백과 줄바꿈 차이도 해시에 영향을 준다.
- 내용이 바뀌면 해시를 다시 계산해 배포해야 한다.
- MDN은 외부 리소스를 해시 기반으로 허용할 때
integrity속성도 필요하다고 설명한다.
따라서 hash 기반 CSP는 빌드 산출물이 고정돼 있고 배포 파이프라인에서 해시 갱신을 자동화할 수 있을 때 특히 깔끔하다.
FAQ
Q. 정적 React나 Vue 배포 페이지라면 무엇이 더 잘 맞나?
HTML이 정적으로 제공되고 캐시를 적극적으로 쓴다면 hash 기반 CSP가 더 자연스럽다. web.dev는 정적 HTML이나 캐시되는 페이지에서 hash 기반 strict CSP를 대표 사례로 든다.
Q. SSR 페이지에서도 hash를 쓸 수 있나?
가능은 하지만, 인라인 블록이 자주 달라지면 해시 재계산 부담이 커진다. 응답을 매번 렌더링하는 구조라면 nonce가 유지보수상 더 단순한 경우가 많다.
Q. CSP만 있으면 XSS를 막았다고 봐도 되나?
아니다. web.dev는 strict CSP를 XSS 완화에 유용한 방어층으로 설명하지만, 입력 검증과 취약점 수정의 대체재로 보지는 않는다. CSP는 방어 심화 수단으로 보는 편이 맞다.
정리
선택 기준은 복잡하지 않다. 동적으로 렌더링되는 HTML이면 nonce, 정적이거나 오래 캐시되는 HTML이면 hash를 먼저 검토하면 된다. 그리고 런타임에 신뢰된 스크립트가 다른 스크립트를 불러오는 구조라면 strict-dynamic이 왜 같이 나오는지까지 이해해야 정책을 올바르게 읽을 수 있다.
결국 CSP에서 중요한 것은 문법을 외우는 것보다 배포 방식과 빌드 방식을 정책과 맞추는 일이다. 응답마다 값을 주입할 수 있는지, 콘텐츠가 얼마나 자주 바뀌는지, 스크립트 로딩이 정적인지 동적인지를 먼저 보면 nonce와 hash 중 어느 쪽이 맞는지 더 빨리 판단할 수 있다.
참고 자료
'프로그래밍 > 서버, DBMS' 카테고리의 다른 글
| Let's Encrypt 인증은 HTTP-01, DNS-01, TLS-ALPN-01 중 무엇을 써야 할까? 선택 기준 정리 (0) | 2026.05.11 |
|---|---|
| Let's Encrypt rate limit은 어떻게 계산될까? 2026 기준 발급 제한과 재시도 방법 정리 (0) | 2026.05.08 |
| 안전하게 키를 보관하려면 무엇을 봐야 할까? KMS 서비스 선택 기준 정리 (0) | 2026.05.05 |
| MySql - DB 저장 위치 변경 & 디스크 변경 (데이터 복사) (0) | 2022.04.08 |
| 리눅스 파일 전송 명령어 : SCP (0) | 2021.12.15 |





