安全指南

本页旨在提供有关如何安全使用 APOC 的指南。APOC 的不安全使用可能导致许多常见的软件漏洞,包括 安全配置错误敏感数据暴露服务器端请求伪造语言注入

我们的指南建议采取基于原则的方法来处理安全问题,并分为三个部分。第一部分,我们将首先探讨我们的总体原则。第二部分,我们将讨论在执行查询之前如何为 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 企业版的一项功能,社区版用户不可用。

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

还有关于用户以完全特权执行给定过程的能力的 执行提升过程 特权。这意味着,如果用户被授予提升过程特权,他们就可以读取或写入数据库,而这些操作在其他情况下是不允许的。这些特权等同于 执行管理过程 特权。对于 执行提升函数 存在类似的特权。

执行提升特权是一项强大的功能,有可能被滥用。有几个强大的 APOC 过程能够根据用户输入对数据库运行整个查询。如果用户被授予以完全特权执行这些过程中的任何一个的提升特权,这相当于授予用户运行任何 Cypher 查询的能力。

此类过程的示例包括

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

保护文件系统的安全

APOC 包含几个可以读取或写入文件系统上特定文件的过程。如果配置不当,这些过程可能导致高影响力的漏洞,例如 敏感数据暴露。如果工作负载需要,用户需要启用过程才能与文件系统交互,但只能在特定目录中。如果工作负载不需要与文件系统交互,用户应完全限制过程与文件系统交互的能力。

可以从文件系统读取的过程示例包括 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.uuid.install 过程,该过程反过来在幕后构建并执行第二个查询。第二个查询获取所有节点,删除一个标签,然后重新附加一个不同的标签。

CALL apoc.uuid.install("Person", {})
// executes a query similar to this: `MATCH (n:Person) SET n.uuid`

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

CALL apoc.cypher.runFile("test.cypher", {})
// executes `MATCH (n) RETURN n`

上述示例中的两个过程都根据接收到的输入构建并执行新查询。这两个过程之间的唯一区别在于它们接收到的输入。在第一个示例中,过程知道输入表示 Cypher 字面量。在第二个示例中,过程知道输入表示整个 Cypher 查询。第一个示例中的输入可以进行净化,而第二个示例中的输入则无法净化。

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

不需要净化的过程示例
© . All rights reserved.