安全加固:secureHeaders、csrf、xss 防护中间件
设置 Content-Security-Policy、X-Frame-Options
在现代 Web 应用中,安全必须是默认配置,而非事后补救。Hono 提供了轻量、高效的中间件生态,可轻松集成关键安全防护,抵御常见攻击如 XSS、CSRF、点击劫持等。
本文将介绍如何通过 Hono 中间件实现全面的安全加固。
一、核心安全威胁与对策
| 威胁 | 风险 | 防护措施 |
|---|---|---|
| XSS(跨站脚本) | 注入恶意脚本窃取数据 | CSP、输入过滤、转义 |
| CSRF(跨站请求伪造) | 伪造用户请求执行操作 | CSRF Token、SameSite Cookie |
| 点击劫持(Clickjacking) | 诱骗用户点击隐藏元素 | X-Frame-Options、frame-ancestors |
| MIME 类型嗅探 | 执行非预期内容 | X-Content-Type-Options: nosniff |
| 信息泄露 | 暴露服务器版本 | X-Powered-By 移除 |
二、1. secureHeaders:设置基础安全头
Hono 内置 secureHeaders 中间件,自动设置多个关键安全头。
安装
bash
npm install @hono/hono启用默认安全头
ts
import { Hono } from 'hono'
import { secureHeaders } from 'hono/secure-headers'
const app = new Hono()
// 启用基础安全头
app.use('*', secureHeaders())
// 或自定义配置
app.use('*', secureHeaders({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "trusted-cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "cdn.example.com"],
connectSrc: ["'self'", "api.example.com"],
frameAncestors: ["'none'"], // 禁止嵌入 iframe
},
},
xFrameOptions: 'DENY', // 可选:SAMEORIGIN
xContentTypeOptions: 'nosniff',
referrerPolicy: 'strict-origin-when-cross-origin',
permissionsPolicy: {
camera: [],
microphone: [],
geolocation: ['self'],
}
}))生成的头部示例:
http
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' trusted-cdn.com; ...
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(self)secureHeaders 是安全加固的第一步,应始终启用。
三、2. csrf 防护:防止跨站请求伪造
CSRF 攻击利用用户登录状态,伪造请求执行非预期操作(如转账、改密码)。
使用 csrf 中间件
ts
import { csrf } from 'hono/csrf'
// 仅对表单提交等敏感路由启用
app.use('/api/*', csrf())
// 示例:受保护的 API
app.post('/api/update-profile', async (c) => {
const data = await c.req.json()
// 此处已通过 CSRF 校验
return c.json({ success: true })
})工作原理:
- 中间件在响应中设置
Set-Cookie: _csrf=xxx; - 前端需在请求头中携带
CSRF-Token: xxx; - 服务端校验 token 是否匹配。
前端配合(Fetch):
ts
// 获取 CSRF Token(首次访问时 cookie 已设置)
const token = document.cookie
.split('; ')
.find(row => row.startsWith('_csrf='))
?.split('=')[1]
fetch('/api/update-profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CSRF-Token': token!
},
body: JSON.stringify(data)
})适用于传统表单或非同源 API。
四、3. xss 防护:输入过滤与转义
Hono 无内置 XSS 过滤中间件,但可通过自定义中间件实现。
自定义 XSS 防护中间件
ts
// middleware/xss.ts
import { XSSOptions } from 'xss'
import xss from 'xss'
export const xssProtection = (options?: XSSOptions) => {
const defaultOptions = {
whiteList: {
// 仅允许基本标签
b: [],
i: [],
em: [],
strong: [],
p: [],
br: [],
a: ['href', 'target']
},
stripIgnoreTag: true, // 移除不支持的标签
...options
}
return async (c: any, next: () => Promise<any>) => {
const originalParse = c.req.parseBody
// 包装 parseBody,自动净化表单数据
c.req.parseBody = async () => {
const body = await originalParse.call(c.req)
const cleanBody: any = {}
for (const [key, value] of Object.entries(body)) {
if (typeof value === 'string') {
cleanBody[key] = xss(value, defaultOptions)
} else {
cleanBody[key] = value
}
}
return cleanBody
}
// 包装 json,自动净化
const originalJson = c.req.json
c.req.json = async () => {
const data = await originalJson.call(c.req)
return sanitizeObject(data, defaultOptions)
}
await next()
}
}
// 递归净化对象
function sanitizeObject(obj: any, options: XSSOptions) {
if (typeof obj !== 'object' || obj === null) return obj
if (Array.isArray(obj)) {
return obj.map(item => sanitizeObject(item, options))
}
const result: any = {}
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'string') {
result[key] = xss(value, options)
} else {
result[key] = sanitizeObject(value, options)
}
}
return result
}使用:
ts
import { xssProtection } from './middleware/xss'
app.use('/api/user/*', xssProtection())五、其他安全实践
1. 安全 Cookie 设置
ts
c.cookie('session', token, {
httpOnly: true, // 防止 JS 访问
secure: true, // HTTPS only
sameSite: 'lax', // 防 CSRF
maxAge: 86400
})2. 速率限制(防暴力破解)
ts
import { ratelimit } from '@hono/ratelimit'
import { Redis } from '@upstash/redis'
const redis = new Redis({ url: '', token: '' })
app.use('/login', ratelimit({
window: '10 m',
limit: 5,
keyPrefix: 'login',
async store(request) {
return {
async increment(key) {
const value = await redis.incr(key)
await redis.expire(key, 600)
return { value, limit: 5, reset: Date.now() + 600_000 }
}
}
}
}))3. 移除 X-Powered-By
ts
app.use('*', (c, next) => {
c.res.headers.delete('X-Powered-By')
return next()
})六、最终安全加固结构
ts
const app = new Hono()
// 1. 基础安全头
app.use('*', secureHeaders({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
frameAncestors: ["'none'"]
}
},
xFrameOptions: 'DENY'
}))
// 2. CSRF 保护(敏感路由)
app.use('/api/admin/*', csrf())
// 3. XSS 输入净化
app.use('/api/user/*', xssProtection())
// 4. 速率限制
app.use('/login', rateLimitMiddleware)
// 5. 你的业务路由
app.post('/api/user/profile', (c) => {})
export default app七、验证安全头
部署后使用 SecurityHeaders.com 扫描,确保评级为 A+。
八、总结
| 中间件 | 作用 | 推荐等级 |
|---|---|---|
secureHeaders | 设置 CSP、X-Frame-Options 等 | ✅✅✅ 必用 |
csrf | 防跨站请求伪造 | ✅✅ 敏感操作必用 |
自定义 xssProtection | 净化输入 | ✅ 处理用户内容时使用 |
ratelimit | 防暴力破解 | ✅ 登录/注册必用 |
安全是持续过程。通过 Hono 的中间件机制,你可以在不影响业务逻辑的前提下,轻松构建纵深防御体系,显著提升应用安全性。