이 글은 Cloudflare에서 제공하는 웹사이트에 보안 HTTP 헤더를 설정하는 과정을 상세히 안내합니다. Cloudflare Workers를 활용하여 XSS, 클릭재킹, MIMI 스니핑, 교차 사이트 스크립팅과 같은 다양한 보안 취약점으로부터 웹사이트를 안전하게 보호할 수 있습니다. OWASP(Open Web Application Security Project)는 이러한 보안 헤더 적용을 권장하고 있습니다. 오와스프.
이전에는 Apache, Nginx, IIS 같은 웹 서버에서 헤더를 설정하는 방법을 다루었지만, 이제는 Cloudflare를 사용하는 경우 Cloudflare Workers를 통해 HTTP 응답 헤더를 손쉽게 제어할 수 있습니다. Cloudflare Workers는 JavaScript, C, C++, Rust 등으로 작성된 코드를 서버리스 환경에서 실행할 수 있는 강력한 플랫폼입니다. Cloudflare의 전 세계 200개 이상의 데이터 센터에 배포되어 전역적으로 빠른 속도를 제공합니다.
Cloudflare Workers를 통한 보안 헤더 구현은 매우 간편하고 유연합니다. 특정 하위 도메인이나 URI에만 헤더를 적용하거나, 정규식을 이용하여 더욱 세밀하게 설정할 수 있습니다. 일치하는 패턴을 정의하여 적용 범위를 정할 수 있습니다.
본 예시에서는 Scott Helme의 코드를 기반으로 진행하겠습니다.
시작하기
- 먼저 Cloudflare에 로그인하고 Workers 대시보드로 이동합니다 (직접 링크).
- GitHub에서
worker.js
코드를 복사하여 Cloudflare Worker의 스크립트 편집기에 붙여넣습니다.
const securityHeaders = { "Content-Security-Policy": "upgrade-insecure-requests", "Strict-Transport-Security": "max-age=1000", "X-Xss-Protection": "1; mode=block", "X-Frame-Options": "DENY", "X-Content-Type-Options": "nosniff", "Referrer-Policy": "strict-origin-when-cross-origin" }, sanitiseHeaders = { Server: "" }, removeHeaders = [ "Public-Key-Pins", "X-Powered-By", "X-AspNet-Version" ]; async function addHeaders(req) { const response = await fetch(req), newHeaders = new Headers(response.headers), setHeaders = Object.assign({}, securityHeaders, sanitiseHeaders); if (newHeaders.has("Content-Type") && !newHeaders.get("Content-Type").includes("text/html")) { return new Response(response.body, { status: response.status, statusText: response.statusText, headers: newHeaders }); } Object.keys(setHeaders).forEach(name => newHeaders.set(name, setHeaders[name])); removeHeaders.forEach(name => newHeaders.delete(name)); return new Response(response.body, { status: response.status, statusText: response.statusText, headers: newHeaders }); } addEventListener("fetch", event => event.respondWith(addHeaders(event.request)));
아직 저장하지 마세요. 다음 헤더들은 필요에 따라 조정할 수 있습니다.
- Content-Security-Policy: 애플리케이션의 정책을 설정해야 하는 경우 여기서 설정할 수 있습니다.
예를 들어, 여러 URL에서 iFrame을 통해 콘텐츠를 가져와야 하는 경우 다음과 같이 frame-ancestors
를 사용할 수 있습니다.
"Content-Security-Policy" : "frame-ancestors 'self' gf.dev koreantech.org.com",
위와 같이 설정하면 gf.dev
, koreantech.org.com
, 그리고 자신의 사이트에서 콘텐츠를 로드할 수 있습니다.
- X-Frame-Options: iFrame을 사용하여 동일한 사이트 내의 일부 페이지에 콘텐츠를 표시하려면
SAMEORIGIN
으로 변경할 수 있습니다.
"X-Frame-Options": "SAMEORIGIN",
- Server: 여기서 서버 헤더를 삭제하거나 원하는 값으로 변경할 수 있습니다.
"Server" : "koreantech.org Server",
- RemoveHeaders: 정보 유출 위험을 줄이기 위해 특정 헤더를 제거할 수 있습니다.
let removeHeaders = [ "Public-Key-Pins", "X-Powered-By", "X-AspNet-Version", ]
- 새 헤더 추가: 사용자 정의 헤더가 필요한 경우,
securityHeaders
섹션에 추가할 수 있습니다.
let securityHeaders = { "Content-Security-Policy" : "frame-ancestors 'self' gf.dev koreantech.org.com", "Strict-Transport-Security" : "max-age=1000", "X-Xss-Protection" : "1; mode=block", "X-Frame-Options" : "SAMEORIGIN", "X-Content-Type-Options" : "nosniff", "Referrer-Policy" : "strict-origin-when-cross-origin", "Custom-Header" : "Success", }
필요한 모든 헤더를 구성한 후, Worker 이름을 지정하고 ‘저장 및 배포’ 버튼을 클릭합니다.
이제 Worker가 준비되었습니다. 다음 단계는 헤더를 적용할 웹사이트에 Worker를 연결하는 것입니다.
- Cloudflare 대시보드에서 해당 웹사이트를 선택합니다.
- ‘Workers’ 탭으로 이동하여 ‘경로 추가’를 클릭합니다.
- 적용할 URL 패턴을 입력합니다 (정규식 사용 가능).
- 생성한 Worker를 선택하고 저장합니다.
모든 설정이 완료되었습니다. 이제 웹사이트에 보안 헤더가 적용된 것을 확인할 수 있습니다.
다음은 Chrome 개발자 도구에서 확인한 헤더 정보입니다. HTTP 헤더 테스팅 도구를 활용하여 검증할 수도 있습니다.
Server 헤더가 변경되지 않는 이유는 Cloudflare에서 이를 무시하는 것으로 보입니다.
전체 구현 과정은 약 15분 정도 소요되며, Apache나 Nginx처럼 웹 서버를 재시작하거나 다운타임을 발생시킬 필요가 없습니다. 프로덕션 환경에 적용하기 전에 테스트 환경이나 특정 페이지에 먼저 적용하여 결과를 확인하는 것이 좋습니다.
정말 멋지죠!
코드 제공에 감사드립니다. Scott.