deno.com
本页内容

安全与权限

默认情况下,Deno 是安全的。除非你明确启用,否则使用 Deno 运行的程序无法访问敏感的 API,例如文件系统访问、网络连接或环境访问。你必须使用命令行标志或运行时权限提示显式授予对这些资源的访问权限。这与 Node 有很大的不同,在 Node 中,依赖项会自动被授予对所有系统 I/O 的完全访问权限,这可能会在你的项目中引入隐藏的漏洞。

在使用 Deno 运行完全不受信任的代码之前,请阅读下面关于执行不受信任的代码的部分。

关键原则 跳转到标题

在深入了解权限的具体细节之前,重要的是要理解 Deno 安全模型的关键原则

  • 默认情况下无 I/O 访问权限:在 Deno 运行时中执行的代码无权读取或写入文件系统上的任意文件、发出网络请求或打开网络监听器、访问环境变量或派生子进程。
  • 对相同特权级别的代码执行没有限制:Deno 允许通过多种方式执行任何代码(JS/TS/Wasm),包括 evalnew Function、动态导入和 Web Workers,这些代码与调用者具有相同的特权级别,并且对代码的来源(网络、npm、JSR 等)几乎没有限制。
  • 同一应用程序的多次调用可以共享数据:Deno 提供了一种机制,允许同一应用程序的多次调用通过内置的缓存和 KV 存储 API 共享数据。不同的应用程序无法看到彼此的数据。
  • 在同一线程上执行的所有代码共享相同的特权级别:在同一线程上执行的所有代码共享相同的特权级别。在同一线程内,不同的模块不可能具有不同的特权级别。
  • 代码不能在未经用户同意的情况下提升其特权:在 Deno 运行时中执行的代码不能在未经用户通过交互式提示或调用时标志明确同意的情况下提升其特权。
  • 初始静态模块图可以在没有限制的情况下导入本地文件:在初始静态模块图中导入的所有文件都可以在没有限制的情况下导入,即使没有为该文件显式授予读取权限也是如此。这不适用于任何动态模块导入。

这些关键原则旨在提供一个环境,用户可以在其中执行代码,并将对主机或网络的损害风险降到最低。安全模型旨在易于理解,并在运行时和在其中执行的代码之间提供清晰的关注点分离。安全模型由 Deno 运行时强制执行,并且不依赖于底层操作系统。

权限 跳转到标题

默认情况下,大多数系统 I/O 的访问都被拒绝。有些 I/O 操作即使在默认情况下也是在有限的能力内允许的。这些操作将在下面描述。

要启用这些操作,用户必须显式地授予 Deno 运行时权限。这可以通过将 --allow-read--allow-write--allow-net--allow-env--allow-run 标志传递给 deno 命令来完成。

在脚本执行期间,当运行时提示时,用户还可以显式地授予对特定文件、目录、网络地址、环境变量和子进程的权限。如果 stdout/stderr 不是 TTY,或者当 --no-prompt 标志传递给 deno 命令时,则不会显示提示。

用户还可以使用 --deny-read--deny-write--deny-net--deny-env--deny-run 标志显式地禁止访问特定资源。这些标志优先于 allow 标志。例如,如果你允许网络访问但拒绝访问特定域,则 deny 标志将优先。

Deno 还提供了一个 --allow-all 标志,该标志授予脚本所有权限。这会完全禁用安全沙箱,应谨慎使用。--allow-all 具有与在 Node.js 中运行脚本相同的安全属性(即无)。

定义:-A, --allow-all

deno run -A script.ts
deno run --allow-all script.ts

文件系统访问 跳转到标题

默认情况下,执行的代码无法读取或写入文件系统上的任意文件。这包括列出目录的内容、检查给定文件是否存在以及打开或连接到 Unix 套接字。

读取文件的访问权限使用 --allow-read(或 -R)标志授予,写入文件的访问权限使用 --allow-write(或 -W)标志授予。这些标志可以与路径列表一起指定,以允许访问特定文件或目录。

定义:--allow-read[=<PATH>...]-R[=<PATH>...]

# Allow all reads from file system
deno run -R script.ts
# or 
deno run --allow-read script.ts

# Allow reads from file foo.txt and bar.txt only
deno run --allow-read=foo.txt,bar.txt script.ts

定义:--deny-read[=<PATH>...]

# Allow reading files in /etc but disallow reading /etc/hosts
deno run --allow-read=/etc --deny-read=/etc/hosts script.ts

# Deny all read access to disk, disabling permission prompts for reads.
deno run --deny-read script.ts

定义:--allow-write[=<PATH>...]-W[=<PATH>...]

# Allow all writes to file system
deno run -W script.ts
# or 
deno run --allow-write script.ts

# Allow writes to file foo.txt and bar.txt only
deno run --allow-write=foo.txt,bar.txt script.ts

定义:--deny-write[=<PATH>...]

# Allow reading files in current working directory 
# but disallow writing to ./secrets directory.
deno run --allow-write=./ --deny-write=./secrets script.ts

# Deny all write access to disk, disabling permission prompts.
deno run --deny-write script.ts

Deno 中的某些 API 是使用底层文件系统操作实现的,即使它们不提供对特定文件的直接读/写访问权限。这些 API 会读取和写入磁盘,但不需要任何显式的读/写权限。这些 API 的一些示例包括

  • localStorage
  • Deno KV
  • caches
  • Blob

由于这些 API 是使用文件系统操作实现的,因此用户可以使用它们来消耗文件系统资源(如存储空间),即使他们没有直接访问文件系统的权限。

在模块加载期间,Deno 可以从磁盘加载文件。这有时需要显式权限,有时默认允许

  • 从入口点模块以静态分析方式导入的所有文件默认允许读取。这包括静态 import 语句和动态 import() 调用,其中参数是指向特定文件或文件目录的字符串字面量。可以使用 deno info <entrypoint> 打印此列表中的完整文件列表。
  • 以静态分析方式无法动态导入的文件需要运行时读取权限。
  • node_modules/ 目录中的文件默认允许读取。

当从网络获取模块或将 TypeScript 代码转译为 JavaScript 代码时,Deno 使用文件系统作为缓存。这意味着即使用户未显式授予读/写权限,Deno 也可能消耗文件系统资源(如存储空间)。

网络访问 跳转到标题

默认情况下,执行的代码无法发出网络请求、打开网络监听器或执行 DNS 解析。这包括发出 HTTP 请求、打开 TCP/UDP 套接字以及监听传入的 TCP 或 UDP 连接。

网络访问权限使用 --allow-net 标志授予。此标志可以与 IP 地址或主机名列表一起指定,以允许访问特定网络地址。

定义:--allow-net[=<IP_OR_HOSTNAME>...]-N[=<IP_OR_HOSTNAME>...]

# Allow network access
deno run -N script.ts
# or
deno run --allow-net script.ts

# Allow network access to github.com and jsr.io
deno run --allow-net=github.com,jsr.io script.ts

# A hostname at port 80:
deno run --allow-net=example.com:80 script.ts

# An IPv4 address on port 443
deno run --allow-net=1.1.1.1:443 script.ts

# An IPv6 address, all ports allowed
deno run --allow-net=[2606:4700:4700::1111] script.ts

定义:--deny-net[=<IP_OR_HOSTNAME>...]

# Allow access to network, but deny access 
# to github.com and jsr.io
deno run --allow-net --deny-net=github.com,jsr.io script.ts

# Deny all network access, disabling permission prompts.
deno run --deny-net script.ts

在模块加载期间,Deno 可以从网络加载模块。默认情况下,Deno 允许使用静态和动态导入从以下位置加载模块,而无需显式网络访问权限

  • https://deno.land/
  • https://jsr.deno.org.cn/
  • https://esm.sh/
  • https://raw.githubusercontent.com
  • https://gist.githubusercontent.com

这些位置是受信任的“公共利益”注册表,预计不会通过 URL 路径启用数据泄露。你可以使用 --allow-imports 标志添加更多受信任的注册表。

此外,Deno 允许通过 npm: 说明符导入任何 NPM 包。

Deno 还会每天最多一次向 https://dl.deno.land/ 发送请求,以检查 Deno CLI 的更新。可以使用 DENO_NO_UPDATE_CHECK=1 环境变量禁用此功能。

环境变量 跳转到标题

默认情况下,执行的代码无法读取或写入环境变量。这包括读取环境变量和设置新值。

环境变量的访问权限使用 --allow-env 标志授予。此标志可以与环境变量列表一起指定,以允许访问特定环境变量。从 Deno v2.1 开始,你现在可以指定后缀通配符,以允许“作用域”访问环境变量。

定义:--allow-env[=<VARIABLE_NAME>...]-E[=<VARIABLE_NAME>...]

# Allow access to all environment variables
deno run -E script.ts
# or
deno run --allow-env script.ts

# Allow HOME and FOO environment variables
deno run --allow-env=HOME,FOO script.ts

# Allow access to all environment variables starting with AWS_
deno run --allow-env="AWS_*" script.ts

定义:--deny-env[=<VARIABLE_NAME>...]

# Allow all environment variables except 
# AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
deno run \
  --allow-env \
  --deny-env=AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY \
  script.ts

# Deny all access to env variables, disabling permission prompts.
deno run --deny-env script.ts

Windows 用户请注意:环境变量在 Windows 上不区分大小写,因此 Deno 也以不区分大小写的方式匹配它们(仅在 Windows 上)。

Deno 在启动时会读取某些环境变量,例如 DENO_DIRNO_COLOR请参阅完整列表)。

无论代码是否被授予读取环境变量的权限,NO_COLOR 环境变量的值对在 Deno 运行时中运行的所有代码都是可见的。

系统信息 跳转到标题

默认情况下,执行的代码无法访问系统信息,例如操作系统版本、系统正常运行时间、负载平均值、网络接口和系统内存信息。

系统信息的访问权限使用 --allow-sys 标志授予。此标志可以与允许的接口列表一起指定,列表来自以下内容:hostnameosReleaseosUptimeloadavgnetworkInterfacessystemMemoryInfouidgid。这些字符串映射到 Deno 命名空间中提供操作系统信息的函数,例如 Deno.systemMemoryInfo

定义:--allow-sys[=<API_NAME>...]-S[=<API_NAME>...]

# Allow all system information APIs
deno run -S script.ts
# or
deno run --allow-sys script.ts

# Allow systemMemoryInfo and osRelease APIs
deno run --allow-sys="systemMemoryInfo,osRelease" script.ts

定义:--deny-sys[=<API_NAME>...]

# Allow accessing all system information but "networkInterfaces"
deno run --allow-sys --deny-sys="networkInterfaces" script.ts

# Deny all access to system information, disabling permission prompts.
deno run --deny-sys script.ts

子进程 跳转到标题

默认情况下,在 Deno 运行时中执行的代码无法派生子进程,因为这将构成违反代码不能在未经用户同意的情况下提升其特权的原则。

Deno 提供了一种执行子进程的机制,但这需要用户的显式许可。这可以使用 --allow-run 标志完成。

你从程序中派生的任何子进程都独立于授予父进程的权限运行。这意味着子进程可以访问系统资源,而与你授予派生它的 Deno 进程的权限无关。这通常被称为特权提升。

因此,请确保仔细考虑是否要授予程序 --allow-run 访问权限:它实际上会使 Deno 安全沙箱失效。如果你确实需要派生特定的可执行文件,则可以通过限制 Deno 进程可以启动的程序来降低风险,方法是将特定的可执行文件名传递给 --allow-run 标志。

定义:--allow-run[=<PROGRAM_NAME>...]

# Allow running all subprocesses
deno run --allow-run script.ts

# Allow running "curl" and "whoami" subprocesses
deno run --allow-run="curl,whoami" script.ts

注意

你可能永远都不想使用 --allow-run=deno,除非父进程具有 --allow-all,因为能够派生 deno 进程意味着脚本可以派生另一个具有完全权限的 deno 进程。

定义:--deny-run[=<PROGRAM_NAME>...]

# Allow running running all programs, but "whoami" and "ps".
deno run --allow-run --deny-run="whoami,ps" script.ts

# Deny all access for spawning subprocessing, disabling
# permission prompts.
deno run --deny-run script.ts

默认情况下,npm 包在安装期间(例如使用 deno install)不会执行其安装后脚本,因为这将允许任意代码执行。当使用 --allow-scripts 标志运行时,npm 包的安装后脚本将作为子进程执行。

FFI (外部函数接口) 跳转到标题

Deno 提供了一种从 Deno 运行时中执行以其他语言(例如 Rust、C 或 C++)编写的代码的机制。这是使用 Deno.dlopen API 完成的,该 API 可以加载共享库并从中调用函数。

默认情况下,执行的代码无法使用 Deno.dlopen API,因为这将构成违反代码不能在未经用户同意的情况下提升其特权的原则。

除了 Deno.dlopen 之外,FFI 还可以通过 Node-API (NAPI) 本机插件使用。默认情况下也不允许这些插件。

Deno.dlopen 和 NAPI 本机插件都需要使用 --allow-ffi 标志显式许可。此标志可以与文件或目录列表一起指定,以允许访问特定动态库。

与子进程一样,动态库不在沙箱中运行,因此不具有与其加载到的 Deno 进程相同的安全限制。因此,请极其谨慎地使用。

定义:--allow-ffi[=<PATH>...]

# Allow loading dynamic all libraries
deno run --allow-ffi script.ts

# Allow loading dynamic libraries from a specific path
deno run --allow-ffi=./libfoo.so script.ts

定义:--deny-ffi[=<PATH>...]

# Allow loading all dynamic libraries, but ./libfoo.so
deno run --allow-ffi --deny-ffi=./libfoo.so script.ts

# Deny loading all dynamic libraries, disabling permission prompts.
deno run --deny-ffi script.ts

从 Web 导入 跳转到标题

允许从 Web 导入代码。默认情况下,Deno 限制你可以从中导入代码的主机。静态导入和动态导入都是如此。

如果你想动态导入代码,无论是使用 import() 还是 new Worker() API,都需要授予额外的权限。从本地文件系统导入需要 --allow-read,但 Deno 也允许从 http:https: URL 导入。在这种情况下,你需要指定显式的 --allow-import 标志

# allow importing code from `https://example.com`
$ deno run --allow-import=example.com main.ts

默认情况下,Deno 允许从以下主机导入源

  • deno.land
  • esm.sh
  • jsr.io
  • cdn.jsdelivr.net
  • raw.githubusercontent.com
  • gist.githubusercontent.com

仅允许使用 HTTPS 导入

默认情况下,此允许列表适用于静态导入,如果指定了 --allow-import 标志,则默认也适用于动态导入。

# allow dynamically importing code from `https://deno.land`
$ deno run --allow-import main.ts

请注意,为 --allow-import 指定允许列表将覆盖默认主机列表。

代码求值 跳转到标题

Deno 对相同特权级别的代码执行没有限制。这意味着在 Deno 运行时中执行的代码可以使用 evalnew Function,甚至动态导入或 Web Workers 来执行任意代码,这些代码与调用 evalnew Function 或动态导入或 Web Worker 的代码具有相同的特权级别。

此代码可以托管在网络上、位于本地文件中(如果授予了读取权限),或者以纯文本形式存储在调用 evalnew Function 或动态导入或 Web Worker 的代码中的字符串中。

执行不受信任的代码 跳转到标题

虽然 Deno 提供了旨在保护主机和网络免受损害的安全功能,但不受信任的代码仍然是可怕的。在执行不受信任的代码时,拥有多层防御非常重要。下面概述了一些执行不受信任的代码的建议,我们建议在执行任意不受信任的代码时使用所有这些建议

  • 以有限的权限运行 deno,并预先确定实际需要运行的代码(并使用 --frozen lockfile 和 --cached-only 防止加载更多代码)。
  • 使用操作系统提供的沙箱机制,如 chrootcgroupsseccomp 等。
  • 使用沙箱环境,如 VM 或 MicroVM(gVisor、Firecracker 等)。

你找到你需要的内容了吗?

隐私政策