본문 바로가기
일반IT/IT보안

⛑️ 내 Express 서버에 안전모 씌우기: Helmet 미들웨어 A to Z

by gasbugs 2025. 10. 5.

안녕하세요, Node.js 와 Express로 열심히 서버를 만들고 계신 개발자 여러분! 기능 구현에 집중하다 보면 자칫 '보안'이라는 중요한 숙제를 놓치기 쉽습니다. 하지만 우리가 만든 소중한 서비스와 사용자의 데이터를 지키기 위해 보안은 선택이 아닌 필수죠.

"보안... 어렵고 복잡할 것 같아... 어디서부터 시작해야 하지? 🤔"

 

이런 고민을 하시는 분들을 위해, 단 몇 줄의 코드로 내 서버의 보안 레벨을 확 끌어올릴 수 있는 마법 같은 도구, Helmet을 소개합니다! 지금부터 저와 함께 Express 서버에 튼튼한 안전모를 씌워보시죠!

 

 

Helmet, 너는 누구냐? 🛡️

Helmet은 Express 애플리케이션을 위한 보안 미들웨어의 '종합 선물 세트'입니다. 웹 애플리케이션의 여러 보안 취약점은 서버가 브라우저에게 보내는 HTTP 응답 헤더(HTTP Response Headers)를 제대로 설정하는 것만으로도 상당수 예방할 수 있습니다.

 

Helmet은 바로 이 보안 관련 HTTP 헤더 15개를 자동으로 설정해 주는 역할을 합니다.

 

개발자가 보안 헤더의 종류와 값을 일일이 외우고 설정하는 수고를 덜어주고, 간단한 설치와 적용만으로 기본적인 보안 기반을 단단하게 다져주는 고마운 친구인 셈이죠.

코드 분석: Helmet 보안 기능 톺아보기

아래 코드는 Helmet의 주요 기능을 사용하여 서버 보안을 설정하는 좋은 예시입니다. 각 라인이 어떤 역할을 하는지 하나씩 자세히 파헤쳐 보겠습니다.

// Express 서버 기술 스택 정보 숨기기
app.disable("x-powered-by");

// 클릭재킹 방지
app.use(helmet.frameguard()); 

// 민감한 정보 클라이언트 캐싱 방지
app.use(helmet.noCache());

// 콘텐츠 보안 정책(CSP) 설정
app.use(helmet.contentSecurityPolicy()); 

// HTTPS 통신 강제 (HSTS)
app.use(helmet.hsts());

// MIME 타입 스니핑 방지
app.use(nosniff());

1. app.disable("x-powered-by");

  • 역할: 🕵️‍♂️ 서버 정보 숨기기
  • 설정 헤더: X-Powered-By 헤더 제거

Express로 서버를 만들면, 기본적으로 HTTP 응답 헤더에 X-Powered-By: Express 라는 정보가 포함됩니다. 이는 "이 서버는 Express 기술로 만들어졌어요!"라고 광고하는 것과 같습니다. 공격자는 이 정보를 보고 Express의 알려진 취약점을 이용해 공격을 시도할 수 있습니다. 이 한 줄의 코드는 불필요한 정보 노출을 막아 공격의 빌미를 주지 않는, 보안의 가장 첫걸음입니다.


2. app.use(helmet.frameguard());

  • 역할: 🖱️💥 클릭재킹(Clickjacking) 방어
  • 설정 헤더: X-Frame-Options: SAMEORIGIN

클릭재킹이란, 공격자가 자신의 악성 사이트에 투명한 <iframe>을 씌우고 그 아래에 우리가 만든 정상적인 사이트를 로드하는 공격입니다. 사용자는 악성 사이트의 버튼(ex: "경품 당첨!")을 누르는 줄 알지만, 실제로는 보이지 않는 우리 사이트의 버튼(ex: "회원 탈퇴")을 누르게 되는 무서운 공격이죠.

 

helmet.frameguard()는 X-Frame-Options 헤더를 설정하여, 우리 웹 페이지가 다른 도메인의 <iframe> 안에서 렌더링되는 것을 막아줍니다. 내 사이트는 오직 내 도메인 안에서만 보여야 안전하겠죠?


3. app.use(helmet.noCache());

  • 역할: 📄❌💾 민감 정보 캐싱 방지
  • 설정 헤더: Cache-Control, Pragma, Expires 등

만약 사용자가 공용 PC에서 은행 사이트에 로그인했다고 상상해보세요. 브라우저가 계좌 정보가 담긴 페이지를 캐시(임시 저장)해버린다면, 다음 사람이 브라우저 기록을 통해 민감한 정보를 훔쳐볼 수도 있습니다.

 

helmet.noCache()는 브라우저가 중요한 정보를 디스크에 저장하지 않도록 관련 캐시 제어 헤더들을 설정하여 이런 위험을 방지합니다.


4. app.use(helmet.contentSecurityPolicy()); (CSP)

  • 역할: 📜✅ XSS(Cross-Site Scripting) 등 콘텐츠 주입 공격 방어
  • 설정 헤더: Content-Security-Policy

XSS는 공격자가 웹사이트에 악성 스크립트를 삽입하여 다른 사용자의 쿠키를 훔치거나 정보를 탈취하는 공격입니다. CSP는 우리 웹사이트에서 로드하는 모든 리소스(스크립트, 스타일시트, 이미지 등)의 출처를 서버가 직접 통제하는 '화이트리스트' 정책입니다.

helmet.contentSecurityPolicy()를 사용하면, "우리 사이트는 https://my-domain.com에서 온 스크립트만 허용하고, 이미지는 https://my-cdn.com에서만 가져와!" 와 같이 매우 엄격한 규칙을 설정할 수 있습니다. 허가되지 않은 출처의 리소스는 브라우저가 아예 로드를 차단해버리므로 XSS 공격을 매우 효과적으로 막을 수 있습니다. (참고: 실제 운영 환경에서는 helmet.contentSecurityPolicy({ directives: { ... } }) 와 같이 서비스에 맞는 세부 정책을 설정해야 합니다.)


5. app.use(helmet.hsts());

  • 역할: 🔒 HTTPS 통신 강제
  • 설정 헤더: Strict-Transport-Security

사용자가 실수로 http://로 접속을 시도하거나, 공격자가 중간에서 HTTPS 연결을 HTTP로 다운그레이드 시키려고 할 때를 대비한 보안 장치입니다. hsts()는 Strict-Transport-Security 헤더를 설정하여 브라우저에게 "이 사이트는 앞으로 무조건 HTTPS로만 접속해야 해!"라고 명령합니다. 한 번이라도 HTTPS로 접속한 이후에는, 브라우저가 알아서 모든 HTTP 요청을 HTTPS로 바꾸어 통신하게 됩니다.


6. app.use(nosniff());

  • 역할: 👃🚫 MIME 타입 스니핑 방지
  • 설정 헤더: X-Content-Type-Options: nosniff

서버는 응답을 보낼 때 Content-Type 헤더를 통해 "이 파일은 이미지(image/png)야" 또는 "이건 자바스크립트(application/javascript)야" 라고 파일 종류를 알려줍니다. 하지만 일부 브라우저는 이 정보를 무시하고 파일 내용을 직접 '킁킁거리며(sniffing)' 추측해서 타입을 결정하려고 합니다.

 

공격자가 이를 악용해 실행 가능한 스크립트 파일을 텍스트 파일인 척 업로드했을 때, 브라우저가 속아서 스크립트를 실행해버릴 수 있습니다. nosniff()는 X-Content-Type-Options 헤더를 nosniff로 설정하여, 브라우저가 서버가 알려준 Content-Type을 묻지도 따지지도 말고 그대로 따르도록 강제합니다.

 

마무리하며

지금까지 Helmet 미들웨어를 통해 Express 서버의 보안을 강화하는 여러 방법을 알아보았습니다. 보시다시피, 각 기능은 특정 보안 위협에 대응하는 명확한 목적을 가지고 있으며, 단 몇 줄의 코드로 우리 서버에 겹겹의 보호막을 씌울 수 있습니다.

 

물론 Helmet이 모든 보안을 책임져 주는 만능 해결사는 아닙니다. 데이터베이스 쿼리 시의 SQL 인젝션 방어, 안전한 비밀번호 관리 등 개발자가 직접 신경 써야 할 부분은 여전히 많습니다.

 

하지만 Helmet을 사용하는 것은 내 Express 서버 보안의 가장 중요하고 확실한 첫걸음입니다. 지금 바로 여러분의 프로젝트에 Helmet 안전모를 씌워주세요! 👷‍♀️👷‍♂️


Express, Nodejs, Helmet, 보안, 웹보안, 미들웨어, 서버개발, Clickjacking, XSS