farrow-helmet
v0.1.0
Published
Security middleware for Farrow HTTP applications that helps secure your web apps by setting various HTTP security headers
Maintainers
Readme
farrow-helmet
A security middleware for Farrow HTTP applications that helps secure your web apps by setting various HTTP security headers.
farrow-helmet is a TypeScript-first security middleware designed specifically for the Farrow HTTP framework, providing comprehensive protection against common web vulnerabilities.
中文文档 | English
Table of Contents
- Features
- Quick Start
- Installation
- Basic Usage
- Security Headers
- Configuration
- Advanced Usage
- Best Practices
- Examples
- API Reference
- Testing
- Contributing
- License
Features
🛡️ Comprehensive Security Headers - Automatically sets essential security headers
🔧 Highly Configurable - Fine-tune security policies for your needs
⚡ TypeScript First - Full type safety and IntelliSense support
🚀 Minimal Dependencies - Lightweight wrapper around proven helmet.js
🧪 Well Tested - 21+ test cases covering all functionality
📦 Farrow Optimized - Designed specifically for Farrow HTTP framework
Quick Start
npm install farrow-helmetimport { Http, Response } from 'farrow-http'
import { helmet } from 'farrow-helmet'
const app = Http()
// Apply security middleware
app.use(helmet())
app.get('/').use(() => Response.json({
message: '🛡️ Secured with farrow-helmet!'
}))
app.listen(3000)That's it! Your Farrow application is now protected with essential security headers.
Installation
# npm
npm install farrow-helmet
# yarn
yarn add farrow-helmet
# pnpm
pnpm add farrow-helmetPrerequisites:
- Node.js 16+
- TypeScript 4.5+
- farrow-http 2.x
Basic Usage
Default Protection
Apply helmet with sensible defaults:
import { helmet } from 'farrow-helmet'
app.use(helmet())This automatically enables:
- Content type sniffing protection
- Clickjacking protection
- XSS filtering (modern approach)
- Referrer policy controls
- Download security for IE
- Cross-domain policy restrictions
Custom Configuration
Customize security policies to fit your needs:
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
},
hsts: {
maxAge: 31536000, // 1 year
includeSubDomains: true,
preload: true
}
}))Security Headers
farrow-helmet sets the following security headers by default:
| Header | Default Value | Purpose |
|--------|---------------|---------|
| X-Content-Type-Options | nosniff | Prevents MIME type sniffing attacks |
| X-Frame-Options | SAMEORIGIN | Prevents clickjacking attacks |
| X-XSS-Protection | 0 | Modern XSS protection (disables legacy filter) |
| Referrer-Policy | no-referrer | Controls referrer information leakage |
| X-Download-Options | noopen | Prevents IE from executing downloads |
| X-Permitted-Cross-Domain-Policies | none | Restricts Flash/PDF cross-domain access |
Additional Available Headers
Configure these headers with custom options:
- Content Security Policy (CSP) - Prevents XSS and injection attacks
- HTTP Strict Transport Security (HSTS) - Enforces HTTPS connections
- X-DNS-Prefetch-Control - Controls DNS prefetching
- Expect-CT - Certificate transparency enforcement
- Feature-Policy / Permissions-Policy - Controls browser features
Configuration
Content Security Policy (CSP)
Protect against XSS and injection attacks:
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://trusted-cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
fontSrc: ["'self'", "https:", "data:"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
frameAncestors: ["'none'"],
blockAllMixedContent: [],
upgradeInsecureRequests: []
}
}
}))HTTP Strict Transport Security (HSTS)
Force HTTPS connections:
app.use(helmet({
hsts: {
maxAge: 31536000, // 1 year in seconds
includeSubDomains: true, // Apply to all subdomains
preload: true // Enable HSTS preloading
}
}))Environment-Specific Configuration
Configure different policies for different environments:
const isDevelopment = process.env.NODE_ENV === 'development'
const isProduction = process.env.NODE_ENV === 'production'
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
...(isDevelopment ? ["'unsafe-eval'", "'unsafe-inline'"] : [])
],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "blob:"]
}
},
hsts: isProduction ? {
maxAge: 63072000, // 2 years
includeSubDomains: true,
preload: true
} : false, // Disable HSTS in development
// Report CSP violations in development
contentSecurityPolicyReportOnly: isDevelopment
}))Disabling Specific Headers
Disable headers you don't need:
app.use(helmet({
xFrameOptions: false, // Disable X-Frame-Options
contentSecurityPolicy: false, // Disable CSP
xContentTypeOptions: false // Disable X-Content-Type-Options
}))Advanced Usage
Integration with Other Middleware
Proper middleware ordering is crucial for security:
const app = Http({ logger: true })
// 1. Error handling (first)
app.use(async (req, next) => {
try {
return await next(req)
} catch (error) {
console.error('Request failed:', error)
return Response.status(500).json({
error: 'Internal server error'
})
}
})
// 2. Security headers (early)
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"]
}
}
}))
// 3. CORS (after security)
import { cors } from 'farrow-cors'
app.use(cors({
origin: 'https://trusted-domain.com',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}))
// 4. Authentication
app.use(authenticationMiddleware)
// 5. Business logic
app.get('/api/data').use(() => Response.json({ data: [] }))Router-Specific Security
Apply different security policies to different routes:
import { Router } from 'farrow-http'
// Public API - Restrictive CSP
const publicRouter = Router()
publicRouter.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'none'"],
scriptSrc: ["'none'"],
styleSrc: ["'none'"],
imgSrc: ["'none'"]
}
}
}))
publicRouter.get('/status').use(() => Response.json({ status: 'ok' }))
// Admin panel - More permissive
const adminRouter = Router()
adminRouter.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}
}))
adminRouter.get('/dashboard').use(() => Response.html(dashboardHTML))
// Mount routers
app.route('/api').use(publicRouter)
app.route('/admin').use(adminRouter)Dynamic Security Policies
Adjust policies based on request context:
const dynamicHelmet = (req: RequestInfo, next: any) => {
const isAPIRequest = req.pathname.startsWith('/api')
const isAdminRequest = req.pathname.startsWith('/admin')
let helmetConfig = {}
if (isAPIRequest) {
helmetConfig = {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'none'"]
}
}
}
} else if (isAdminRequest) {
helmetConfig = {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"]
}
}
}
}
return helmet(helmetConfig)(req, next)
}
app.use(dynamicHelmet)Best Practices
1. Start with Strict Policies
Begin with restrictive policies and gradually relax them:
// Start strict
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'none'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"],
imgSrc: ["'self'"]
}
}
}))
// Then add exceptions as needed
// scriptSrc: ["'self'", "https://trusted-cdn.com"]2. Use CSP Report-Only Mode for Testing
Test CSP policies without breaking functionality:
app.use(helmet({
contentSecurityPolicyReportOnly: {
directives: {
defaultSrc: ["'self'"],
reportUri: ['/csp-report']
}
}
}))
// Handle CSP reports
app.post('/csp-report').use((req) => {
console.log('CSP Violation:', req.body)
return Response.status(204).empty()
})3. Environment-Specific Security
const securityConfig = {
development: {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-eval'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
},
hsts: false
},
production: {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}
}
app.use(helmet(securityConfig[process.env.NODE_ENV] || securityConfig.production))4. Monitor and Log Security Headers
app.use(helmet())
app.use((req, next) => {
const response = next(req)
// Log security-relevant requests
if (req.pathname.includes('..') || req.pathname.includes('<script>')) {
console.warn('Suspicious request:', {
ip: req.headers['x-forwarded-for'] || 'unknown',
userAgent: req.headers['user-agent'],
path: req.pathname
})
}
return response
})Examples
Complete Production Setup
import { Http, Response, Router } from 'farrow-http'
import { helmet } from 'farrow-helmet'
import { createContext } from 'farrow-pipeline'
const app = Http({
logger: {
ignoreIntrospectionRequestOfFarrowApi: true
}
})
// Security configuration
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
baseUri: ["'self'"],
blockAllMixedContent: [],
fontSrc: ["'self'", "https:", "data:"],
frameAncestors: ["'self'"],
imgSrc: ["'self'", "data:"],
objectSrc: ["'none'"],
scriptSrc: ["'self'"],
scriptSrcAttr: ["'none'"],
styleSrc: ["'self'", "https:", "'unsafe-inline'"],
upgradeInsecureRequests: []
}
},
hsts: {
maxAge: 63072000, // 2 years
includeSubDomains: true,
preload: true
}
}))
// CORS for API
import { cors } from 'farrow-cors'
const apiRouter = Router()
apiRouter.use(cors({
origin: 'https://yourdomain.com',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count', 'X-Request-ID']
}))
apiRouter.get('/users').use(() => {
return Response.json({ users: [] })
})
app.route('/api').use(apiRouter)
// Static files with security
app.serve('/static', './public')
// Health check
app.get('/health').use(() => {
return Response.json({
status: 'healthy',
timestamp: new Date().toISOString()
})
})
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log(`🛡️ Secure server running on http://localhost:${port}`)
})CSP Violation Reporting
import { ObjectType, String, Optional, Any } from 'farrow-schema'
class CSPReport extends ObjectType {
documentUri = String
referrer = Optional(String)
violatedDirective = String
originalPolicy = String
blockedUri = Optional(String)
sourceFile = Optional(String)
lineNumber = Optional(Number)
columnNumber = Optional(Number)
}
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
reportUri: ['/csp-report']
}
}
}))
app.post('/csp-report', { body: { "csp-report": CSPReport } }).use((req) => {
const report = req.body["csp-report"]
console.warn('CSP Violation:', {
uri: report.documentUri,
directive: report.violatedDirective,
blocked: report.blockedUri,
source: report.sourceFile,
line: report.lineNumber,
timestamp: new Date().toISOString()
})
// Store in database or send to monitoring service
// await logCSPViolation(report)
return Response.status(204).empty()
})API Reference
helmet(options?: HelmetOptions): HttpMiddleware
The main helmet function that returns a Farrow HTTP middleware.
HelmetOptions
All options are optional and match the helmet.js API:
interface HelmetOptions {
contentSecurityPolicy?: CSPOptions | false
hsts?: HSTSOptions | false
xContentTypeOptions?: boolean
xDnsPrefetchControl?: { allow?: boolean } | false
xDownloadOptions?: boolean
xFrameOptions?: XFrameOptionsOptions | false
xPermittedCrossDomainPolicies?: XPermittedCrossDomainPoliciesOptions | false
xPoweredBy?: boolean
xXssProtection?: boolean
referrerPolicy?: ReferrerPolicyOptions | false
// ... and more
}CSPOptions
Content Security Policy configuration:
interface CSPOptions {
directives?: {
defaultSrc?: string[]
scriptSrc?: string[]
styleSrc?: string[]
imgSrc?: string[]
fontSrc?: string[]
objectSrc?: string[]
baseUri?: string[]
formAction?: string[]
frameAncestors?: string[]
// ... and more
}
reportOnly?: boolean
reportUri?: string[]
}HSTSOptions
HTTP Strict Transport Security configuration:
interface HSTSOptions {
maxAge?: number // Max age in seconds
includeSubDomains?: boolean
preload?: boolean
}For complete API documentation, see the helmet.js documentation.
Testing
farrow-helmet comes with comprehensive tests covering all functionality:
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverageTesting Your Security Setup
Test your security headers:
import { Http } from 'farrow-http'
import { helmet } from 'farrow-helmet'
import request from 'supertest'
const app = Http()
app.use(helmet())
app.get('/test').use(() => Response.json({ message: 'test' }))
describe('Security Headers', () => {
it('should set security headers', async () => {
const response = await request(app.server())
.get('/test')
.expect(200)
expect(response.headers['x-content-type-options']).toBe('nosniff')
expect(response.headers['x-frame-options']).toBe('SAMEORIGIN')
expect(response.headers['x-xss-protection']).toBe('0')
expect(response.headers['referrer-policy']).toBe('no-referrer')
})
})Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/AisonSu/farrow-helmet.git
cd farrow-helmet
# Install dependencies
pnpm install
# Run tests
pnpm test
# Build the project
pnpm buildReporting Issues
Please report security issues privately to [email protected].
For other issues, please use the GitHub issue tracker.
License
MIT © Aison
Related
- Farrow - The Farrow framework
- helmet.js - The original helmet library for Express
- OWASP Secure Headers - Security header guidelines
Security Notice: While farrow-helmet helps secure your applications by setting security headers, it's not a silver bullet. Always follow security best practices, keep your dependencies updated, and consider additional security measures like input validation, authentication, and authorization.
