本页译自英文原文。如有出入,以英文版本为准。
安全
糟糕的默认值就是 bug。DaloyJS 把由核心强制执行的防护与官方安全中间件区分开来,危险的操作默认就被拦截,而与部署相关的配置则保持显式。
核心防护默认保护每一个应用。官方中间件则添加取决于你的路由、用户和部署方式的策略。
核心强制执行的内容
这些检查发生在 App 或运行时适配器内部。应用无需调用任何中间件即可获得它们。
- 01入口传入的请求方法、路径、请求头、请求体
- 02大小上限请求体大小限制Content-Length 超过上限即 413
- 03解析加固的 JSON 解析剥离 __proto__ / constructor
- 04路由路由器 + 方法检查.. 被解析为规范路径。错误的方法返回 405
- 05处理函数你的类型化处理函数在 requestTimeoutMs 限制下运行
| 威胁 | 内置行为 |
|---|---|
| 请求体大小 DoS | 流式读取,硬性上限(默认 1 MiB),先检查 Content-Length -> 413。自 1.5.4 起,上限覆盖每条路由,包括直接读取 ctx.request 的处理函数;在此之前只覆盖带请求体 schema 的路由。上限在读取请求体时生效,因此忽略请求体的路由不会因此被拒绝。 |
| 原型污染 | safeJsonParse 通过 reviver 剥离 __proto__、constructor、prototype。 |
| 请求体中格式错误的 UTF-8 | 自 1.16.0 起,JSON、urlencoded 和文本请求体解析器采用严格解码:含有无效或过长 UTF-8 序列的请求体会返回 400,而不是替换为 U+FFFD 后再校验(RFC 8259 §8.1)。格式正确的非 ASCII 字符和开头的 BOM 仍可正常解析。自己读取 ctx.request.text() 的处理函数仍使用平台宽松的解码方式。 |
| 响应头 / 响应拆分 | sanitizeHeaderName / sanitizeHeaderValue 拒绝 CRLF + NUL。 |
| 路径穿越 | 两层防护。首先规范化请求 URL:WHATWG URL 解析会解析 . / ..(及其 %2e 写法),Node 适配器也对原始请求目标执行同样的步骤。然后核心路由器会拒绝残留的任何点分段或空的 // 分段,并且绝不会把编码后的穿越(%2e%2e、..%2F)绑定到参数中。路由按精确字符串匹配,因此没有可以逃逸出去的目录。 |
| Slow-loris / 挂起的处理函数 | requestTimeoutMs(默认 30 秒)会返回 408,并触发 ctx.request.signal 以便协作式清理。自 1.5.4 起,它覆盖 hook、请求体读取和处理函数(之前只覆盖处理函数)。Node 适配器还会设置 socket 超时。 |
| 因错误的值而失效的限制 | 自 1.5.4 起,bodyLimitBytes、requestTimeoutMs、maxHeaderCount、jsonMaxKeys 和 jsonMaxDepth 必须是有限的非负整数;NaN(例如 Number(process.env.UNSET))和其他无效值在任何环境下都会在构造时抛出错误。 |
| 不支持的内容类型 | 带请求体 schema 的路由会拒绝不在允许列表中的 content-type -> 415。 |
| 方法混淆 | 返回真正的 405 并带 Allow 响应头,绝不返回误导性的 404。 |
| 信息泄露(5xx) | 除非环境被明确设为 development 或 test,否则 5xx problem+json 的 detail 都会被脱敏;未设置 NODE_ENV 时同样会脱敏。 |
官方安全中间件
这些中间件是 DaloyJS 的一部分,并在一起编写文档,但它们保持显式,因为 CSP、CORS、限流键、会话密钥和 CSRF 推行都是部署层面的决定。
当需要统计失败的凭据时,请把 rateLimit() 或 loginThrottle() 放在 bearer/basic/JWK/mTLS 认证之前。即使认证在 preBody 中拒绝了请求,限流器仍会消耗这次尝试,并在超过上限后返回 429。DaloyJS 不会为此读取声明的请求体。这条路径上的自定义键生成器应使用原始请求数据,或由更早的 preBody hook 写入的 state。
官方起步模板已经为你接好了这些中间件:Node、Bun 和 Deno 启用 secureHeaders()、requestId() 和 rateLimit()。Cloudflare Worker 和 Vercel 启用 secureHeaders() 和 requestId(),并使用更严格、适合边缘环境的请求体和超时限制。
按部署目标推荐的配置
除非有具体的理由需要修改,否则请从下面的中间件开始。这能让有风险的选择保持显式且一致。
| 目标 | 推荐的基线 |
|---|---|
| Node / Bun / Deno API | requestId()、secureHeaders()、rateLimit(),API 跨源时再加上 cors()。 |
| Cloudflare Workers | 默认使用 requestId() 和 secureHeaders()。只在需要时使用 cors();当流量分布在多个 isolate 上时,优先使用外部/共享的限流器,而不是默认的内存存储。 |
| Vercel | 默认使用 requestId() 和 secureHeaders()。只在需要时添加 cors();如果需要跨区域持久的计数器,请使用共享的限流器。 |
| 基于 Cookie 认证的应用 | 在基线之上添加 session() 和 csrf(),让会修改数据的路由免受跨站表单和 fetch 攻击。 |
| 位于可信反向代理之后 | 保留基线,然后为 rateLimit() 配置显式的 keyGenerator,或者只在代理会剥离并重写转发请求头之后才设置 trustProxyHeaders: true。 |
用 csrf() 保护会改变状态的路由
使用 csrf()(英文)保护会修改数据的端点。支持两种策略:
- 双重提交 Cookie(默认):在安全请求上设置令牌 Cookie,要求非安全方法在
x-csrf-token请求头中携带相同的值,并以时间安全的 403 拒绝不匹配的请求。 - Fetch Metadata(
strategy: "fetch-metadata"):无令牌的防护,依赖现代的Sec-Fetch-Site请求头。无需 Cookie 往返,也不与 HTML 渲染耦合。推荐用于新的面向浏览器的应用。
secureHeaders() 默认值
如果你需要不同的 CSP、想在本地开发时禁用 HSTS,或者需要更宽松的权限策略,请显式地向 secureHeaders() 传入选项。旧式的 X-XSS-Protection: 0 响应头需通过 xssProtection: true 主动开启,适用于希望显式禁用旧浏览器 XSS 过滤器的部署。
使用按请求 nonce 和 Trusted Types 的 CSP
secureHeaders() 可以根据指令映射构建 CSP,并把一个全新的按请求 nonce 注入 script-src、script-src-elem、style-src 和 style-src-elem,还可以输出 require-trusted-types-for 'script',在运行时加固以防范 DOM XSS。nonce 通过 ctx.state.cspNonce 暴露,处理函数可以把它渲染到 <script nonce="..."> 标签中。
不要用 @daloyjs/core/docs 中的 htmlResponse() 渲染这个页面:该辅助函数会下发它自己的 Content-Security-Policy(为 Swagger / Scalar 文档界面调整过,带有 'unsafe-inline'),并覆盖上面严格的 nonce CSP,这样控制内联脚本的就不再是 nonce 了。请把 htmlResponse() 留给你的 API 文档路由,而对受 nonce 保护的页面返回你自己的 Response 响应体。
认证
SQL 注入
Daloy 不提供数据库驱动,但它确实负责的 HTTP 边界(严格的 Zod schema、加固的 JSON 解析器、请求体大小上限)缩小了能到达你仓储层的攻击面。参见 SQL 注入(英文),了解各 ORM(Prisma、Drizzle、Kysely、原生驱动)安全与不安全的写法、动态 ORDER BY 的允许列表方案,以及维护者用来捕捉回归的 grep 规则。
命令注入
DaloyJS 的运行时经 CI 关卡保证不使用 child_process,因此框架本身无法调用 shell。参见命令注入(英文),了解确实需要调用外部程序的处理函数应采用的安全写法(execFile + argv 数组,绝不使用 exec(`cmd $${input}`))、Windows 上的 BatBadBut 陷阱,以及在 PR 阶段阻止新 bug 的 grep 规则。
管理面板
要在 DaloyJS 之上构建管理后台或客户成功平台?参见保护管理面板(英文)中推荐的模式:internal: true 路由、ipRestriction()、使用按请求 nonce 的严格 CSP、按管理员的身份认证、登录限流的 rateLimit() 分组以及结构化审计日志,与 Aikido 公开的“安全管理面板”检查清单一一对应。
供应链
DaloyJS 通过 pnpm 分发,以获得更严格的安装模型。脚手架生成的 pnpm 应用会继承安装时的管控,而 DaloyJS 自己的仓库和可选的 GitHub Actions 套件则增加了 CI/CD 管控,用以对抗近期 npm 事件中出现的缓存投毒、维护者钓鱼和 OIDC 令牌滥用等模式。
- 严格隔离:包无法访问幽灵依赖。
- 内容寻址存储:每个字节都经过哈希和校验。
- CI 中使用冻结的锁文件并加上
--ignore-scripts:可复现的安装,且不执行传递依赖的生命周期脚本。 verifyStoreIntegrity,读取时检测损坏。strictPeerDependencies,不会悄悄出现 peer 依赖不匹配。- 在
pnpm-workspace.yaml中设置minimumReleaseAge: 1440,新发布的版本要等 24 小时才会安装(pnpm 11 会忽略.npmrc中的写法;参见安装加固)。 - 脚手架中设置
ignoreScripts: true,DaloyJS 仓库中则使用strictDepBuilds加allowBuilds允许列表:除了经过审查的脚本之外,不运行任何依赖的安装脚本。这能阻止安装脚本蠕虫,但挡不住在导入时运行的负载。 - 用 SHA 固定的 GitHub Actions:可选生成的 GitHub 工作流会把第三方 action 固定到不可变的提交,而不是可变的标签。
- 受保护的 DaloyJS npm 发布:框架自己的包使用仅由标签触发的发布工作流、受保护环境审批、OIDC 可信发布以及
--provenance。
如果你生成的应用不在 GitHub 上,请直接沿用可移植的部分,并把 GitHub 工作流规则转换到你的 CI 平台上。框架无法在私有的 GitLab、Bitbucket、Azure DevOps 或本地部署环境中强制执行分支保护或 runner 出站限制。
可信代理与限流
在推导限流键时,DaloyJS 默认不再信任 X-Forwarded-For 或 X-Real-IP。除非你的反向代理会剥离并重写这些请求头,否则客户端可以伪造它们。因此,默认的限流器以无法伪造的 TCP 对端为键(自 1.3.7 起;只有在不暴露对端信息的运行时上才共用一个桶),直到你提供显式的 keyGenerator,或者在可信代理之后主动开启 trustProxyHeaders: true / trustedProxies。信任代理请求头时,键取 X-Forwarded-For 最右侧的条目(即你的代理追加的那一项),绝不会取攻击者添加在左侧的条目。多跳链路用 trustedHops 声明跳数。当源站本身可被直接访问时,请设置 trustedProxies,在相信任何转发请求头之前,先用 CIDR 允许列表验证对端 socket(参见 autoBan 的说明(英文))。
如果你已经在 App 上用 behindProxy 声明了网络拓扑,就无需重复声明。没有设置 keyGenerator、trustProxyHeaders、trustedHops 或 trustedProxies 中任何一项的 rateLimit() 或 loginThrottle() 会沿用 App 的 behindProxy,因此位于你的代理之后(或者在没有对端 socket 的 Vercel 和 Cloudflare Workers 上)的每个客户端都会得到自己的桶,而不是所有人共用一个。显式的限流器选项始终优先,trustProxyHeaders: false 则可以退出。请让 behindProxy 与实际情况一致:如果你知道代理的地址,优先使用 { cidrs },因为 { hops } 会信任任何能直接访问应用的人发来的最右侧转发条目。
对于输入凭据的路由,请在 /login、OTP 和密码重置路由上使用 loginThrottle()(英文),并在相关的 WebSocket 升级上使用 wsRateLimit()。这两个辅助函数可以从同一个 groupId 桶中扣减。
自托管文档资源
内置的文档辅助函数不再强制使用以 jsDelivr 为准的 CSP。你可以自行托管 Swagger UI 或 Scalar 的资源,为引导脚本添加 nonce,并为你的文档路由输出同源的 CSP。
安装加固
在 pnpm 11+ 上,安装时的设置以 camelCase 键写在 pnpm-workspace.yaml 中。pnpm 11 会忽略 .npmrc 中这些设置的写法,因此只依赖 .npmrc 的配置会悄悄失去冷却期和脚本策略。.npmrc 镜像只为 npm 和较旧的工具保留。
这些设置保护使用它们的安装过程。ignoreScripts 能阻止安装脚本蠕虫(Shai-Hulud 那一类);而在你的代码导入包时才运行的负载,只能靠冷却期和提交到仓库的锁文件来缓解。create-daloy 会为 pnpm 脚手架写入这个文件。
完整的 CI/CD 和维护者操作手册请阅读供应链安全(英文)。请在 CI 中以及发布之前运行 pnpm audit --prod。
OWASP API Security Top 10 对照
关于 Daloy 如何逐项应对 OWASP API Security Top 10 (2023) 中的每个条目,以及各项通用最佳实践(加密、校验、限流、日志、资产清单、第三方 API 安全),请阅读 OWASP API Top 10 对照(英文)。
报告漏洞
请通过 GitHub 的私密漏洞报告功能 github.com/daloyjs/daloy/security/advisories/new 提交,并附上复现步骤。不要公开提交包含漏洞利用细节的 issue。
最后更新: