DuckDB 是一个强大的分析型数据库引擎。它可以读写文件、访问网络、加载扩展程序以及使用系统资源。像任何强大的工具一样,在处理敏感数据或在共享环境中使用时,这些功能需要进行适当的配置。
本页面记录了 DuckDB 的安全模型和与安全相关的设置。正确的配置取决于您的用例、环境和威胁模型。如果您计划将 DuckDB 嵌入到您的应用程序中,请同时参阅 “嵌入 DuckDB” 页面。
不可信输入
不可信的 SQL 输入
警告:请将 DuckDB 中的 SQL 视为 Bash 或 Python 中的代码。在没有适当沙箱保护的情况下,请勿执行来自不可信来源的 SQL。
DuckDB 以运行它的用户的全部权限执行 SQL,这就像 shell 或脚本解释器(例如 bash 或 Python)一样。正如您不会在没有沙箱保护的情况下运行不可信的 shell 脚本或 Python 程序一样,对于 DuckDB 中的 SQL 也应保持同样的谨慎。
如果您的应用程序必须执行来自不可信来源的 SQL,请在运行不可信代码时使用额外的安全防护措施,例如:
- 使用 duckdb-wasm 进行沙箱隔离
- 在隔离的容器中运行 DuckDB(例如具有受限功能的 Docker)
- 使用具有最小权限的虚拟机或独立进程
- 应用操作系统级别的沙箱保护
- 使用网络隔离以防止数据泄露
- 在应用程序层面实施严格的查询超时限制
本页所述的设置提供了深度防御 (defense-in-depth) 并可以限制某些功能,但它们不能替代适当的沙箱保护。还要记住,沙箱保护不仅应从安全角度考虑,还应考虑防止拒绝服务 (DoS) 攻击:恶意输入很容易导致 DuckDB 消耗过多的资源,例如内存、磁盘、CPU 或网络。
不可信的非 SQL 输入
警告:即使是输入到 DuckDB 的非 SQL 输入也可能轻易产生意想不到的后果。在构建对安全敏感的 DuckDB 应用程序时,请务必确保您充分了解将不可信输入馈送给 DuckDB 的影响。
除了 SQL,DuckDB 还有几个可用于与数据库交互的非 SQL API。例如,Python 中有一个关系 API,允许以编程方式构建查询。
这些 API 接受用户输入,如文件路径、表名、列名和过滤表达式。虽然它们不执行原始 SQL 字符串,但它们仍然会触发 DuckDB 操作,从而可能读取文件、访问网络和使用系统资源。
非 SQL API 的示例注意事项
- 文件路径:像
duckdb.read_csv(path)或duckdb.read_parquet(path)这样的函数接受文件路径。攻击者控制的路径可能读取敏感文件(例如/etc/passwd)或访问远程 URL。 - 表名和列名:虽然这些通常是标识符而不是可执行代码,但未经清理的输入可能导致意外行为或信息泄露。
- 过滤表达式:某些 API 接受会被编译为 DuckDB 表达式的过滤表达式,这些表达式通常支持包含任意 SQL 的子查询。请像对待 SQL 一样谨慎对待它们。
建议
- 在将所有用户提供的输入传递给 DuckDB API 之前,请对其进行验证和清理。
- 当从不可信来源接受输入时,应用与处理不可信 SQL 相同的沙箱原则。
- 仔细阅读所有所用函数的文档,以确保您了解在特定用例下,该函数是否可以安全地处理不可信输入。
扩展
DuckDB 具有灵活的扩展机制,可以添加新文件格式、函数和远程文件系统访问等功能。扩展程序以与 DuckDB 进程本身相同的权限运行,因此在对安全敏感的环境中需要仔细考虑。
自动加载
当某些 SQL 语句需要时,DuckDB 可以自动加载核心扩展。为了保持对加载哪些扩展的完全控制,您可以禁用自动加载:
SET autoload_known_extensions = false;
SET autoinstall_known_extensions = false;
核心扩展与社区扩展
DuckDB 扩展分为两类:
- 核心扩展:由 DuckDB 团队维护并提供全面支持。这些包括
parquet、json和httpfs等扩展。 - 社区扩展:由第三方贡献,并通过
INSTALL extension_name FROM community安装。这些不是由 DuckDB 团队维护的,因此请仅从您信任的来源安装社区扩展。
要完全禁用社区扩展:
SET allow_community_extensions = false;
报告漏洞
如果您发现潜在漏洞,请通过 GitHub 机密报告。
限制 DuckDB 功能的设置
本节记录的设置可以为 DuckDB 部署提供额外的加固。但是,在所有配置中,不应将其视为全面的安全机制。这些设置旨在作为深度防御措施,以限制潜在安全问题的影响,但它们无法提供针对所有攻击向量的全面保护,尤其是在执行不可信 SQL 时。在处理不可信输入以获得稳健的安全性时,请将这些设置与操作系统或容器级别的适当沙箱结合使用,如“不可信的 SQL 输入”部分所述。
安全模式 (CLI)
DuckDB 的 CLI 客户端支持“安全模式”,这可以防止 DuckDB 访问除数据库文件以外的外部文件。可以通过命令行参数或点命令激活此模式。
duckdb -safe ...
.safe_mode
限制文件访问
DuckDB 可以通过其 CSV 解析器的 read_csv 函数列出目录和读取任意文件,或通过 read_text 函数读取文本。这使得读取本地文件系统成为可能,例如:
SELECT *
FROM read_csv('/etc/passwd', sep = ':');
禁用文件访问
可以通过两种方式禁用文件访问。首先,您可以禁用单个文件系统。例如:
SET disabled_filesystems = 'LocalFileSystem';
其次,您可以通过将 enable_external_access 选项设置为 false 来完全禁用外部访问。
SET enable_external_access = false;
此设置意味着:
ATTACH无法附加到文件中的数据库。COPY无法读写文件。read_csv、read_parquet、read_json等函数无法从外部源读取数据。
allowed_directories 和 allowed_paths 选项
您可以使用 allowed_directories 和 allowed_paths 选项分别限制 DuckDB 对特定目录或文件的访问。这些选项允许对文件系统进行细粒度的访问控制。例如,您可以设置 DuckDB 仅使用 /tmp 目录。
SET allowed_directories = ['/tmp'];
SET enable_external_access = false;
FROM read_csv('test.csv');
应用此设置后,DuckDB 将拒绝读取当前工作目录中的文件。
Permission Error:
Cannot access file "test.csv" - file system operations are disabled by configuration
锁定配置
出于安全原因,与安全相关的配置设置通常会自动锁定。例如,虽然我们可以使用 SET allow_community_extensions = false 禁用社区扩展,但在不重启数据库的情况下,我们无法在事后重新启用它们。尝试这样做会导致错误:
Invalid Input Error: Cannot upgrade allow_community_extensions setting while database is running
这可以防止重新启用那些已被明确禁用的设置。
然而,许多配置设置(例如资源约束)不会自动锁定。如果您允许用户在您自己的硬件上不受限制地运行 SQL 语句,您可能需要在完成自己的配置后考虑使用以下命令锁定配置:
SET lock_configuration = true;
这可以防止从那一刻起修改任何配置设置。
密钥 (Secrets)
密钥用于管理登录 AWS 或 Azure 等第三方服务的凭据。DuckDB 可以使用 duckdb_secrets() 表函数显示密钥列表。默认情况下,这会遮盖所有敏感信息(如安全密钥)。可以设置 allow_unredacted_secrets 选项以显示安全密钥中包含的所有信息。如果您在运行不可信的 SQL 输入,建议不要开启此选项。
查询可以访问在“密钥管理器”(Secrets Manager) 中定义的密钥。例如,如果定义了一个用于认证具有特定 AWS S3 存储桶写入权限的用户的密钥,则查询可以写入该存储桶。这适用于持久密钥和临时密钥。
持久密钥以未加密的二进制格式存储在磁盘上。它们具有与 SSH 密钥相同的权限 600,即只有运行 DuckDB(父)进程的用户可以读取和写入它们。
预处理语句以防止 SQL 注入
与其他 SQL 数据库一样,建议在 DuckDB 中使用预处理语句来防止 SQL 注入。
重要提示:当您控制查询结构但接受不可信的数据值(例如用户提供的搜索词或 ID)时,预处理语句可以防止 SQL 注入。如果用户能够提供 SQL 查询本身,这等同于允许他们运行任意代码 —— 请参阅“不可信的 SQL 输入”。
因此,避免在查询中拼接字符串:
import duckdb
duckdb.execute("SELECT * FROM (VALUES (32, 'a'), (42, 'b')) t(x) WHERE x = " + str(42)).fetchall()
相反,请使用预处理语句:
import duckdb
duckdb.execute("SELECT * FROM (VALUES (32, 'a'), (42, 'b')) t(x) WHERE x = ?", [42]).fetchall()
约束资源使用
DuckDB 可能会消耗大量的 CPU、RAM 和磁盘空间。可以通过限制这些资源来控制 DuckDB 实例的使用情况。
例如,可以使用以下命令设置 DuckDB 可以使用的 CPU 线程数:
SET threads = 4;
其中 4 是允许的线程数。
也可以限制最大内存 (RAM) 使用量,例如:
SET memory_limit = '4GB';
可以使用以下命令限制临时文件目录的大小:
SET max_temp_directory_size = '4GB';
权限
避免以 root 用户身份运行 DuckDB(例如使用 sudo)。没有正当理由以 root 身份运行 DuckDB。