【问题标题】:Handle Neo4j Injection for dynamic labels and relationships处理动态标签和关系的 Neo4j 注入
【发布时间】:2017-10-07 00:39:57
【问题描述】:

我一直在为 c# 使用带有 neo4j 客户端的 WithParams,但 withParams 不适用于标签或关系类型。

我当时想到的替代方法是连接我想要基于某个标签作为参数形成的字符串,然后构造密码查询。那就是:

string optionalMatchString = $"p =(n1)-[{relationshipsString}]-(n2)";
graphClient.Cypher.Match("(n1)")
           .Where((Node n1) => n1.Identifier == identifier)
           .OptionalMatch(optionalMatchString)

你可以猜到,relationshipString 是传递给我的一个参数。如果我使用 WithParams 查询将不会替换参数,所以现在我连接字符串,但这很容易受到攻击......(是吗?)

我了解了APOC,看到了this issue

这是我看到的一个例子this

CALL db.labels() yield label
call apoc.cypher.run("MATCH (n:`"+label+"`) RETURN keys(n) as keys LIMIT 1",{}) yield value as row
RETURN label, row.keys as keys

显然,有一个名为 cypher.run 的 APOC 程序,我可以在其中将我的标签(或相关的关系)作为变量(来自参数),但据我所见,它们只是连接一个字符串......那和我一直在做的一样吗?或者 APOC 是否以某种方式在查询之上执行其他操作? APOC 程序对注射是否“安全”?

【问题讨论】:

  • 您的数据库中是否有一组预定义的关系?如果这样做,您可以将关系类型列入白名单,即检查它是否是可能的关系类型的成员。如果不是,请拒绝该请求。
  • 我有它作为备份,但我不想绕过那条路线。必须有一个 wat 来避免用这个来注入 neo4j...

标签: c# stored-procedures neo4j neo4jclient neo4j-apoc


【解决方案1】:

如果您的数据库中有一组预定义的关系,您只需将关系类型列入白名单,即检查它是否是可能的关系类型的成员。

如果由于某种原因这对您不起作用,您仍然可以将标签作为参数传递并使用 Cypher 中的 labels 方法。但是,这会损害性能,因为数据库会查询所有节点并通过过滤操作检查它们的类型。

我知道这是一个 C# 问题,但让我分享一些 Java 代码来重现这个问题:

GraphDatabaseService gds = new TestGraphDatabaseFactory()
    .newImpermanentDatabaseBuilder().newGraphDatabase();
ApocHelper.registerProcedure(gds, Cypher.class);
gds.execute("CREATE (:MyLabel {attr1: 'hello'})");
Result result = gds.execute(
    "CALL apoc.cypher.run(" +
        "'MATCH (n) WHERE $label1 IN labels(n) RETURN keys(n) as keys LIMIT 1', " +
        "{label1: $label2}" +
    ") " +
    "YIELD value AS row " +
    "RETURN row",
    ImmutableMap.of("label2", "MyLabel")
);
System.out.println(result.next());

这会返回:

{row={keys=[attr1]}}

但同样,这是一个学术解决方案,我真的想不出一个最好的用例。

(与此问题同时,APOC issue 中也有讨论。)

【讨论】:

    【解决方案2】:

    编辑

    存在数据库被擦除的风险(请参阅下面来自 @Gabor 的 cmets),但我仍然会沿着 Enum 的路线解决这个问题。但是,从那个观点来看,Gabor 的答案更好。


    APOC 正在做与您相同的事情,因此您不会从使用它中获得任何好处。我认为这里的关键是查看您需要的保护级别。

    接受您的查询,(我对RETURN 位进行了猜测)归结为:

    MATCH (n) WHERE n.Id = {p0} OPTIONAL MATCH (n)-[:{injectionPoint}]->(n1) RETURN n, n1

    假设攻击者知道以下信息,让我们看看我们可以从中得到什么:

    • 您返回的格式(即您从nn1 获得的信息)
    • 您的查询结构如何,即知道您正在返回一个名为 n1 的节点

    他们可以尝试添加另一个查询,即注入如下内容:

    "ORIGINAL]->(n1) RETURN n1; MATCH (n) RETURN labels(n);"

    但这不起作用有两个原因,一是您调用的 Cypher 端点一次只接受一个查询,二是来自 MATCH (n) RETURN labels(n) 的响应与您的响应不匹配当前正在获取,因此会出错。

    您面临的最大风险是有人能够通过传递不同的关系类型直到他们得到响应来制定您的模型 - 不过这需要很长时间,所以可能不是这样的问题。

    攻击者确实必须知道上面的信息才能解决这个问题,但如果没有这些信息,他们只会在黑暗中刺伤。如果他们确实拥有该信息 - 那么他们可能已经知道他们可以从您的数据库中获得什么。

    如果你把你正在做的事情放到一个 SQL 上下文中,你会从 any 表中请求一些东西,带有一个 ID,然后通过提供一个表名,查找对于任何连接到的东西。你会在 SQL 中这样做吗?另外 - 如果没有标签,您正在扫描 整个 数据库以获取此信息,这将非常缓慢。

    我认为您最好将标签添加到其中一个节点(如果不是同时添加)至少,并使用诸如 Enum 之类的东西来表示您允许的关系 - 我很感激这对于大数。

    【讨论】:

    • 如果攻击者只想擦除您的数据库会发生什么?以这个字符串为例:ORIGINAL]->(n1) WITH count(*) AS dummy MATCH (n) DETACH DELETE (n);
    • 由于上述相同的原因,该字符串不起作用 - 您必须尝试一次执行 2 个密码查询...
    • 不,这将是一个 Cypher 查询。 WITH count(*) AS dummy 始终只返回一行,因此您可以有效地将两个查询链接到一个查询中。
    • 是的,你是对的,由于C# 在这种情况下的工作方式,你输入的密码不起作用,但你可以得到这个结果。这确实是一种注射风险——我的错!
    猜你喜欢
    • 2014-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 2018-01-17
    • 1970-01-01
    • 2014-04-15
    相关资源
    最近更新 更多