Deno 的权限系统是其最核心的安全特性之一,采用"默认拒绝"的安全模型。这种设计确保了代码在未经明确授权的情况下无法访问敏感资源。
权限系统概述
Deno 的安全模型基于最小权限原则,默认情况下脚本没有任何权限,所有资源访问都需要显式授权。
权限类型
1. 文件系统权限
bash# 允许读取所有文件 deno run --allow-read script.ts # 允许读取特定目录 deno run --allow-read=/app,/data script.ts # 允许写入所有文件 deno run --allow-write script.ts # 允许写入特定目录 deno run --allow-write=/tmp script.ts # 同时允许读写 deno run --allow-read --allow-write script.ts
2. 网络权限
bash# 允许所有网络访问 deno run --allow-net script.ts # 允许访问特定域名 deno run --allow-net=api.example.com script.ts # 允许访问特定端口 deno run --allow-net=:8080 script.ts # 允许访问特定 IP 和端口 deno run --allow-net=127.0.0.1:8000 script.ts
3. 环境变量权限
bash# 允许访问所有环境变量 deno run --allow-env script.ts # 允许访问特定环境变量 deno run --allow-env=API_KEY,DATABASE_URL script.ts
4. 子进程权限
bash# 允许创建子进程 deno run --allow-run script.ts # 允许运行特定命令 deno run --allow-run=git,npm script.ts
5. 系统信息权限
bash# 允许获取系统信息 deno run --allow-sys script.ts # 允许获取特定系统信息 deno run --allow-sys=hostname,osRelease,osVersion script.ts
6. 高精度时间权限
bash# 允许访问高精度时间 deno run --allow-hrtime script.ts
7. FFI(外部函数接口)权限
bash# 允许加载动态库 deno run --allow-ffi script.ts # 允许加载特定库 deno run --allow-ffi=/path/to/library.so script.ts
权限组合使用
bash# 组合多个权限 deno run --allow-read --allow-write --allow-net --allow-env=API_KEY app.ts # 使用 --allow-all 授予所有权限(不推荐生产环境) deno run --allow-all app.ts
代码中的权限检查
Deno 提供了 API 来检查当前拥有的权限:
typescript// 检查是否具有读取权限 const canRead = await Deno.permissions.query({ name: "read", path: "/tmp" }); console.log(canRead.state); // "granted", "prompt", or "denied" // 检查是否具有网络权限 const canAccessNet = await Deno.permissions.query({ name: "net" }); console.log(canAccessNet.state); // 请求权限 const netPermission = await Deno.permissions.request({ name: "net" }); if (netPermission.state === "granted") { console.log("Network access granted"); }
权限提示模式
Deno 支持交互式权限提示:
bash# 使用 --prompt 权限标志 deno run --prompt=net script.ts
当脚本尝试访问需要权限的资源时,Deno 会提示用户是否授权。
实际应用示例
1. 文件服务器
typescript// file-server.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); const filePath = `.${url.pathname}`; try { const content = await Deno.readFile(filePath); return new Response(content); } catch { return new Response("File not found", { status: 404 }); } }; await serve(handler, { port: 8000 });
运行:
bashdeno run --allow-read --allow-net file-server.ts
2. API 客户端
typescript// api-client.ts const API_KEY = Deno.env.get("API_KEY"); if (!API_KEY) { throw new Error("API_KEY environment variable not set"); } const response = await fetch("https://api.example.com/data", { headers: { "Authorization": `Bearer ${API_KEY}`, }, }); const data = await response.json(); console.log(data);
运行:
bashdeno run --allow-net --allow-env=API_KEY api-client.ts
3. 文件处理工具
typescript// file-processor.ts const inputFile = Deno.args[0]; const outputFile = Deno.args[1]; const content = await Deno.readTextFile(inputFile); const processed = content.toUpperCase(); await Deno.writeTextFile(outputFile, processed); console.log(`Processed ${inputFile} to ${outputFile}`);
运行:
bashdeno run --allow-read --allow-write file-processor.ts input.txt output.txt
权限最佳实践
- 最小权限原则:只授予必要的权限
- 明确指定资源:使用具体的路径和域名,而不是通配符
- 避免 --allow-all:在生产环境中绝不使用
- 文档化权限需求:在 README 中说明运行脚本所需的权限
- 使用权限检查:在代码中检查权限并提供友好的错误信息
- 环境隔离:在容器或沙箱环境中运行不受信任的代码
安全优势
Deno 的权限系统提供了以下安全优势:
- 防止数据泄露:未经授权无法读取敏感文件
- 防止系统破坏:未经授权无法写入或删除文件
- 防止网络攻击:未经授权无法进行网络请求
- 防止环境泄露:未经授权无法访问环境变量
- 防止命令注入:未经授权无法执行系统命令
- 可审计性:所有权限使用都是显式的,便于审计
与 Node.js 的对比
| 特性 | Deno | Node.js |
|---|---|---|
| 默认权限 | 无权限 | 完全访问 |
| 权限控制 | 命令行标志 | 无内置机制 |
| 安全模型 | 默认拒绝 | 默认允许 |
| 权限粒度 | 细粒度控制 | 无控制 |
| 审计能力 | 显式权限 | 隐式权限 |
Deno 的权限系统为 JavaScript/TypeScript 运行时提供了企业级的安全保障,使其特别适合处理敏感数据和运行不受信任代码的场景。