Deno 权限模型与安全实践
默认拒绝的权限体系、--allow 系列参数、权限集固化与供应链安全清单。
0. 一句话理解
Deno 的默认状态是”什么都不能做”:读文件、写网络都要在运行命令里显式授权;权限最小化是 Deno 最重要的安全特性。
想象你雇了一个新员工(脚本):Node 的做法是第一天就给他全公司门禁卡;Deno 的做法是给一张白卡,他要进机房、进档案室,得逐项申请、你逐项批——批了什么,他能去哪就一清二楚。
1. 权限报错体验
// read_file.ts
const content = await Deno.readTextFile("secret.txt")
console.log(content)
deno run read_file.ts
error: PermissionDenied: Requires read access to "secret.txt", run again with the --allow-read flag
at file:///D:/demo/read_file.ts:1:24
讲解:
- 直接运行会报错:报错信息同时告诉你”缺什么权限”和”加什么参数”,照抄即可。
- 这是”默认拒绝”的设计:即使脚本被恶意第三方依赖控制,它也无法悄悄读取你的文件。
- 相比 Node.js 默认全开,Deno 把”要不要给权限”变成了每次运行时的显式决定——权限清单本身就是一份安全审计文档。
2. 授权参数全景
# 只允许读当前目录
deno run --allow-read=. read_file.ts
# 只允许访问指定域名
deno run --allow-net=api.example.com fetch_data.ts
# 允许读写文件与网络(生产环境按需最小化,不要图省事用 --allow-all)
deno run --allow-read --allow-write --allow-net app.ts
| 参数 | 控制的能力 | 典型收紧写法 |
|---|---|---|
--allow-read(-R) | 读文件 | --allow-read=./data 只读数据目录 |
--allow-write(-W) | 写文件 | --allow-write=./output 只写输出目录 |
--allow-net(-N) | 网络访问 | --allow-net=localhost:8000 只听本地端口 |
--allow-env(-E) | 环境变量 | --allow-env=PORT,APP_ENV 只放行白名单变量 |
--allow-run | 启动子进程 | --allow-run=git 只允许 git |
--allow-sys(-S) | 系统信息(内存、网卡等) | --allow-sys=osInfo 按需放行 |
--allow-import | 从网络下载代码(依赖) | --allow-import=jsr.io,npmjs.com 限定源 |
-A / --allow-all | 全部权限 | 本地临时调试专用,生产禁用 |
讲解:
--allow-read=.的=.表示只读当前目录,比无参数的全盘读取安全得多;--allow-net支持同时列多个”域名:端口”。- 括号里是缩写形式(如
-R、-N、-A),单独使用时命令更短;团队项目建议写全称——可读性优先,权限清单是给人审的。 --allow-import值得单独强调:它管的是”允许从哪些地方下载代码”,把依赖来源限定到 jsr.io 与 npmjs.com,能挡住从陌生站点拉代码的供应链攻击。- 运行中遇到未授权操作时,Deno 会在终端弹交互式授权询问;CI 等非交互环境加
--no-prompt,未经授权直接失败而不是挂起等待。
3. 权限写进配置:权限集(Deno 2.5+)
权限参数敲久了又长又容易漏,Deno 2.5 起可以把权限固化进 deno.json:
// deno.json —— 权限集集中管理,命令行只留一个任务名
{
"permissions": {
"read": ["./assets", "./data"],
"net": ["localhost:8000", "api.example.com"],
"env": ["PORT", "APP_ENV"]
},
"tasks": {
"dev": "deno run --watch server.ts",
"start": "deno run server.ts"
}
}
讲解:
- 顶层
permissions字段对本项目所有deno run/task/test生效:deno task start不带任何--allow参数,权限照样生效。 - 收益有三层:命令行变短、权限清单进版本库可评审、改权限的提交在 code review 里一目了然。
- 需要临时突破权限时,仍可在命令行叠加
--allow-write=./tmp——配置是默认值,命令行是覆盖项。
4. 敏感信息:密钥不进代码
# Windows PowerShell 设置环境变量
$env:DB_PASSWORD = "s3cr3t"
# 运行时显式授权读取环境变量
deno run --allow-env=DB_PASSWORD app.ts
// app.ts
const password = Deno.env.get("DB_PASSWORD")
if (!password) {
throw new Error("缺少 DB_PASSWORD 环境变量")
}
讲解:
Deno.env.get读取环境变量,密钥放在环境变量或密钥管理服务(如云厂商 Secret Manager)里,绝不写进代码与 git。--allow-env=DB_PASSWORD只放行这一个变量,其他环境变量脚本读不到;权限系统让”环境变量”这个全局口袋也变成了白名单。if (!password) throw是”fail fast”:缺少必需配置时立即失败,而不是带着空密码运行。
5. 依赖供应链安全
deno install # 按 deno.json/锁文件安装依赖并生成 deno.lock
deno check --all # 全项目类型检查
deno audit # 扫描依赖已知漏洞(Deno 2.6+,类似 npm audit)
deno audit fix # 自动升级到修复版本(Deno 2.8+)
deno install --frozen # 严格按锁文件安装,版本不一致直接报错(CI 用)
讲解:
deno install根据 import 生成锁文件(deno.lock),锁定每个依赖的精确版本与校验和,后续安装一致复现。deno check --all对全项目做类型检查,错误在 CI 里暴露而不是运行时。deno audit自 Deno 2.6 提供,对照 GitHub CVE 数据库扫描依赖漏洞(可加--socket做更深度的供应链分析);2.8 补充的audit fix能自动升级到修复版本。应纳入 CI 流程。- 锁文件必须提交进仓库;配合
--frozen,“团队、CI、生产三处依赖完全一致”才有制度保障。 - 就算某个依赖被投毒,它依然受权限系统约束——读不到未授权的文件、连不了未授权的网络。权限模型是最后一道兜底防线。
6. 生产环境安全清单
- 用最小权限运行:只给
--allow-net=你的域名、--allow-env=必需变量,权限集写进 deno.json 固化; - 容器内以非 root 用户运行,避免容器逃逸后获得 root;
- 密钥放 Secret Manager,轮换机制 + 审计日志;
- 依赖锁文件提交 git,CI 里跑
deno audit与deno check,安装用--frozen; - 不信任任何第三方模块的权限请求:权限永远由你的
deno run命令决定,依赖自己”申请”不来。
7. 常见陷阱
- 图省事
-A上生产:全权限让安全模型形同虚设,等于回到了 Node 的默认状态。正确姿势:本地调试可以-A,生产命令按最小权限枚举并写进 tasks。 - 权限粒度太粗:
--allow-read不带值等于可读全盘;至少限定到目录,敏感目录(密钥、配置)绝不入列。 - CI 里权限弹窗挂死:非交互环境忘记
--no-prompt,任务卡在等待授权。CI 的 deno 命令统一加--no-prompt。 - 锁文件冲突后删掉重来:等价于一次全量依赖升级。正确做法是解决冲突后保留已有条目,只让新增依赖重新解析,再用
deno install --frozen验证。 - 以为
--allow-env是”能读所有环境变量”才安全:恰恰相反,环境变量里常混着云平台的临时凭证;白名单越短,脚本被攻破后的爆炸半径越小。
8. 动手试试
- 写一个脚本读取
TEMP/TMP环境变量,分别用--allow-env=TEMP与不带参数运行,观察差异。 - 用
--allow-net=example.com访问https://example.com成功、再访问https://httpbin.org失败,体会域名白名单的作用。 - 在项目里启用 deno.lock(
deno install),打开锁文件看看每个依赖记录了什么;再把权限改写成 deno.json 的permissions字段。 - 跑一次
deno audit,确认当前依赖没有已知漏洞,把这个命令加进你的 CI 配置。
9. 本篇小结
初学者要点:
- 权限按需给:
--allow-read=.只读当前目录、--allow-net=域名只连指定主机;密钥走环境变量,锁文件保供应链。 - 权限报错不是 bug:照报错提示补参数;非交互环境记得
--no-prompt。 -A是调试快捷键,不是生产配置。
进阶注意:
- Deno 2.5+ 的
permissions配置让权限清单随代码演进、随版本评审,是团队项目固化的首选方式。 - 供应链三件套——锁文件入库、
deno install --frozen、deno audit(2.6+)——构成依赖安全的最低配置,缺一环就少一层保障。 - 权限模型与依赖来源控制(
--allow-import)联动,才能覆盖”代码怎么进来、数据怎么出去”两个方向。