这里:https:

关于在 log4j 中使用字符串连接,他们是这样说的:

更重要的是,这种方法很容易受到攻击!想象一下用户提供的 userId 包含以下内容:不存在的参数的占位符会触发失败:{} {} {dangerousLookup}

有人能解释一下为什么这很危险吗?我不明白字符串连接与这个“dangerousLookup”有什么关系。

2

  • 1
    文档试图告诉您,log4j 将评估格式字符串中的各种表达式(例如${env:SECRET_ENV_VAR})。因此,您不应将任何您无法控制的内容连接到它。


    – 

  • @Violet: 因为格式字符串中的表达式未被评估。


    – 


最佳答案
3

我认为文档非常清楚

假设用户提供的 userId 包含以下内容:不存在的参数占位符会触发失败:{} {}

如果你有以下记录器并且通过了上面的userId

LOGGER.info("failed for user ID: " + userId);

代码执行将会中断,因为记录器将以内容的形式出现,"failed for user ID: placeholders for non-existing args to trigger failure: {} {} "并且记录器将引发异常,即未提供所需的 2 个参数。

此外,正如评论者Violet所提到的,有人甚至可以传递带有动态评估的连接参数。某些内容不应该打印在日志中,例如,${env:SECRET_ENV_VAR}危险查找中提到的情况。

您还可以将以上两者结合起来,看看某人如何使用记录器中抛出的异常,该记录器向用户提供异常的信息,以获取原本应该隐藏的信息。

这就是为什么他们建议要求信息的布局是具体的,而不是控制论点。例如

LOGGER.info("failed for user ID `{}`", userId);

现在,记录器将仅打印布局中描述的提供的参数userId,并且不会将其与布局混合,以便保护您免受上述和其他几个问题的影响。

3

  • 1
    在某些情况下,您甚至可以引用 JNDI 并从远程机器加载类。


    – 

  • @talexmovedtoCodidact:确实对字符串连接和参数化日志记录产生了同等影响。


    – 

  • @Panagiotis Bougioukos:由于参数数量不匹配而引发的抛出行为在2.23.0Log4j Core 版本中短暂出现过,并在版本中被警告所取代2.23.1(请参阅)。


    – 

文档令人困惑,感谢您报告问题。字符串连接和参数化日志记录本身都没有安全隐患。但是,当您将两者混合使用时,就会出现问题:

logger.warn("Login failed for user '" + user + "' from remote address '{}'.", remoteIp);

如果攻击者{}以用户身份发送,您将收到类似以下消息:

Login failed for user '192.0.2.1' from remote address '{}'.

这是一个轻微的漏洞,但它可能会给自动日志解析器带来问题或使管理员感到困惑。如果您使用参数化日志记录,则格式字符串应该是编译时常量

如果仅使用字符串连接或参数化日志记录,则不会出现此类问题:

// String concatenation
logger.warn("Login failed for user '" + user + "' from remote address '" + remoteIp + "'.");
// Parameterized logging
logger.warn("Login failed for user '{}' from remote address '{}'.", user, remoteIp);

两者都将返回:

Login failed for user '{}' from remote address '192.0.2.1'.

注意:如果日志文件被自动解析,并且日志消息的参数有意义,建议使用

如果您使用字符串连接(如链接的示例所示),并且连接的变量的值可能来自您无法控制的外部源,则攻击者可以使该值具有占位符,log4j 的日志记录方法将照常处理这些占位符,并且该占位符可能包含利用 log4j 中的漏洞的恶意代码。然后,该恶意代码将被执行,攻击者将获得利用该漏洞的好处。这就是其含义{dangerouslookup},大概是指 log4j 中过去使用 JNDI 查找作为攻击媒介的漏洞。