본문으로 건너뛰기 Helmet Complete Guide

Helmet Complete Guide

Helmet Complete Guide

이 글의 핵심

Helmet helps secure Express apps by setting various HTTP headers. It's a collection of 15 smaller middleware functions that set security-related headers.

Introduction

Helmet helps secure Express apps by setting HTTP security headers. It’s not a silver bullet, but it provides an important layer of defense against common attacks.

Why Helmet?

Without Helmet, your Express app exposes information and is vulnerable:

// Without Helmet
app.get('/', (req, res) => {
  res.send('Hello');
});

// Response headers:
// X-Powered-By: Express (exposes framework)
// (missing security headers)

With Helmet:

app.use(helmet());

// Response headers now include:
// Content-Security-Policy: ...
// X-DNS-Prefetch-Control: off
// X-Frame-Options: SAMEORIGIN
// Strict-Transport-Security: ...
// X-Content-Type-Options: nosniff
// (and more!)

1. Installation

npm install helmet

2. Basic Usage

const express = require('express');
const helmet = require('helmet');

const app = express();

// Use Helmet with defaults
app.use(helmet());

app.get('/', (req, res) => {
  res.send('Hello, secure world!');
});

app.listen(3000);

3. What Helmet Does

Helmet is a collection of 15 smaller middleware:

app.use(helmet());

// Equivalent to:
app.use(helmet.contentSecurityPolicy());
app.use(helmet.crossOriginEmbedderPolicy());
app.use(helmet.crossOriginOpenerPolicy());
app.use(helmet.crossOriginResourcePolicy());
app.use(helmet.dnsPrefetchControl());
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());

4. Content Security Policy (CSP)

CSP prevents XSS attacks by controlling which resources can be loaded:

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
    styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
    imgSrc: ["'self'", "data:", "https:"],
    fontSrc: ["'self'", "https://fonts.gstatic.com"],
    connectSrc: ["'self'", "https://api.example.com"],
    frameSrc: ["'none'"],
    objectSrc: ["'none'"],
    upgradeInsecureRequests: [],
  },
}));
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'"],
    imgSrc: ["'self'"],
    fontSrc: ["'self'"],
    connectSrc: ["'self'"],
    frameSrc: ["'none'"],
    objectSrc: ["'none'"],
  },
}));

CSP for React/Vue Apps

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'"], // React inline scripts
    styleSrc: ["'self'", "'unsafe-inline'"], // CSS-in-JS
    imgSrc: ["'self'", "data:", "https:"],
    connectSrc: ["'self'", process.env.API_URL],
  },
}));

5. HTTP Strict Transport Security (HSTS)

Forces HTTPS connections:

app.use(helmet.hsts({
  maxAge: 31536000, // 1 year in seconds
  includeSubDomains: true,
  preload: true,
}));

// Only enable in production with HTTPS
if (process.env.NODE_ENV === 'production') {
  app.use(helmet.hsts({
    maxAge: 31536000,
    includeSubDomains: true,
  }));
}

6. X-Frame-Options

Prevents clickjacking:

// Deny all framing
app.use(helmet.frameguard({ action: 'deny' }));

// Allow same origin
app.use(helmet.frameguard({ action: 'sameorigin' }));

// Allow specific domain
app.use(helmet.frameguard({
  action: 'allow-from',
  domain: 'https://example.com'
}));

7. X-Content-Type-Options

Prevents MIME sniffing:

app.use(helmet.noSniff());

// Sets header:
// X-Content-Type-Options: nosniff

8. Referrer Policy

Controls Referer header:

app.use(helmet.referrerPolicy({
  policy: 'strict-origin-when-cross-origin'
}));

// Options:
// - no-referrer
// - no-referrer-when-downgrade
// - origin
// - origin-when-cross-origin
// - same-origin
// - strict-origin
// - strict-origin-when-cross-origin (recommended)
// - unsafe-url

9. Hide Powered By

Remove X-Powered-By header:

app.use(helmet.hidePoweredBy());

// Or manually:
app.disable('x-powered-by');

// Or fake it:
app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }));

10. Custom Configuration

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "https://cdn.example.com"],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
  },
  frameguard: {
    action: 'deny',
  },
  referrerPolicy: {
    policy: 'strict-origin-when-cross-origin',
  },
}));

11. Disable Specific Middleware

app.use(helmet({
  contentSecurityPolicy: false, // Disable CSP
  frameguard: false, // Disable X-Frame-Options
}));

12. Production Configuration

const express = require('express');
const helmet = require('helmet');

const app = express();

// Production-ready Helmet config
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", process.env.CDN_URL],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", process.env.API_URL],
      fontSrc: ["'self'", "https://fonts.gstatic.com"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
  frameguard: {
    action: 'deny',
  },
  referrerPolicy: {
    policy: 'strict-origin-when-cross-origin',
  },
}));

// Additional security
app.disable('x-powered-by');

// Trust proxy (if behind reverse proxy)
app.set('trust proxy', 1);

app.listen(process.env.PORT || 3000);

13. With CORS

const helmet = require('helmet');
const cors = require('cors');

// Apply Helmet first
app.use(helmet());

// Then CORS
app.use(cors({
  origin: process.env.FRONTEND_URL,
  credentials: true,
}));

14. Testing Security Headers

# Check headers with curl
curl -I https://example.com

# Look for:
# Content-Security-Policy: ...
# Strict-Transport-Security: max-age=31536000; includeSubDomains
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff
# Referrer-Policy: strict-origin-when-cross-origin

Security Scanners

# Mozilla Observatory
# https://observatory.mozilla.org/

# Security Headers
# https://securityheaders.com/

# SSL Labs
# https://www.ssllabs.com/ssltest/

15. Common Issues

Issue 1: CSP Blocking Inline Scripts

// Problem: React/Vue inline scripts blocked
<script>window.__INITIAL_STATE__ = {...}</script>

// Solution 1: Use nonce
app.use((req, res, next) => {
  res.locals.nonce = crypto.randomBytes(16).toString('base64');
  next();
});

app.use(helmet.contentSecurityPolicy({
  directives: {
    scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
  },
}));

// In template:
<script nonce="<%= nonce %>">...</script>

// Solution 2: Allow unsafe-inline (less secure)
app.use(helmet.contentSecurityPolicy({
  directives: {
    scriptSrc: ["'self'", "'unsafe-inline'"],
  },
}));

Issue 2: HSTS Breaking Local Development

// Only enable HSTS in production
if (process.env.NODE_ENV === 'production') {
  app.use(helmet.hsts({
    maxAge: 31536000,
  }));
}

Issue 3: CSP Blocking CDN Resources

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: [
      "'self'",
      "https://cdn.jsdelivr.net",
      "https://cdnjs.cloudflare.com",
    ],
    styleSrc: [
      "'self'",
      "https://fonts.googleapis.com",
    ],
  },
}));

16. Best Practices

1. Use Helmet in All Environments

// Development
app.use(helmet({
  contentSecurityPolicy: false, // Easier debugging
}));

// Production
app.use(helmet()); // Full security

2. Test Thoroughly

// Test CSP in report-only mode first
app.use(helmet.contentSecurityPolicy({
  directives: { /* ... */ },
  reportOnly: true, // Only report violations, don't block
}));

3. Monitor CSP Violations

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    reportUri: '/api/csp-violation-report',
  },
}));

app.post('/api/csp-violation-report', (req, res) => {
  console.log('CSP Violation:', req.body);
  // Log to monitoring service
  res.status(204).end();
});

Summary

Helmet provides essential security headers:

  • 15 security middlewares in one package
  • CSP prevents XSS attacks
  • HSTS enforces HTTPS
  • Frameguard prevents clickjacking
  • Production-ready with minimal config

Key Takeaways:

  1. Always use Helmet in production
  2. Configure CSP for your specific app
  3. Enable HSTS only with HTTPS
  4. Test security headers regularly
  5. Monitor CSP violations

Next Steps:

Resources:


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Complete Helmet guide for Express.js security. Learn security headers, CSP, XSS protection, and hardening Node.js applic…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Helmet, Security, Express, Node.js, HTTP Headers, Web Security.