웹보안 헤더 13종 진단 — HTTP 응답 한 줄이 '신뢰'를 만든다
웹사이트를 방문할 때 사람들이 확인하는 건 주소창의 자물쇠 아이콘, 즉 HTTPS 여부다. 그게 '이 사이트는 안전하다'는 감각의 전부인 경우가 많다. 그런데 실제로 보안을 만드는 것들은 훨씬 더 깊은 곳에 있다. 브라우저와 서버가 주고받는 HTTP 응답 헤더(response header) — 사용자 눈에는 전혀 보이지

HSTS·CSP·X-Frame-Options 등 13개 보안 헤더를 자동으로 점검하기까지, 우리가 배운 것들
들어가며 — 눈에 보이지 않는 보안의 증거
웹사이트를 방문할 때 사람들이 확인하는 건 주소창의 자물쇠 아이콘, 즉 HTTPS 여부다. 그게 '이 사이트는 안전하다'는 감각의 전부인 경우가 많다. 그런데 실제로 보안을 만드는 것들은 훨씬 더 깊은 곳에 있다. 브라우저와 서버가 주고받는 HTTP 응답 헤더(response header) — 사용자 눈에는 전혀 보이지 않는 그 한 줄 한 줄이, 방문자를 XSS 공격에서 막아주고, 클릭재킹에서 지키고, HTTPS가 아닌 연결로 흘러가지 않도록 붙잡는다.
문제는 이 헤더들이 있는지, 제대로 설정됐는지를 일반 담당자가 확인하기가 쉽지 않다는 점이다. 개발자조차 매 배포 때마다 "헤더 잘 나가고 있지?"를 꼼꼼히 확인하지 못한다. 그리고 공공 웹사이트는 사정이 더 복잡하다. 외주 개발사가 만들었고, 운영은 내부 담당자가 하며, 보안 점검은 별도 사업으로 가끔 들어오는 구조. 그 사이 어딘가에서 보안 헤더는 조용히 누락되거나 잘못 설정된 채로 서비스된다.
ViewCheck가 13종의 보안 헤더를 자동으로 점검하는 기능을 만든 건, 바로 이 '눈에 보이지 않아서 놓치는' 문제를 풀기 위해서였다. 이 글은 그 과정에서 우리가 배운 것들 — 보안 헤더가 무엇인지, 왜 13종인지, 어떻게 HTTP 요청에서 실측하는지, 그리고 공공 웹에서 실제로 무슨 패턴이 보이는지 — 을 솔직하게 적어보려 한다.
미리 말해두고 싶은 건, 이 글이 "ViewCheck 쓰면 보안이 완벽해집니다"라는 이야기를 하려는 게 아니라는 점이다. 보안 헤더 점검은 전체 웹 보안의 한 층이다. 완전한 보안을 말하기엔 우리도 아직 연구 중인 부분이 훨씬 많다. 다만 그 층 하나를 '자동으로, 근거 있게, 매번' 확인하는 일이 얼마나 중요한지는, 지금부터 차근차근 이야기할 수 있다.

HTTP 응답 헤더란 무엇인가 — 브라우저와 서버의 은밀한 대화
웹페이지를 열면 브라우저는 서버에게 "이 페이지 좀 줘"라고 요청한다(HTTP Request). 서버는 HTML 문서를 돌려주는데, 그 앞에 응답 헤더(HTTP Response Header)라는 줄들이 딸려온다. 사용자는 이 헤더를 절대 보지 못한다. 브라우저가 알아서 읽고, 알아서 처리한다.
예를 들면 이런 식이다.
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'; script-src 'self'
Referrer-Policy: strict-origin-when-cross-origin
맨 위의 200 OK는 "요청 성공"이라는 신호고, Content-Type은 "내려주는 게 HTML이야"라는 선언이다. 그 아래에 나열된 줄들이 바로 보안 헤더들이다. Strict-Transport-Security는 "앞으로 이 사이트는 반드시 HTTPS로만 접속해"라고 브라우저에게 지시한다. X-Frame-Options: DENY는 "이 페이지는 어떤 iframe에도 포함될 수 없어"라고 막는다. Content-Security-Policy는 "이 페이지에서 로드할 수 있는 리소스 출처는 여기야"라고 선언한다.
이 헤더들이 없거나 잘못 설정되면 브라우저는 훨씬 많은 것을 허용한다. 외부 스크립트가 끼어들어도 막지 않고, HTTP로 접속하려는 시도에도 그냥 응한다. 사용자에게는 아무 변화가 없어 보이지만, 공격자 입장에서는 문이 열린 셈이다.
헤더는 서버에서 설정한다. 웹 서버(Nginx, Apache)나 애플리케이션 프레임워크에서 몇 줄만 추가하면 된다. 설정 자체는 어렵지 않다. 그런데도 많은 사이트에서 빠져 있는 이유는 간단하다 — 설정 안 해도 사이트가 일단 돌아가기 때문이다. 동작에는 문제없으니 배포 때 빠뜨려도 티가 나지 않는다. 그래서 자동 점검이 의미를 갖는다.
OWASP(Open Worldwide Application Security Project)의 Secure Headers Project(OSHP)는 이 보안 헤더들에 대한 포괄적인 가이드를 오픈 프로젝트로 운영하고 있다. OSHP의 설명을 빌리자면, "HTTP 헤더는 보안을 강화하는 훌륭한 수단이며 구현도 쉽다. 올바른 HTTP 응답 헤더는 XSS, 클릭재킹, 정보 노출 같은 보안 취약점을 예방하는 데 도움이 된다(HTTP Headers are a great booster for web security with easy implementation. Proper HTTP response headers can help prevent security vulnerabilities like Cross-Site Scripting, Clickjacking, Information disclosure and more)"라고 정리한다(OWASP Secure Headers Project, owasp.org/www-project-secure-headers). 한 줄짜리 헤더가 XSS 공격의 진입로를 막는다 — 이게 보안 헤더의 가치를 가장 잘 요약한 문장이다.
왜 '13종'인가 — ViewCheck가 점검하는 헤더 목록
우리가 13종이라고 말할 때, 이게 어떤 기준으로 나온 숫자인지부터 정리하자.
보안 헤더는 아주 많다. OWASP의 HTTP Security Response Headers Cheat Sheet(cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html)를 보면 수십 개의 헤더가 나열된다. 그 중에서 우리가 공공 웹 환경, 특히 대한민국 전자정부 서비스에서 의미 있게 점검 가능하고, 실제로 누락 시 위험이 커지는 것들을 추려 13종으로 구성했다.
정확히 어떤 것들인지 먼저 나열해 보자.
| 번호 | 헤더 이름 | 주요 기능 |
|---|---|---|
| 1 | Strict-Transport-Security (HSTS) |
HTTPS 강제 접속 지시 |
| 2 | Content-Security-Policy (CSP) |
리소스 로드 출처 제한 |
| 3 | X-Frame-Options |
iframe 삽입 차단(클릭재킹 방어) |
| 4 | X-Content-Type-Options |
MIME 타입 스니핑 차단 |
| 5 | Referrer-Policy |
리퍼러 정보 노출 제어 |
| 6 | Permissions-Policy |
브라우저 기능(카메라·마이크 등) 접근 제어 |
| 7 | Cross-Origin-Resource-Policy (CORP) |
크로스 오리진 리소스 접근 제한 |
| 8 | Cross-Origin-Opener-Policy (COOP) |
창(window) 간 참조 격리 |
| 9 | Cross-Origin-Embedder-Policy (COEP) |
크로스 오리진 임베드 정책 |
| 10 | X-XSS-Protection |
구형 브라우저 XSS 필터 (레거시 헤더) |
| 11 | Cache-Control |
민감 페이지 캐싱 방지 |
| 12 | Clear-Site-Data |
사이트 데이터 초기화 지원 |
| 13 | Set-Cookie 보안 속성 |
HttpOnly / Secure / SameSite 플래그 |
마지막 항목이 조금 다르게 보일 수 있다. Set-Cookie는 응답 헤더 이름이지만, 거기 담긴 HttpOnly, Secure, SameSite 속성값이 보안에서 결정적인 역할을 한다. 쿠키를 어떻게 설정했는지를 별도로 검사하기 때문에, 실질적으로는 세 가지 쿠키 속성까지 포함해 점검하는 셈이다.
이 13종이 전부는 아니다. 보안은 헤더 외에도 TLS 프로토콜 버전, SSL 인증서 상태, 쿠키 설정, 혼합 콘텐츠(mixed content) 등 여러 층으로 이루어진다. ViewCheck는 13종 헤더 외에도 SSL 인증서 만료일, 발급자, TLS 프로토콜 버전, 혼합 콘텐츠 여부 같은 항목을 함께 점검한다. 13종은 그 중 '헤더'에 해당하는 부분을 추린 숫자다.
HSTS — HTTPS를 강제로 기억시키는 지시
첫 번째로 HSTS를 살펴보자. Strict-Transport-Security라는 긴 이름보다 HSTS로 더 많이 불린다.
HSTS의 동작 원리는 단순하다. 브라우저가 서버로부터 이 헤더를 받으면, 지정된 기간(max-age) 동안 그 도메인에 대한 모든 HTTP 요청을 자동으로 HTTPS로 업그레이드한다. 다음부터 사용자가 http://로 접속하려 해도 브라우저가 알아서 https://로 바꿔버린다.
MDN Web Docs는 HSTS의 역할을 이렇게 설명한다. "HTTP Strict-Transport-Security 응답 헤더(HSTS)는 브라우저에게 이 호스트에는 HTTPS로만 접근해야 하며, 향후 HTTP로 접근하려는 시도는 자동으로 HTTPS로 업그레이드해야 한다고 알린다. 또한 향후 이 호스트로의 연결에서 브라우저는 잘못된 인증서처럼 보안 연결 오류를 우회하는 것을 허용하지 않는다(The HTTP Strict-Transport-Security response header informs browsers that the host should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be upgraded to HTTPS. Additionally, on future connections to the host, the browser will not allow the user to bypass secure connection errors, such as an invalid certificate)"(MDN Web Docs, Strict-Transport-Security header, developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security).
HSTS가 없으면 어떤 일이 생길까. 사용자가 http://example.go.kr을 입력하면 서버는 https://example.go.kr로 리다이렉트한다. 그런데 이 첫 번째 HTTP 요청이 가는 순간 — 리다이렉트가 일어나기 전 그 찰나에 — 공격자가 끼어들 수 있다. HTTP 통신은 암호화가 없으니, 중간자(MITM) 공격자가 그 요청을 낚아채 가짜 HTTPS 사이트로 돌리는 것이다. HSTS가 있으면 브라우저가 애초에 HTTP 요청 자체를 만들지 않는다. 첫 번째 HTTP 연결의 취약점을 원천 차단하는 셈이다.
단, MDN이 주의점도 명확히 짚는다. "HSTS의 한 가지 약점은 브라우저가 호스트에 적어도 한 번 보안 연결을 맺고 Strict-Transport-Security 헤더를 받기 전까지는 효력을 발휘하지 않는다는 점이다(One weakness of HSTS is that it does not take effect until the browser has made at least one secure connection to the host and received the Strict-Transport-Security header)". 처음 접속하는 사용자는 이 보호를 못 받는다는 뜻이다. 이 한계를 보완하는 게 'HSTS Preload'라는 개념인데, 브라우저 제조사의 목록에 도메인을 미리 등록해 두는 것이다. 공공 웹에서도 적극 활용할 만한 방법이지만, 한국 공공기관 사이트 중 Preload까지 적용한 곳은 아직 많지 않다.
ViewCheck는 HSTS 헤더의 존재 여부뿐 아니라 max-age 값, includeSubDomains 포함 여부, preload 지시어 여부까지 함께 확인한다. "HSTS 있음"과 "HSTS 제대로 있음"은 다르기 때문이다.
CSP — 어디서 온 코드를 허용할 것인가
Content-Security-Policy, 줄여서 CSP는 보안 헤더 중 가장 강력하고 가장 복잡한 것이다.
CSP의 핵심 개념은 '화이트리스트(허용 목록)'다. 이 페이지에서 스크립트를 로드할 수 있는 출처는 여기야, 이미지는 여기만, 폰트는 여기만 — 이렇게 선언하는 것이다. 선언되지 않은 출처에서 온 콘텐츠는 브라우저가 실행을 막는다. XSS(Cross-Site Scripting) 공격의 핵심 경로가 바로 '외부 스크립트 삽입'이기 때문에, CSP는 이 경로 자체를 막는 가장 강력한 방어선 역할을 한다.
MDN의 설명을 들어보자. "HTTP Content-Security-Policy 응답 헤더는 웹사이트 관리자가 특정 페이지에서 사용자 에이전트가 로드할 수 있는 리소스를 제어할 수 있게 해준다. 이는 크로스 사이트 스크립팅 공격에 대한 방어를 돕는다(The HTTP Content-Security-Policy response header allows website administrators to control resources the user agent is allowed to load for a given page. This helps guard against cross-site scripting attacks)"(MDN Web Docs, Content-Security-Policy, developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy).
OWASP의 CSP Cheat Sheet는 더 구체적인 조언을 더한다. "안전하지 않은(unsafe-inline, unsafe-eval) 사용을 피하고, 엄격한 CSP 정책의 역할은 클래식 저장형, 반사형, 일부 DOM XSS 공격에 대한 방어이며, CSP를 구현하려는 모든 팀의 최적 목표가 돼야 한다(A strict policy's role is to protect against classical stored, reflected, and some of the DOM XSS attacks and should be the optimal goal of any team trying to implement CSP)"(OWASP, Content Security Policy Cheat Sheet, cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html).
그런데 실제 공공 웹에서 CSP는 가장 구현이 어려운 헤더이기도 하다. 제대로 된 CSP를 작성하려면, 해당 페이지에서 로드하는 모든 서드파티 스크립트, 폰트, 이미지, 스타일의 출처를 파악해야 한다. 공공 웹은 통계 수집, 챗봇, 지도 API, 외부 광고 등 다양한 서드파티를 사용하는 경우가 많아, 이걸 전부 허용 목록에 담으면서도 보안을 유지하는 게 말처럼 쉽지 않다.
그래서 ViewCheck가 CSP를 점검할 때 단순히 "CSP 있음/없음"만 보는 게 아니라, unsafe-inline이나 unsafe-eval 같은 위험한 키워드가 포함됐는지, default-src가 설정됐는지 같은 내용도 함께 살핀다. CSP가 있어도 default-src 'unsafe-inline' 'unsafe-eval' 이라면, 사실상 보안 효과가 거의 없기 때문이다.
CSP 작성이 너무 어렵다면 현실적인 시작점이 있다. MDN은 "Report-Only 헤더를 먼저 사용해 실제 위반 사항을 수집한 뒤, 그 데이터를 바탕으로 실제 CSP를 작성하는" 방식을 권장한다(MDN Web Docs, Content Security Policy implementation). 처음부터 완벽한 CSP를 쓰려 하지 않고, 위반 보고 먼저 수집해 점진적으로 조여가는 접근이다. 우리도 공공 웹 담당자들에게 이 방식을 설명할 때 현실적인 시작점으로 자주 언급한다.
X-Frame-Options — 클릭재킹의 문을 닫는다
X-Frame-Options는 CSP보다 단순하지만, 방어하는 공격 유형이 매우 구체적이다. 바로 **클릭재킹(Clickjacking)**이다.
OWASP는 클릭재킹을 이렇게 정의한다. "클릭재킹, 또는 'UI 리드레스 공격'은 공격자가 여러 개의 투명하거나 불투명한 레이어를 사용해 사용자가 최상위 페이지를 클릭하려 할 때, 다른 페이지의 버튼이나 링크를 클릭하도록 속이는 것이다. 공격자는 사용자의 클릭을 납치해 다른 페이지로 라우팅한다(Clickjacking, also known as a 'UI redress attack', is when an attacker uses multiple transparent or opaque layers to trick a user into clicking on a button or link on another page when they were intending to click on the top level page, with the attacker 'hijacking' clicks meant for their page and routing them to another page)"(OWASP, Clickjacking, owasp.org/www-community/attacks/Clickjacking).
구체적인 공격 시나리오는 이렇다. 공격자가 악의적인 사이트를 만든다. 그 사이트에 피해 사이트(예: 공공기관 로그인 페이지)를 투명한 iframe으로 올린다. 사용자는 악의적인 사이트에서 "클릭하면 경품"이라는 버튼을 클릭하려 하지만, 실제로는 그 아래 투명하게 깔린 공공기관 로그인 버튼을 누르게 된다. 본인도 모르게 로그인 동작이 실행된다.
X-Frame-Options: DENY는 브라우저에게 "이 페이지는 어떤 iframe에도 들어갈 수 없어"라고 선언한다. SAMEORIGIN은 "같은 출처의 iframe에만 허용"이다. 이 헤더가 있으면 공격자가 피해 사이트를 iframe으로 올리는 것 자체가 차단된다.
현대적인 접근에서는 CSP의 frame-ancestors 지시어가 X-Frame-Options의 상위 호환으로 권장된다. MDN은 이렇게 정리한다. "frame-ancestors 지시어는 X-Frame-Options를 대체하며, 두 가지를 모두 설정하면 frame-ancestors를 지원하지 않는 브라우저에서의 임베딩도 방지할 수 있다(The frame-ancestors directive is a replacement for X-Frame-Options, and by setting both, you can prevent embedding in browsers that don't support frame-ancestors)"(MDN Web Docs, Clickjacking defense, developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking).
실무 조언: 공공 웹에서 가장 빠르게 적용할 수 있는 보안 헤더 중 하나가 바로 X-Frame-Options다. 설정이 간단하고, 사이트 기능에 영향을 주지 않으며(iframe을 의도적으로 허용해야 하는 경우 제외), 효과는 명확하다. ViewCheck 분석에서 이 헤더가 없는 사이트를 보면, 특히 로그인 페이지가 포함된 서비스라면 위험도 높음으로 플래깅한다.
X-Content-Type-Options — MIME 스니핑의 함정
이름이 다소 낯선 X-Content-Type-Options는 **MIME 타입 스니핑(MIME type sniffing)**이라는 공격을 막는다.
MIME 타입은 서버가 "이건 JavaScript야", "이건 이미지야"라고 선언하는 방식이다. 그런데 과거 일부 브라우저는 서버가 선언한 MIME 타입이 내용과 다를 경우, 내용을 직접 분석해 타입을 추론(스니핑)했다. 공격자는 이 점을 악용해 서버가 "이건 텍스트야"라고 선언했지만 실제로는 JavaScript 코드를 담은 파일을 서빙하고, 브라우저가 그걸 스크립트로 실행하게 만들었다.
MDN은 이 헤더의 역할을 명확히 설명한다. "HTTP X-Content-Type-Options 응답 헤더는 Content-Type 헤더에 선언된 MIME 타입을 존중해 변경하지 않아야 한다고 알려준다. 이는 MIME 타입 스니핑을 피할 수 있게 해준다(The HTTP X-Content-Type-Options response header indicates that the MIME types advertised in the Content-Type headers should be respected and not changed, allowing you to avoid MIME type sniffing)"(MDN Web Docs, X-Content-Type-Options header, developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options).
값은 nosniff 하나뿐이다. X-Content-Type-Options: nosniff 한 줄이면 된다. 설정이 이렇게 단순한데도 적용하지 않은 사이트가 생각보다 많다. 아마 존재 자체를 모르는 경우가 대부분일 것이다. 자동 점검이 이런 '모르기 때문에 빠뜨리는' 헤더를 짚어주는 데 가장 효과적이다.

Referrer-Policy와 Permissions-Policy — 정보를 덜 흘리는 설계
보안은 때로 "뭘 하지 않느냐"의 문제이기도 하다. Referrer-Policy와 Permissions-Policy는 그 관점에서 이해하면 쉽다.
Referrer-Policy — 어디서 왔는지를 얼마나 알려줄 것인가
사용자가 A 페이지에서 링크를 클릭해 B 페이지로 이동할 때, 브라우저는 기본적으로 B 서버에게 "이 사용자는 A에서 왔어요"라는 정보(Referer 헤더)를 보낸다. 문제는 A 페이지의 URL이 민감한 정보를 포함할 때다. 예를 들어 https://example.go.kr/mypage?token=abc123에서 클릭이 일어났다면, 이 전체 URL이 외부로 흘러나간다. 세션 토큰이나 사용자 ID가 포함된 URL이 외부 서비스로 전달되는 것이다.
Referrer-Policy는 이 정보를 얼마나, 어떤 상황에 보낼지 제어한다. MDN이 권장하는 기본값은 strict-origin-when-cross-origin이다. 이 값은 "같은 출처 요청에는 전체 URL을 보내고, 다른 출처 요청에는 출처(scheme + host)만 보내며, HTTP에서 HTTPS로 이동 시에는 아무것도 보내지 않는다"는 정책이다. 이렇게 하면 내부 분석은 가능하면서 외부 서비스로 민감 정보가 흘러나가는 걸 막을 수 있다(MDN Web Docs, Referrer-Policy header, developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy).
공공 웹에서 이 헤더가 중요한 이유가 있다. 민원 처리 시스템, 신청 화면, 로그인 세션이 있는 페이지 — 이런 곳의 URL에는 의도치 않게 사용자 식별 정보가 포함될 수 있다. 그 URL이 외부로 흘러나가면 개인정보 관련 문제로 이어질 수 있다. Referrer-Policy는 그 방어선이다.
Permissions-Policy — 필요 없는 기능은 처음부터 막는다
Permissions-Policy(과거 Feature-Policy)는 브라우저의 강력한 기능들에 대한 접근 제어를 선언한다. 카메라, 마이크, 지오로케이션(GPS), 전체화면, 자동 재생 같은 기능들이 대상이다.
MDN은 이 헤더의 역할을 이렇게 설명한다. "Permissions-Policy 헤더는 현재 문서와 포함된 콘텐츠에서 어떤 기능이나 API가 사용될 수 있는지 없는지를 제어하는 메커니즘을 제공한다"(MDN Web Docs, Permissions-Policy header, developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy). 보안 전문가들의 조언은 단순하다 — "사이트가 사용하지 않는 기능은 처음부터 막아라." 사용하지 않는 카메라·마이크 접근을 선언으로 차단하면, 그 기능을 이용하려는 악성 스크립트도 실행되지 않는다. 공격 표면을 줄이는 것이다.
공공 웹 대부분은 카메라나 마이크를 사용하지 않는다. 그렇다면 Permissions-Policy: camera=(), microphone=(), geolocation=()처럼 선언해 아예 막아두는 게 맞다. 이 헤더를 설정한 공공 사이트는 아직 많지 않다. ViewCheck가 이 헤더의 부재를 잡아내는 이유다.
쿠키 보안 속성 — HttpOnly, Secure, SameSite
쿠키는 웹 서비스의 인증 상태를 유지하는 핵심 수단이다. 그래서 쿠키가 탈취되면 곧바로 세션 하이재킹(Session Hijacking) — 로그인한 척하기 — 으로 이어진다. 이를 막는 세 가지 속성이 있다.
HttpOnly: HttpOnly 플래그가 붙은 쿠키는 JavaScript에서 접근할 수 없다. XSS 공격을 통해 악성 스크립트가 심어지더라도, document.cookie로 쿠키를 읽어갈 수 없다. XSS 취약점이 있다고 해도 쿠키 탈취로 이어지는 경로를 차단한다. 세션 쿠키에는 반드시 이 플래그가 있어야 한다.
Secure: Secure 플래그가 붙은 쿠키는 HTTPS 연결에서만 전송된다. HTTP로 요청이 가는 상황에서 쿠키가 평문으로 전달되는 것을 막는다. HSTS와 함께 쓰면 이중 보호가 된다.
SameSite: 이 속성은 다른 출처에서 온 요청에 쿠키를 포함할지를 제어한다. SameSite=Strict는 같은 사이트 요청에만 쿠키를 보내고, SameSite=Lax는 안전한 메서드(GET)의 크로스 사이트 요청에는 허용한다. CSRF(Cross-Site Request Forgery) 공격을 차단하는 데 효과적이다. 과거 CSRF 방어의 주력이었던 CSRF 토큰과 함께 쓰면 훨씬 강력해진다.
ViewCheck는 사이트에서 발급되는 쿠키들의 Set-Cookie 헤더를 수집해, 각 쿠키에 이 세 가지 플래그가 적절히 설정됐는지 확인한다. 세션 쿠키처럼 민감한 쿠키에 HttpOnly와 Secure가 빠져 있으면, 위험도 높음으로 분류한다.
크로스 오리진 정책 3형제 — CORP·COOP·COEP
최근 몇 년 사이 등장한 비교적 새로운 헤더들이 있다. Cross-Origin-Resource-Policy(CORP), Cross-Origin-Opener-Policy(COOP), Cross-Origin-Embedder-Policy(COEP)다. 셋 다 '크로스 오리진' 관련이고, 함께 묶어서 이해하면 편하다.
이 헤더들이 등장한 배경은 스펙터(Spectre) CPU 취약점이다. 2018년 발견된 이 취약점은 같은 브라우저 프로세스에 올라온 다른 사이트의 메모리를 엿볼 수 있다는 것이었다. 브라우저 제조사들은 이를 막기 위해 '사이트 격리(Site Isolation)'를 강화했고, 그 일환으로 개발자가 명시적으로 선언할 수 있는 크로스 오리진 정책 헤더들이 만들어졌다.
- CORP: "이 리소스는 같은 오리진(또는 같은 사이트)에서만 로드할 수 있어"라고 선언. 다른 사이트가 이 서버의 이미지나 API 응답을 로드하는 것을 차단.
- COOP: 브라우저 창(window) 간의 참조를 격리. 다른 출처에서 이 페이지의
window객체에 접근하는 것을 막아, 탐색 공격(navigation attack)을 방지. - COEP: 페이지에 포함되는 모든 리소스가 크로스 오리진 정책을 갖추도록 요구. COOP와 함께 설정하면
SharedArrayBuffer같은 고성능 기능을 안전하게 쓸 수 있는 '교차 출처 격리(cross-origin isolated)' 상태가 된다.
이 세 헤더는 공공 웹에서의 현실적인 도입 난이도가 HSTS나 X-Content-Type-Options보다 높다. 특히 COEP는 페이지에 포함된 서드파티 리소스 전체가 CORP를 지원해야 하기 때문에, 서드파티 의존도가 높은 공공 포털에서 당장 적용하기 어려울 수 있다. ViewCheck는 이 헤더들의 부재를 기록하되, 위험도 분류 시 다른 헤더들과 달리 '권장'으로 표시한다. '없으면 바로 위험'이 아니라 '있으면 더 안전한' 층이기 때문이다.
Mozilla HTTP Observatory — 외부 기준점으로 삼기
보안 헤더를 자동 점검하는 잘 알려진 도구로 Mozilla의 HTTP Observatory가 있다. 이 도구의 역사와 최근 변화가 흥미롭다.
2016년에 출시된 Mozilla Observatory는 사이트 URL을 입력하면 HTTP 헤더 준수 여부를 검사하고 A+에서 F까지 점수를 부여하는 서비스였다. MDN의 블로그는 이 도구가 4,700만 회 이상의 스캔을 통해 690만 개 이상의 사이트에 인사이트를 제공했다고 기록하고 있다(MDN Web Docs, "Introducing the MDN HTTP Observatory", developer.mozilla.org/en-US/blog/mdn-http-observatory-launch). 2024년 10월, 이전의 Mozilla Observatory(HTTP + TLS + SSH Observatory 포함)가 종료되고, HTTP Observatory가 MDN의 새 홈으로 이전됐다. 지금은 developer.mozilla.org/en-US/observatory에서 무료로 사용할 수 있다.
ViewCheck가 관심 갖는 건 이 도구의 채점 방식이다. Observatory는 모든 사이트를 100점 기본값에서 시작해 헤더 누락이나 잘못된 설정으로 감점하고, 좋은 설정에는 보너스를 부여하는 방식을 쓴다. 이 방식의 장점은 "어떤 헤더가 없어서 얼마나 감점됐는지"가 명확하다는 점이다. ViewCheck도 비슷한 방식으로, 각 헤더의 부재와 잘못된 설정에 대한 영향도를 달리 적용해 신뢰성 점수를 산출한다.
흥미로운 건, 우리가 분석한 공공 웹사이트들을 동일 기준으로 비교해 보면 점수 분포가 상당히 넓다는 점이다. HSTS와 X-Content-Type-Options 정도는 갖춘 곳이 많지만, CSP를 제대로 구현한 곳은 적고, Referrer-Policy나 Permissions-Policy까지 신경 쓴 곳은 더 드물다. 이 분포를 보여주는 게 ViewCheck의 '벤치마킹' 기능이기도 하다.

HTTP 요청으로 직접 측정한다 — ViewCheck의 실측 방법
이제 ViewCheck가 실제로 어떻게 이 헤더들을 확인하는지를 이야기해 보자.
보안 헤더는 HTML 소스를 보는 것으로는 알 수 없다. HTML 파일 안에 있는 게 아니라, 서버와 브라우저 사이의 HTTP 통신에서 오가는 것이기 때문이다. 확인하려면 실제 HTTP 요청을 서버에 보내고, 돌아온 응답의 헤더를 직접 읽어야 한다.
ViewCheck의 크롤러(Playwright 기반)는 페이지를 실제 브라우저로 방문하면서 서버의 HTTP 응답을 가로채 헤더를 수집한다. 페이지 방문 시 브라우저가 받는 응답 헤더를 그대로 분석하는 것이다. 이 과정에서 여러 페이지를 순차적으로 방문하기 때문에, 메인 페이지뿐 아니라 로그인 페이지, 신청 페이지 등 다른 하위 페이지에서도 헤더를 확인한다. 중요한 건 각 페이지마다 헤더 설정이 다를 수 있다는 점이다.
예를 들어 메인 페이지에는 HSTS가 있지만 로그인 페이지에는 없을 수도 있다. 또는 CSP가 메인에서는 엄격하지만 어드민 페이지에서는 느슨하게 설정돼 있을 수도 있다. ViewCheck는 이런 페이지별 차이를 다중 페이지 분석으로 잡아낸다. 단일 페이지만 보면 놓칠 수 있는 문제들이다.
행안부 「전자정부 웹사이트 품질관리 지침」(행정안전부고시 제2025-46호, 2025년 6월 25일 시행)은 신뢰성 영역에서 "웹사이트에 공개된 정보의 오류, 개인정보 노출 여부, 게시판 등에 대한 점검 및 개선을 통해 신뢰성을 확보"하도록 규정한다(행정안전부, mois.go.kr). 보안 헤더는 이 '신뢰성 확보'의 기술적 기반 중 하나다. HTTP 응답이 올바른 보안 선언을 포함하는지는, HTTP 요청을 실제로 보내야만 알 수 있다. DOM 기반 분석으로는 이 정보를 얻을 수 없다는 게 ViewCheck 아키텍처에서 보안 점검이 '수집 엔진' 담당인 이유다.
공공 웹에서 자주 보이는 패턴 — 실제 분석에서 발견한 것들
수많은 공공 웹사이트를 분석하면서 우리가 발견한 패턴들이 있다. 단, 여기서 특정 사이트를 지목하거나 수치를 확정적으로 제시하는 건 적절하지 않다. 우리가 분석한 시점과 조건이 다르고, 사이트는 계속 변하기 때문이다. 대신 반복적으로 관찰되는 패턴을 유형으로 정리해 본다.
패턴 1: HTTPS만 되면 됐다는 인식
HSTS가 없는 사이트가 예상보다 많다. HTTPS 인증서는 갱신하고 적용했지만, HTTP 요청을 자동으로 HTTPS로 전환하는 HSTS 설정은 빠져 있는 경우다. "HTTPS로 리다이렉트하잖아요"라는 대답이 돌아오는데, 리다이렉트는 첫 번째 HTTP 요청이 만들어진 이후에 일어난다. 그 찰나가 취약한 순간이다.
패턴 2: X-Content-Type-Options의 부재
설정이 가장 단순한 헤더 중 하나인데도 없는 경우가 꽤 있다. nosniff 한 줄이면 되는데, 존재 자체를 모르는 경우가 많아 보인다. 자동 점검이 가장 즉각적인 개선을 이끌어내는 헤더이기도 하다.
패턴 3: CSP가 없거나, 있어도 느슨함
CSP를 아예 적용하지 않은 사이트도 많고, 있더라도 'unsafe-inline'을 허용한 경우가 적지 않다. 서드파티 스크립트나 인라인 스타일을 대거 사용하는 공공 포털 특성상, 처음부터 엄격한 CSP를 작성하기 어렵다는 현실이 반영된 것이다. 이걸 "CSP 있으니 OK" vs "CSP가 실제로 보호하는가"로 구분해서 봐야 한다.
패턴 4: 쿠키에 HttpOnly·Secure 누락
세션 관리를 위한 쿠키에 HttpOnly나 Secure 플래그가 없는 경우를 적지 않게 본다. 이건 특히 구형 레거시 시스템에서 많이 나타난다. 외주 개발사가 만든 당시에 이 설정을 빠뜨렸고, 그 이후 바꾸지 않은 것이다.
패턴 5: Referrer-Policy 미설정
민원 신청 페이지나 마이페이지처럼 URL에 사용자 정보가 포함될 수 있는 곳에서도 Referrer-Policy가 없는 경우가 많다. 이 헤더는 상대적으로 최근에 중요성이 부각된 것이기도 하고, 눈에 보이는 피해가 즉각 나타나지 않아 우선순위에서 밀리는 것 같다.
이 패턴들이 말해주는 건 하나다 — 한 번 만들고 끝낸 보안 설정은 시간이 지날수록 구멍이 생긴다. 처음 개발 당시에 했어도, 서버를 교체하거나 설정을 옮기는 과정에서 헤더가 날아가기도 한다. 그래서 자동화된 주기적 점검이 중요하다.

보안 헤더 점검과 행안부 7대 품질 영역 — 신뢰성의 위치
행정안전부 「전자정부 웹사이트 품질관리 지침」은 품질을 7대 영역으로 분류한다. 호환성, 접근성, 개방성, 접속성, 편의성, 효율성, 그리고 신뢰성. 보안 헤더는 이 중 신뢰성 영역의 핵심 지표다.
그런데 흥미로운 건, 이 7개 영역이 측정 방법 면에서 크게 두 가지로 갈린다는 점이다. 하나는 DOM(페이지 구조)을 분석해 알 수 있는 것들 — 접근성(ARIA 속성, 구조), 호환성(HTML 표준 준수), 컴포넌트 존재 여부 같은 것들이다. 다른 하나는 실제 HTTP 요청을 보내봐야 알 수 있는 것들 — 보안 헤더, 응답 시간, SSL 인증서, 리소스 크기 같은 것들이다.
보안 헤더는 후자에 속한다. DOM을 아무리 분석해도 Strict-Transport-Security 헤더가 있는지 알 수 없다. HTML 소스 안에 없으니까. 실제 HTTP 요청을 보내고, 돌아온 응답 헤더를 읽어야만 알 수 있다.
ViewCheck의 아키텍처에서 이 영역이 수집·통합 엔진의 몫인 이유가 여기에 있다. Playwright가 실제 브라우저를 띄워 페이지를 방문하면서 HTTP 응답을 받는 순간, 그 응답 헤더들이 수집된다. DOM 기반의 KRDSrule 엔진이 하는 일과는 레이어가 다른 것이다. 어느 쪽도 다른 쪽의 일을 대신할 수 없고, 그래서 분업이 필요하다.
이 맥락에서 보면, 공공 웹 품질 점검에서 '보안 헤더를 확인했느냐'는 질문은 사실 '7대 품질 영역을 제대로 점검했느냐'의 핵심 부분이다. DOM만 보는 점검은 신뢰성 영역을 아예 건드리지 못한다. 이게 ViewCheck가 HTTP 실측을 포함하는 이유이기도 하다.
왜 다중 페이지 점검이 중요한가 — 보안 헤더의 일관성 문제
보안 헤더는 메인 페이지에만 있어서는 의미가 없다. 사용자가 실제로 민감한 작업을 하는 건 로그인 페이지, 신청서 작성 페이지, 결제 페이지 같은 '깊은 곳'이기 때문이다. 그리고 이 깊은 곳의 헤더 설정이 메인과 다른 경우가 생각보다 많다.
왜 이런 일이 생길까. 공공 웹 개발의 현실 때문이다. 메인 페이지는 프론트 개발사가, 민원 신청 시스템은 다른 개발사가, 로그인은 또 다른 SSO 솔루션이 담당하는 경우가 많다. 각 시스템마다 서버 설정이 다르고, 보안 헤더를 누가 챙겼는지도 다르다. 결과적으로 메인에서 확인한 보안 헤더 상태와 로그인 페이지의 상태가 전혀 다를 수 있다.
ViewCheck의 다중 페이지 분석은 이 불일치를 잡아낸다. 메인 페이지 점검 결과만으로 사이트 전체의 보안 상태를 대표하지 않는다. 로그인, 신청, 검색, 상세 페이지 등 다양한 유형의 페이지를 실제로 방문하고, 각 페이지에서 보안 헤더 상태를 확인한다. 그래서 "메인은 통과인데 로그인 페이지에 HSTS 없음"이라는 결과가 나올 수 있다.
이 점이 단일 페이지 점검과의 가장 큰 차이다. 보안 관점에서 사이트를 평가할 때, 가장 약한 고리가 전체 보안 수준을 결정한다. 메인이 A+여도 로그인 페이지가 F라면, 그 서비스의 보안은 F에 가깝다.
X-XSS-Protection — 레거시 헤더를 어떻게 볼 것인가
13종 중 조금 독특한 위치에 있는 헤더가 X-XSS-Protection이다. 이 헤더는 Internet Explorer 8에서 처음 도입된 XSS 필터를 제어하는 것으로, 모던 브라우저에서는 이미 더 강력한 CSP가 그 역할을 대체했다. Chrome은 이 헤더 지원을 2019년에 종료했다. 그럼에도 우리가 이 헤더를 점검 항목에 유지하는 이유가 있다.
첫째, 잘못 설정된 X-XSS-Protection이 오히려 위험할 수 있기 때문이다. X-XSS-Protection: 1; mode=block 같은 설정은 그냥 두는 것보다 낫지만, 과거 연구에서 일부 브라우저의 XSS 필터 자체가 오히려 공격 벡터로 악용될 수 있다는 사례가 발견됐다. 그래서 OWASP는 이 헤더를 0(비활성화)으로 설정하고 CSP로 대체하길 권장한다.
둘째, 공공 웹은 레거시 브라우저를 고려해야 하는 경우가 있다. 기업 내부 시스템이나 구형 환경을 써야 하는 사용자가 있는 공공 서비스에서, 오래된 브라우저 지원이 아직 요구사항인 경우가 있다. 그런 환경에서는 이 헤더의 존재 자체를 확인하고 올바르게 설정하는 게 의미 있다.
OWASP의 HTTP Headers Cheat Sheet는 현대 권장 사항을 명확히 정리한다. "Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Permissions-Policy, Referrer-Policy는 현재 권장 보안 헤더이며, 이 헤더들을 적절히 설정하면 다양한 공격에 대한 방어를 강화할 수 있다"(OWASP, HTTP Security Response Headers Cheat Sheet, cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html). X-XSS-Protection은 이 목록에 없다. 레거시로 취급되는 것이다. ViewCheck는 이 헤더를 점검 목록에 포함하되, 위험도 분류에서 현재 유효한 헤더들과 다르게 취급한다.
Cache-Control — 민감한 정보가 캐시에 남는 것을 막는다
Cache-Control은 성능 최적화 헤더로 알려져 있지만, 보안 관점에서도 중요하다. 특히 공공 서비스에서 민원 정보, 개인 정보, 신청 결과처럼 민감한 내용을 담은 페이지가 브라우저 캐시나 프록시 캐시에 저장되는 걸 막아야 할 때 필요하다.
예를 들어 공공 도서관의 공용 PC에서 어떤 시민이 민원 신청 결과 페이지를 보고 나갔다. 그 다음 사용자가 같은 브라우저에서 '뒤로 가기'를 눌렀더니 이전 사람의 민원 결과가 캐시에서 보였다면? 이건 단순한 불편이 아니라 개인정보 유출이다.
Cache-Control: no-store, no-cache, must-revalidate 같은 설정이 있으면 이런 민감 페이지의 캐싱을 막는다. ViewCheck는 민원 신청, 로그인, 개인 정보 표시 같은 민감 페이지 유형에서 Cache-Control 설정을 특히 꼼꼼히 확인한다.
Clear-Site-Data — 로그아웃 시 데이터를 확실히 지운다
Clear-Site-Data는 서버가 브라우저에게 "이 사이트와 관련된 저장 데이터를 지워줘"라고 명령하는 헤더다. 주로 로그아웃 응답에서 사용한다. "cache", "cookies", "storage" 등의 값을 지정하면 해당 유형의 데이터가 삭제된다.
이 헤더의 가치는 로그아웃의 완결성에 있다. 로그아웃을 했는데도 쿠키가 남아 있거나, 로컬 스토리지의 세션 데이터가 남아 있으면, 다음 사용자(또는 동일 PC의 다른 사용자)가 그 데이터를 악용할 수 있다. Clear-Site-Data는 로그아웃 시 이 데이터를 서버 지시로 한 번에 정리하는 방법이다.
공공 PC 환경을 고려하면 이 헤더의 중요성이 더 높아진다. 민원 담당자나 시민이 공용 컴퓨터에서 공공 서비스를 이용하는 상황이 아직 많기 때문이다. ViewCheck는 로그아웃 응답에 이 헤더가 있는지를 확인 항목으로 포함한다. 단, 로그아웃 흐름을 테스트하는 건 자동화가 쉽지 않아 일부는 '확인 필요'로 남겨두는 경우도 있다.
보안 점수 산정 방식 — "있음"과 "제대로 있음" 사이
ViewCheck가 13종 보안 헤더를 점검한 결과를 '보안 점수'로 변환할 때, 우리가 고민한 게 있다. 단순히 헤더 개수를 세는 방식으로는 실제 보안 수준을 반영하기 어렵다는 것이다.
예를 들어 CSP가 있는데 default-src *; script-src * 처럼 모든 출처를 허용한다면, 있으나 마나인 CSP다. X-Frame-Options가 있는데 ALLOWALL이라면(이런 값은 표준이 아니지만 실제로 잘못 적용된 경우를 본 적 있다), 오히려 클릭재킹에 취약하다는 걸 알리는 셈이다. 헤더의 '존재'와 헤더의 '효과' 사이에는 차이가 있다.
그래서 ViewCheck는 헤더의 유무뿐 아니라 주요 속성값의 안전성도 함께 평가한다. CSP에서는 unsafe-inline, unsafe-eval 같은 위험 키워드의 포함 여부를, HSTS에서는 max-age 값이 적절한지(일반적으로 1년 이상 권장)를, 쿠키에서는 세 가지 보안 플래그의 존재를 확인한다. 그리고 이를 가중치를 달리해 점수에 반영한다.
보안에 절대적인 점수는 없다. 어떤 사이트도 "100점이라 완벽하다"고 말하기 어렵다. 새로운 취약점이 발견되고, 브라우저 지원이 바뀌며, 공격 방법은 계속 진화한다. ViewCheck의 보안 점수는 "현 시점에서 점검 가능한 범위에서 어느 수준인가"를 나타내는 지표이지, "이 사이트는 안전합니다"라는 인증이 아니다. 이 점은 항상 명확히 하고 싶다.
담당자 관점에서 본 보안 헤더 — 실무 시나리오
추상적인 이야기를 구체적으로 풀어보자. 공공기관 웹 담당자가 실제로 마주하는 상황을 가정해 본다.
외주 개발사로부터 새로운 민원 신청 시스템을 인수받았다. 납품 전 체크리스트에는 "SSL 인증서 적용"과 "접근성 검수"가 있었고, 개발사는 두 가지 모두 완료했다고 보고했다. 담당자는 사이트를 열어보니 HTTPS 자물쇠가 보이고, 화면도 잘 나온다. OK 사인을 낸다.
그런데 몇 달 후 보안 감사에서 지적이 나온다. HSTS 미설정, CSP 없음, 세션 쿠키에 HttpOnly 플래그 없음. 개발사는 "그런 거 체크리스트에 없었는데요"라고 한다. 담당자는 "그게 뭔지 몰랐는데요"라고 한다.
이 시나리오에서 ViewCheck가 인수 전 점검 단계에서 사용됐다면 어떻게 됐을까. URL 한 줄을 넣어 분석을 돌렸다면, HSTS 없음, CSP 없음, HttpOnly 누락이 전부 잡혔을 것이다. 담당자가 "이 항목들은 왜 미통과야?"라고 채팅으로 물으면, 근거가 되는 OWASP 가이드와 MDN 문서 링크가 참조패널에 뜬다. 그걸 개발사에게 전달한다. "이 규칙에 따르면 이 헤더가 필요한데, 납품 전에 추가해 주세요."
물론 ViewCheck를 썼다고 해서 모든 보안 문제가 사라지는 건 아니다. 헤더 점검은 보안의 한 레이어일 뿐이다. 그래도 "몰라서 빠뜨리는" 문제를 줄이는 데는 자동 점검이 확실한 효과를 낸다. 그리고 근거 문서를 함께 제시해 주기 때문에, 단순히 "이거 없어요"가 아니라 "이거 없으면 이런 공격에 취약해요"라는 맥락으로 개발사와 소통할 수 있다.
보안 헤더와 KRDS 846규칙의 관계
보안 헤더 점검은 KRDS 846규칙과 어떤 관계에 있을까. 이 둘을 연결하는 게 처음에는 낯설 수 있다. KRDS는 디자인 시스템이고, 보안 헤더는 HTTP 통신의 영역이니까.
그런데 ViewCheck가 다루는 '서비스 패턴(SP)' 카테고리에는 로그인, 인증, 세션 관련 규칙들이 포함되어 있다. 서비스 패턴은 KRDS의 서비스 흐름 — "사용자가 이 서비스를 어떻게 경험하는가" — 을 정의한다. 그 안에는 "로그인 후 세션 처리가 적절한가", "로그아웃 시 데이터가 정리되는가" 같은 항목이 있을 수 있다. 보안 헤더는 이 항목들의 '기술적 구현'에 해당하는 것들이다.
한편, 보안 헤더 13종 자체는 KRDS 규칙의 직접적인 판정 대상이기보다는, 7대 품질 영역 중 신뢰성 영역의 독립적인 점검 항목으로 더 정확하게 위치한다. 행안부 지침이 '신뢰성'을 7대 영역의 하나로 명시하고, 그 안에서 보안 관련 요구를 포함하기 때문이다.
ViewCheck의 LLM 분석에서 '보안' 카드가 별도로 나타나는 이유가 여기에 있다. 846개 KRDS 규칙 판정 결과와 함께, 13종 보안 헤더 점검 결과가 분리된 관점으로 제시된다. 사용자 입장에서는 "KRDS 준수 상태"와 "신뢰성·보안 상태"를 각각 볼 수 있다. 둘 다 "공공 웹의 질"을 구성하는 층이지만, 측정 방법이 다르기 때문에 별도로 보는 게 맞다.
진단 이후의 여정 — 헤더 설정은 시작일 뿐
ViewCheck가 보안 헤더 점검 결과를 제시한 이후, 실제로 어떤 일이 일어나야 할까. 진단은 시작이지 끝이 아니다.
첫 단계는 우선순위 정렬이다. 13종 헤더 모두 한꺼번에 추가하는 건 현실적으로 어렵다. 어떤 헤더가 없을 때 위험이 가장 크고, 어떤 헤더가 설정이 가장 간단한가를 교차해서 보면 우선순위가 보인다. ViewCheck의 오류 리스트와 개선 로드맵 카드가 이 우선순위를 제안한다.
두 번째 단계는 구현 방법 파악이다. 헤더를 어디서 설정하는지는 서버 환경마다 다르다. Nginx라면 server 블록이나 location 블록에, Apache라면 .htaccess나 httpd.conf에, Node.js Express 앱이라면 헬멧(Helmet.js) 같은 미들웨어로. 어떤 환경을 쓰는지에 따라 방법이 달라진다. ViewCheck는 근거 문서(OWASP Cheat Sheet, MDN 문서)를 참조패널로 제공하지만, 특정 서버 환경의 설정 방법까지 직접 생성하는 건 현재 범위 밖이다.
세 번째 단계는 테스트 후 배포다. 특히 CSP처럼 잘못 설정하면 사이트가 깨질 수 있는 헤더는 반드시 테스트 환경에서 먼저 검증해야 한다. 위에서 언급한 'Report-Only 먼저' 접근이 여기서 빛을 발한다.
네 번째 단계는 재점검과 모니터링이다. 한 번 설정한 보안 헤더가 영원히 유지된다는 보장이 없다. 서버 교체, 설정 파일 업데이트, CDN 변경 등의 과정에서 헤더가 사라질 수 있다. 주기적인 재점검이 필요하다. ViewCheck는 이런 '정기 점검' 사용 방식도 지원한다.

자동 점검의 한계 — 우리가 솔직히 말할 것들
보안 헤더 자동 점검이 아무리 잘 돼도, 해결할 수 없는 영역들이 있다. 솔직히 말하는 게 맞다고 생각한다.
첫째, 헤더가 있다고 공격이 불가능한 건 아니다. CSP가 있어도 잘못 구성됐거나, HSTS가 있어도 첫 방문자는 보호받지 못한다. 보안 헤더는 여러 방어층 중 하나일 뿐이다. SQL Injection, 서버 사이드 취약점, 인증 로직 결함 같은 건 헤더 점검으로 잡을 수 없다.
둘째, 우리의 점검은 외부에서 보이는 응답 헤더를 기준으로 한다. 실제 공격은 더 다양한 경로를 통해 들어온다. 인증된 사용자의 세션, 내부 API, 관리자 페이지의 취약점 등은 외부 자동 점검으로는 닿지 않는다.
셋째, CSP 같은 헤더의 '질'을 완전히 평가하기는 어렵다. unsafe-inline 포함 여부는 확인할 수 있지만, CSP 정책이 실제로 얼마나 엄격한지, 어떤 공격을 어느 수준으로 막는지를 자동으로 완벽하게 평가하는 건 현재 우리 능력의 범위 밖이다. 보안 전문가의 직접 검토가 필요한 영역이다.
넷째, 우리가 잡지 못하는 헤더 설정 오류도 있다. 모든 엣지 케이스를 다 다루기엔 헤더 설정의 조합이 너무 다양하다. 기준이 되는 OWASP Cheat Sheet와 MDN 문서를 따르고 있지만, 완전하다고 말하기는 어렵다.
이 한계들을 나열하는 건, "그러니 ViewCheck는 쓸모없어요"를 말하려는 게 아니다. "자동 점검이 할 수 있는 것과 할 수 없는 것을 구분하고, 각각에 맞는 대응을 해야 한다"는 이야기다. 자동 점검은 '알아야 하는데 몰라서 빠뜨리는' 헤더를 잡고, '검토했어야 하는데 바빠서 못 한' 주기적 확인을 대신한다. 그 이상도 이하도 아니다.
KISA와 공공기관 보안 가이드의 맥락
한국에서 공공기관 웹 보안에 대한 가이드는 한국인터넷진흥원(KISA)에서도 다룬다. KISA는 「웹서버 보안 강화 안내서」 등을 통해 공공기관의 웹 보안 강화 방안을 안내하고 있다. 여기에는 HTTP 보안 헤더 관련 설정 사항도 포함된다.
2026년에는 「주요정보통신기반시설 기술적 취약점 분석·평가 방법 안내서」가 업데이트됐으며, 이는 공공기관을 포함한 주요 인프라 운영자의 보안 기준을 제시한다. 보안 헤더 설정은 이런 기술적 취약점 분석·평가의 점검 항목에 포함된다.
한편 국가법령정보센터에는 「행정기관 및 공공기관 정보시스템 구축·운영 지침」(행안부고시 제2025-1호, 2025.1.2 시행)도 있다. 이 지침은 공공기관 정보시스템의 구축과 운영 전반에 걸친 기준을 다루며, 보안 설정 관련 요구사항도 포함한다.
이런 국내 가이드와 OWASP, MDN 같은 국제 기준이 지향하는 방향은 같다 — HTTP 보안 헤더를 적절히 설정해 사이트 방문자를 보호하라는 것이다. 구체적인 헤더 이름과 권장값에서 차이가 있을 수 있지만, 핵심 원칙은 일치한다. ViewCheck는 국내 공공 웹 환경에 맞추면서도 국제 기준인 OWASP와 MDN을 함께 참조해 점검 기준을 설계했다.
SecurityHeaders.com과 같은 도구와의 비교
커뮤니티에서 잘 알려진 보안 헤더 점검 도구로 SecurityHeaders.com이 있다. Scott Helme가 만들었고, 현재 Snyk이 관리하는 이 서비스는 URL을 입력하면 HTTP 보안 헤더를 검사하고 A+에서 F까지 등급을 부여한다.
이 도구와 ViewCheck의 차이는 크게 두 가지다.
첫째, 통합 관점이다. SecurityHeaders.com은 HTTP 헤더만 본다. ViewCheck는 보안 헤더를 KRDS 846규칙 판정, 접근성, 반응형, SEO, 성능 등 24개 분석 관점의 일부로 통합해서 본다. "보안 헤더는 좋지만 접근성이 낮다"처럼 여러 관점을 한 화면에서 비교할 수 있다.
둘째, 다중 페이지 관점이다. SecurityHeaders.com은 입력한 URL 하나만 점검한다. ViewCheck는 해당 사이트의 여러 페이지를 순회하며 헤더 일관성을 확인한다. 로그인 페이지, 신청 페이지 등 다른 하위 페이지의 상태를 종합적으로 본다.
참고로, Snyk은 SecurityHeaders.com API의 2026년 4월 종료를 발표했다(무료 웹 스캐너는 유지 예정). 이런 도구들의 변화와 무관하게, 우리는 직접 HTTP 요청을 측정하는 방식을 ViewCheck 내에서 유지할 계획이다.
실제 사용 사례 — 대화로 보안 헤더 결과 해석하기
ViewCheck LLM 분석에서 보안 헤더 결과가 어떻게 대화로 전달되는지를 가상의 흐름으로 보여주자.
사용자가 채팅창에 URL을 입력한다. 분석이 돌아간다. '보안' 카드가 등장한다. 거기에는 이런 내용이 요약된다.
■ 보안 점검 결과 요약
—————————————
✅ HTTPS / SSL 인증서: 유효 (만료 270일 남음)
✅ Strict-Transport-Security (HSTS): 있음 (max-age=31536000)
⚠️ Content-Security-Policy: 없음 ← 위험도: 높음
⚠️ X-Frame-Options: 없음 ← 위험도: 중간
✅ X-Content-Type-Options: nosniff
⚠️ Referrer-Policy: 없음 ← 위험도: 낮음
⚠️ Permissions-Policy: 없음 ← 위험도: 낮음
✅ 세션 쿠키 HttpOnly: 있음
⚠️ 세션 쿠키 SameSite: 없음 ← 위험도: 중간
사용자가 묻는다. "Content-Security-Policy가 왜 위험도 높음이야?"
대화형 응답이 나온다. "CSP가 없으면 페이지에서 허용되는 리소스 출처에 제한이 없습니다. 악의적인 스크립트가 외부에서 로드되는 XSS(크로스 사이트 스크립팅) 공격에 취약할 수 있습니다. OWASP에서는 CSP를 XSS 방어의 핵심 수단으로 권장하며, 최소한 default-src 'self' 설정을 시작점으로 제안합니다." 참조패널에는 OWASP CSP Cheat Sheet 링크가 뜬다.
이 흐름이 "있음/없음"을 단순히 나열하는 것과 다른 점은, 왜 중요한지, 어떻게 시작할 수 있는지까지 이어지는 대화가 된다는 것이다. 결과를 읽는 데서 끝나지 않고, 다음 행동으로 연결되는 정보를 건넨다.
보안 헤더 점검과 공개적 신뢰 구축
마지막으로 조금 다른 각도에서 이야기해 보고 싶다. 보안 헤더는 기술적 보호 수단이지만, 동시에 '이 서비스를 만드는 사람들이 얼마나 신경 써서 운영하는가'를 보여주는 신호이기도 하다.
사이트에 접속한 모든 사람이 보안 헤더를 직접 확인하지는 않는다. 하지만 보안 연구자, 기술 담당자, 감사 기관은 본다. SecurityHeaders.com이나 MDN Observatory 같은 도구로 1분이면 확인할 수 있다. 공공 서비스에 대한 신뢰는 수많은 작은 것들이 쌓여 만들어지는데, 보안 헤더가 그 중 하나다.
"이 서비스는 HTTPS는 있는데 HSTS가 없네. CSP도 없네. 쿠키 설정도 엉성하네." — 이런 결과를 본 사람은 그 서비스의 운영 방식 전반에 의문을 갖게 된다. 반대로 잘 설정된 보안 헤더들이 나오면, "이 사이트는 꼼꼼히 관리되는구나"라는 인상을 준다.
공공 서비스의 신뢰는 결국 시민과의 약속이다. 개인정보를 다루고, 민원을 처리하고, 세금과 복지 정보를 보여주는 서비스가 기본적인 보안 설정을 갖추는 것 — 이건 기술적 요구사항이기 이전에 그 서비스를 사용하는 시민에 대한 예의 같은 것이다.
우리가 ViewCheck를 통해 이 영역을 들여다보는 이유도 거기에 있다. "이 공공 서비스가 기본적인 보안 약속을 지키고 있는가"를 자동으로, 근거 있게, 반복적으로 확인할 수 있는 수단을 만드는 것. 그 도구가 완벽하지 않더라도, 없는 것보다는 낫고, 있으면 개선이 시작된다.
마무리 — 눈에 보이지 않는 것을 보는 일
HTTP 응답 헤더는 사용자 눈에 보이지 않는다. 그런데 그 보이지 않는 곳에서, 브라우저가 방문자를 보호할 준비가 돼 있는지 아닌지가 결정된다. HSTS는 첫 연결의 취약점을 닫고, CSP는 외부 스크립트의 진입을 막고, X-Frame-Options는 클릭재킹의 문을 닫는다. 세션 쿠키의 HttpOnly는 XSS로부터 세션을 지키고, SameSite는 CSRF를 막는다.
이걸 매 배포마다, 매 서버 교체마다, 매 시스템 전환마다 사람이 손으로 확인하기는 현실적으로 어렵다. 그래서 자동화가 필요하고, ViewCheck가 이 13종 점검을 URL 한 줄에서 시작하게 만든 이유가 여기에 있다.
물론 아직 연구 중인 것도 많다. CSP 설정의 '질'을 더 정교하게 평가하는 방법, 공공 웹 환경의 복잡한 서드파티 의존성 속에서 현실적인 CSP 작성을 돕는 기능, 다중 페이지에서 헤더 불일치를 더 정밀하게 잡아내는 방법 — 이런 것들은 지금도 실험 중이다.
다음 편에서는 웹 성능 — Core Web Vitals(LCP, CLS, FCP, INP, TTFB)를 ViewCheck가 어떻게 측정하고 해석하는지를 다룰 예정이다. 성능은 보안과는 또 다른 레이어에서 사이트의 신뢰성과 편의성을 구성하는 영역이다.
눈에 보이지 않는 것들이 서비스의 신뢰를 만든다. 그 보이지 않는 것들을 보는 일 — 그게 우리가 하고 있는 연구다.

참고문헌
본문에 인용한 모든 출처는 작성 시점(2026년 6월)에 실재 여부를 WebSearch로 직접 검증했다. 국내 공식 기준과 국제 기관·개발자 문서를 함께 수록한다.
국내 — 공식 기준 / 정책자료
- 행정안전부, 「전자정부 웹사이트 품질관리 지침」(행정안전부고시 제2025-46호, 2025. 6. 25. 일부개정). 국가법령정보센터. https://www.law.go.kr/행정규칙/전자정부웹사이트품질관리지침
- 행정안전부, 「전자정부 웹사이트 품질관리 지침」 일부개정 및 「전자정부 웹사이트 품질관리 가이드」 수정본 안내(고시 제2025-46호, 2025.6). https://www.mois.go.kr/frt/bbs/type001/commonSelectBoardArticle.do?bbsId=BBSMSTR_000000000016&nttId=118636
- 한국인터넷진흥원(KISA), 「웹서버 보안 강화 안내서」. https://www.kisa.or.kr/2060204
- 한국인터넷진흥원(KISA), 기술안내서 가이드 — 소프트웨어 보안약점 진단가이드 등. https://www.kisa.or.kr/2060204/form?postSeq=9&page=1
해외 — 보안 표준 / 개발자 문서
- OWASP Secure Headers Project (OSHP), 공식 프로젝트 페이지. Open Worldwide Application Security Project. https://owasp.org/www-project-secure-headers/
- OWASP, "HTTP Security Response Headers Cheat Sheet". OWASP Cheat Sheet Series. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
- OWASP, "Content Security Policy Cheat Sheet". OWASP Cheat Sheet Series. https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html
- OWASP, "Clickjacking Defense Cheat Sheet". OWASP Cheat Sheet Series. https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html
- OWASP, "HTTP Strict Transport Security Cheat Sheet". OWASP Cheat Sheet Series. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html
- OWASP, "Clickjacking", OWASP Community Page. https://owasp.org/www-community/attacks/Clickjacking
- MDN Web Docs, "Content-Security-Policy (CSP) header". Mozilla. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy
- MDN Web Docs, "Strict-Transport-Security header". Mozilla. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security
- MDN Web Docs, "X-Frame-Options header". Mozilla. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options
- MDN Web Docs, "X-Content-Type-Options header". Mozilla. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options
- MDN Web Docs, "Referrer-Policy header". Mozilla. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
- MDN Web Docs, "Permissions-Policy header". Mozilla. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy
- MDN Web Docs, "Clickjacking — Security". Mozilla. https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking
- MDN Web Docs, "HTTP Header Security Test — HTTP Observatory". Mozilla. https://developer.mozilla.org/en-US/observatory
- MDN Web Docs, "Introducing the MDN HTTP Observatory". Mozilla Blog. https://developer.mozilla.org/en-US/blog/mdn-http-observatory-launch/

관련 글
ViewCheck LLM 분석 리포트 읽는 법 — 점수·등급·근거·우선순위 해석에서 행동까지
분석이 끝나고 화면에 숫자가 떴다. 종합 74점. KRDS 846규칙 중 289개 통과, 228개 미통과, 329개 해당없음. 접근성 68점. 보안 91점. 위반 매트릭스에는 빨간 칸과 노란 칸이 뒤섞여 있다. 그런데 막상 이 결과를 받아 든 담당자의 표정이 묘하다. 뭔가 많이 나왔는데, 무엇을 먼저 봐야 할지 모르겠다.
AI 진단, 어디까지 믿어야 하나 — ViewCheck를 활용하는 사람을 위한 정직한 가이드
2026년 어느 날, 공공기관의 웹 담당자 한 명이 AI 분석 결과를 출력해 상사에게 내밀었다고 상상해 보자. "AI가 분석했더니 우리 사이트 KRDS 준수율이 68점입니다." 상사는 고개를 끄덕이며 "그럼 개선 계획 짜세요"라고 했다. 담당자는 돌아가 개발사에 수정 목록을 전달했다. 문제는, 그 68점이 무엇을 근거로
분석 → 개선 → 재진단, 공공웹 품질을 '점수로 관리'한다는 것 — ViewCheck LLM 분석 활용
공공 웹사이트 품질 진단을 맡은 실무 담당자들과 이야기하다 보면, 어느 지점에서 공통된 표정이 나온다. "진단은 했어요. 그런데 그다음은 어떻게 해야 하죠?" 도구를 돌리면 문제 목록이 나온다. 때로는 수백 개, 때로는 수십 페이지짜리 보고서가 나온다. 그리고 거기서 멈춘다. 문제를 고쳤는지 어떻게 확인하지? 개선 후에
