Hono 生产实战 01:安全中间件和接口防护
Hono 项目能跑起来很容易。
但能安全上线,是另一回事。
很多 API 出问题,不是因为业务逻辑多复杂,而是因为最基础的防护没做:
1 2 3 4 5 6
| CORS 随便放开 请求体没有大小限制 错误堆栈直接返回给前端 没有安全响应头 登录接口没有限流 管理接口只靠前端隐藏按钮
|
Hono 很轻,所以它不会替你默认打开一整套企业级安全策略。你要自己把生产 API 的基础防线搭起来。
这篇文章讲 Hono 生产环境最应该先做的安全中间件和接口防护。
技术栈快照时间:2026-06-23。Hono 和 Cloudflare 相关中间件会持续更新,具体 API 以官方文档为准。
一、先给安全基线
一个生产 Hono API,至少要考虑:
1 2 3 4 5 6 7 8 9
| secure headers CORS 白名单 body limit 统一错误响应 鉴权中间件 权限中间件 基础限流 请求 ID 日志脱敏
|
如果是浏览器前端调用,还要额外考虑:
1 2 3 4
| Cookie SameSite CSRF Authorization header HTTPS
|
不要把这些放到上线前最后一天再补。
安全策略越晚加,越容易破坏已有接口。
Hono 内置 secureHeaders middleware,可以帮你加一组常见安全响应头。
1 2 3 4 5 6
| import { Hono } from 'hono' import { secureHeaders } from 'hono/secure-headers'
const app = new Hono()
app.use('*', secureHeaders())
|
这类 header 不是银弹,但它能减少一些基础风险,比如 MIME sniffing、iframe 嵌入、过宽的权限策略等。
如果你有特殊需求,可以定制:
1 2 3 4
| app.use('*', secureHeaders({ xFrameOptions: 'DENY', xContentTypeOptions: 'nosniff', }))
|
不要指望它解决所有安全问题。
它只是第一层地基。
三、CORS 不要无脑星号
本地开发时很多人喜欢:
这会让开发很爽,但生产环境不要无脑放开。
更推荐按环境配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { cors } from 'hono/cors'
type Bindings = { CORS_ORIGIN: string }
const app = new Hono<{ Bindings: Bindings }>()
app.use('/api/*', async (c, next) => { const corsHandler = cors({ origin: c.env.CORS_ORIGIN, allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'], allowHeaders: ['Content-Type', 'Authorization'], credentials: true, })
return corsHandler(c, next) })
|
如果你不用 cookie,可以不打开 credentials。
如果你打开了 credentials: true,就不要使用 origin: '*'。浏览器不会接受这种组合。
CORS 的目标不是“让所有请求都过”,而是只允许可信前端域名访问。
四、限制请求体大小
没有 body limit 的接口,很容易被大请求拖垮。
Hono 内置 bodyLimit:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { bodyLimit } from 'hono/body-limit'
app.use('/api/*', bodyLimit({ maxSize: 1024 * 1024, onError: (c) => { return c.json({ error: { code: 'PAYLOAD_TOO_LARGE', message: '请求体太大', }, }, 413) }, }))
|
普通 JSON API,1MB 通常已经很大。
文件上传接口应该单独配置,不要让所有接口都接受大 body。
比如:
1 2 3
| app.use('/api/upload/*', bodyLimit({ maxSize: 10 * 1024 * 1024, }))
|
接口粒度越细,越不容易误伤。
五、统一错误响应
生产环境不要把未知错误堆栈返回给前端。
统一写 onError:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| app.onError((err, c) => { const requestId = c.get('requestId')
console.error('unhandled_error', { requestId, message: err instanceof Error ? err.message : String(err), stack: err instanceof Error ? err.stack : undefined, })
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: '服务器内部错误', }, requestId, }, 500) })
|
前端拿到 requestId,就能把问题反馈给后端。
后端通过日志查堆栈。
这比直接把 stack 暴露给用户安全得多。
六、鉴权和授权分开
鉴权是确认:
授权是确认:
不要混在一起。
鉴权中间件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| app.use('/api/private/*', async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', '')
if (!token) { return c.json({ error: { code: 'UNAUTHORIZED', message: '请先登录', }, }, 401) }
const user = await verifyToken(token) c.set('user', user)
await next() })
|
权限中间件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function requireRole(role: 'admin' | 'user') { return async (c, next) => { const user = c.get('user')
if (user.role !== role) { return c.json({ error: { code: 'FORBIDDEN', message: '没有权限', }, }, 403) }
await next() } }
|
前端隐藏按钮只是体验,后端权限校验才是安全。
七、登录和敏感接口要限流
限流不是所有接口都同一套。
最应该限流的是:
1 2 3 4 5 6 7 8
| 登录 注册 验证码 密码重置 发送邮件 文件上传 Webhook 高成本查询
|
Cloudflare 平台可以用 WAF / Rate Limiting 这类边缘能力。
如果你想在 Hono 里做一层简单限流,可以用 KV、Redis、Durable Object 或外部服务。
一个概念版 middleware:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| async function rateLimit(c, next) { const ip = c.req.header('CF-Connecting-IP') ?? 'unknown' const key = `rate:${ip}:${Math.floor(Date.now() / 60_000)}`
const current = Number(await c.env.KV.get(key) ?? 0)
if (current >= 20) { return c.json({ error: { code: 'RATE_LIMITED', message: '请求太频繁', }, }, 429) }
await c.env.KV.put(key, String(current + 1), { expirationTtl: 120, })
await next() }
|
注意,这只是基础窗口计数。
高安全场景要考虑并发竞态、全局一致性和平台级限流。
八、日志不要泄漏敏感信息
日志里不要打印:
1 2 3 4 5 6 7 8 9
| 密码 access token refresh token cookie 身份证 手机号完整值 银行卡 私钥 验证码
|
错误日志也要脱敏。
比如:
1 2 3 4
| console.info('login_failed', { requestId: c.get('requestId'), email: maskEmail(input.email), })
|
日志是排查工具,不是数据仓库。
九、推荐中间件顺序
一个基础顺序:
1 2 3 4 5 6 7 8 9
| app.use('*', requestId()) app.use('*', secureHeaders()) app.use('/api/*', corsConfig()) app.use('/api/*', bodyLimitConfig()) app.use('/api/private/*', auth()) app.use('/api/admin/*', requireRole('admin'))
app.notFound(notFoundHandler) app.onError(errorHandler)
|
顺序很重要。
比如 CORS 要在 route 前面。
body limit 要在解析 body 前面。
鉴权要在私有接口 route 前面。
十、最后的建议
Hono 的轻量是优势,但生产安全不能轻。
我建议每个 Hono API 项目一开始就把这些做好:
1 2 3 4 5 6 7 8
| 安全响应头 CORS 白名单 请求体大小限制 统一错误响应 鉴权和授权分层 敏感接口限流 日志脱敏 requestId
|
不要等接口写完再补安全。
安全不是最后一层装饰,而是 API 设计的一部分。
参考资料