安全指南

本页面的目标是提供关于如何安全使用 APOC 的指导。不安全地使用 APOC 可能会导致许多常见的软件漏洞,包括安全配置错误敏感数据暴露服务端请求伪造 (SSRF) 以及语言注入

我们的指南建议在安全问题上采取基于原则的方法,并分为三个部分。在第一部分中,我们将探讨我们的总体原则。在第二部分中,我们将讨论如何在执行查询之前为 APOC 创建一个安全的环境。最后,在第三部分,我们将涵盖如何在查询中安全地使用 APOC。

安全原则

本节涵盖的安全原则为安全使用 APOC 提供了指导性规则。如果遇到本页面未涵盖的安全挑战,建议用户遵循下述原则。

最小权限原则

也称为最小特权原则,最小权限原则规定,工作负载应仅被赋予其运行所需的最小权限集。APOC 提供了广泛的功能,任何给定的 APOC 安装都不太可能全部使用。建议用户仅启用严格需要的程序和函数,并禁用所有其他程序和函数。

通过仅启用最少必需的功能,用户将降低因运行易受攻击的程序而产生的风险,同时也能满足其功能需求。

纵深防御原则

也称为冗余原则,纵深防御原则要求用户在软件栈的每一层都确保其安装的安全,即使这样做看起来是冗余的。

APOC 构建在由数据库和操作系统暴露并控制的接口之上。通过使用纵深防御方法来保护 APOC 安装,安装环境将被包裹在多层保护中,从而减轻任何一层保护机制失效的风险。如果安装受到 APOC、数据库以及操作系统的共同保护,那么受保护的工作负载被单一漏洞攻破的可能性就会降低。

安装

本节涵盖为创建 APOC 安全环境所采取的步骤。主要关注在编写查询之前保护 APOC。

保护 Neo4j

由于 APOC 提供的功能构建在数据库之上,除非数据库本身安全,否则安装就不可能是安全的。因此,首要任务是确保数据库安装是安全的,这可以通过遵循现有的数据库安全检查清单来实现。本指南将重新详细介绍检查清单中涵盖的一些步骤。

保护 Neo4j 扩展

APOC 是一个 Neo4j 扩展,其功能比任何特定工作负载可能需要的功能要多得多。与任何 Neo4j 扩展一样,有几种控制机制可以帮助确保仅将所需的函数和过程安装到数据库中。

通过配置设置保护 Neo4j 扩展

数据库提供了可在 conf/neo4j.conf 配置文件中配置的配置设置。配置文件控制哪些程序和函数可以加载到数据库中并被取消限制。控制此行为的配置设置如下所示。

设置 描述 默认

dbms.security.procedures.allowlist

要加载的函数和程序名称列表。

"*"

dbms.security.procedures.unrestricted

允许对数据库进行完全访问的函数和程序名称列表。

""

建议遵守现有的安装指南,该指南规定了如何加载和取消限制工作负载所需的最少程序集。

通过 RBAC 保护 Neo4j 扩展

数据库提供了一种基于角色的访问控制 (RBAC) 机制,以微调允许哪些用户角色执行给定操作。这是 Neo4j 企业版功能,社区版用户无法使用。

基于角色的访问控制还提供了限制导入和加载程序可以从哪些外部站点读取数据的功能。加载权限适用于所有可以提供 URL 的 APOC 程序。

存在关于用户执行任何给定程序能力的执行程序权限。默认情况下,所有用户都有权以其自身的权限级别执行任何程序。这意味着没有读取权限的用户无法通过程序读取数据,没有写入权限的用户无法通过程序写入数据。类似的权限也适用于执行函数

此外,还有关于用户以完全权限执行任何给定程序的提升执行程序权限。这意味着即使原本没有读取或写入数据库权限的用户,如果被授予了提升程序权限,也可以进行操作。提升执行程序权限还会覆盖程序上的任何加载权限,允许访问任何 URL。这些权限等同于执行管理程序权限。类似的权限也适用于提升执行函数

提升执行权限是一项强大的功能,有可能被滥用。有几个强大的 APOC 程序能够针对数据库运行从用户输入派生的完整查询。如果用户被授予了以完全权限执行这些程序中的任何一个的提升权限,这等同于允许用户运行任何 Cypher 查询。

此类程序的示例包括:

建议遵循默认行为,即只允许用户以其自身的权限级别执行程序和函数,并避免在 APOC 中进行提升后的程序执行。当某个角色需要执行特定操作的权限时,通常有其他权限可以授予,以达到所需的限制,而无需依赖提升执行。

保护文件系统

APOC 包含多个可以读取或写入文件系统特定文件的程序。如果配置错误,这些程序可能导致高影响的漏洞,例如敏感数据暴露。如果工作负载需要,用户需要启用程序与文件系统的交互功能,但仅限于特定目录。如果工作负载不需要,用户应完全限制程序与文件系统的交互。

可以从文件系统读取的程序示例包括 apoc.import.*apoc.load.*。可以写入文件系统的程序示例包括 apoc.export.*。允许数据库从文件系统读取的 Cypher 子句示例包括 LOAD CSV

在操作系统级别保护文件系统

从操作系统的角度来看,只有一个进程在执行。APOC 并不作为独立于数据库进程之外的操作系统进程存在。这意味着应用于数据库的所有操作系统限制也将应用于 APOC。因此,数据库的文件权限指南中规定的指导方针也适用于 APOC。

建议将数据库进程配置为仅拥有执行工作负载所需的最小文件系统权限集。这意味着要限制数据库进程,使其仅在必要时能够与文件系统交互,即使如此,也只能与特定目标目录交互,而不是整个文件系统。

在数据库级别保护文件系统

APOC 暴露了配置设置,用于控制是否允许与文件系统交互,以及从哪个目录进行交互。这些设置可以在 conf/apoc.conf 文件中配置,如下所述。

设置 描述 默认

apoc.export.file.enabled

启用向文件系统写入文件。

false

apoc.import.file.enabled

启用从文件系统读取文件。

false

apoc.import.file_use_neo4j_config

APOC 在读取或写入文件系统时将遵守 Neo4j 的配置设置。

true

数据库还暴露了配置设置,用于控制是否允许从文件系统读取文件,以及从哪个目录读取。这些设置可以在 conf/neo4j.conf 文件中配置,如下所述。

设置 描述 默认

dbms.security.allow_csv_import_from_file_urls

启用从文件系统读取文件。

false

server.directories.import

将读取文件的权限限制在给定目录中。

import

当 APOC 验证文件系统交互时,它会执行一系列检查。首先检查是否允许读取或写入。如果允许,它会检查可以执行该操作的目录。

在确定是否允许读取或写入时,APOC 首先验证其自身的配置设置是否已启用,然后检查数据库配置设置是否也已启用。只有在启用了 apoc.import.file_use_neo4j_config 配置设置时,APOC 才会检查数据库配置设置是否也已启用。

在确定允许读取或写入的目录时,APOC 会检查是否启用了 apoc.import.file_use_neo4j_config 配置设置。如果是,它将使用与数据库相同的目录限制。如果未启用此配置设置,则 APOC 可以读取或写入文件系统上的任何位置。

安全建议

建议根据工作负载是否需要读取或写入文件而有所不同。有些工作负载不需要任何文件系统交互,有些只要求数据库能够读取文件,而有些则要求数据库和 APOC 都能够读取文件。

如果工作负载不需要任何文件系统读取或写入权限,则用户不应更改任何配置文件中的设置。默认情况下,Neo4j 和 APOC 查询都不允许读取或写入文件。

如果工作负载仅要求数据库能够读取文件,而不需要 APOC 也具备此能力,则用户应仅通过设置 dbms.security.allow_csv_import_from_file_urls=true 将此能力授予数据库。用户无需对 APOC 配置设置进行任何修改,因为默认情况下它们不允许 APOC 从文件系统读取或写入文件。

如果工作负载要求数据库和 APOC 都能够读写文件系统,则用户仍应尽量保持高限制。虽然这意味着需要在两个配置文件中都启用读写权限,但建议同时调整 APOC 配置设置 apoc.import.file_use_neo4j_config=true 和 Neo4j 配置设置 server.directories.import=import

使用

上一节提供了在执行查询之前保护 APOC 安装的指南。本节将提供关于编写包含高风险 APOC 程序和函数的查询的建议。

Cypher 注入

Neo4j 知识库提供了关于防止 Cypher 注入的优秀入门指南,建议学习这些内容,以便更好地理解与 Cypher 注入相关的挑战。

许多 APOC 程序直接使用 Cypher,并且在底层,它们会构建并执行派生自所接收输入的新查询。这些程序对 APOC 用户构成了额外的挑战,用户需要能够识别它们,并了解它们所能提供的有限安全保证。

在下面的第一个示例中,初始查询调用了 apoc.refactor.rename.label 程序,该程序反过来在后台构建并执行第二个查询。第二个查询获取所有节点,移除一个标签,然后重新附加一个不同的标签。

CALL apoc.refactor.rename.label("Person", "Actor", [])
// executes MATCH (n:`Person`) REMOVE n:`Person` SET n:`Actor`

在下面的第二个示例中,初始查询调用了 apoc.cypher.run 程序,该程序在后台构建并执行第二个查询。第二个查询获取所有节点并返回它们。

CALL apoc.cypher.run("MATCH (n) RETURN n", {})
// executes MATCH (n) RETURN n

上述示例中的两个程序都构建并执行了从接收到的输入中派生的新查询。这两个程序之间唯一的区别是它们接收到的输入。在第一个示例中,程序知道输入代表 Cypher 字面量。在第二个示例中,程序知道输入代表一个完整的 Cypher 查询。第一个示例中的输入可以被净化(sanitize),而第二个示例中的输入则无法被净化。

APOC 保证会净化对应于 Cypher 字面量的输入。然而,对于对应于整个 Cypher 查询的输入,APOC 无法提供相同的保证。在后一种情况下,净化 Cypher 查询的责任委托给用户,建议用户仔细遵循前述的 Cypher 注入指南。

加密函数

APOC 提供了一系列哈希函数,能够将任何对象转换为固定长度的统一字符串。此功能具有多种应用,包括加密和非加密应用。加密应用可能包括消息摘要和密码验证,而非加密应用场景可能包括均匀分发数据和数据去重。将加密上不安全的 APOC 哈希函数用于需要加密安全的场景,可能导致诸如弱加密等高影响漏洞。

决定 APOC 提供的哈希函数是否在加密上安全的关键因素是它是否具有不可逆性。如果一个哈希函数在给定输入时很容易计算哈希值,但在给定哈希值时很难计算出原始输入,则认为它是不可逆的。

适用于加密应用的函数示例
安全建议

使用哈希函数时,重要的是首先分析工作负载是否需要不可逆哈希,然后确定给定 APOC 函数提供的哈希算法是否能够提供此属性。建议及时了解最新的安全研究,因为有时会在曾经被认为是不可逆的哈希函数中发现缺陷。

如果工作负载不需要不可逆哈希,则优先使用 APOC 提供的加密上不安全的哈希函数,因为它们的执行速度通常更快。