CodeQL 文档

日志注入

ID: java/log-injection
Kind: path-problem
Security severity: 7.8
Severity: error
Precision: medium
Tags:
   - security
   - external/cwe/cwe-117
Query suites:
   - java-security-extended.qls
   - java-security-and-quality.qls

单击以在 CodeQL 存储库中查看查询

如果将未经清理的用户输入写入日志条目,恶意用户可能能够伪造新的日志条目。

如果用户提供了一些输入,创建多个日志条目的外观,则可能会发生伪造。这可能包括未转义的新行字符或 HTML 或其他标记。

建议

在记录用户输入之前,应适当对其进行清理。

如果日志条目是纯文本,则应从用户输入中删除换行符,例如使用 String replace(char oldChar, char newChar) 或类似的方法。还应注意在日志条目中清楚地标记用户输入,并且恶意用户无法以其他方式造成混淆。

对于将在 HTML 中显示的日志条目,应在记录之前对用户输入进行 HTML 编码,以防止伪造和其他形式的 HTML 注入。

示例

在第一个示例中,由用户提供的用户名使用 logger.warn(来自 org.slf4j.Logger)记录。在第一种情况下(/bad 端点),用户名在未进行任何清理的情况下被记录。如果恶意用户提供 Guest'%0AUser:'Admin 作为用户名参数,则日志条目将被分成两行,其中第一行将是 User:'Guest',第二行将是 User:'Admin'

package com.example.restservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LogInjection {

    private final Logger log = LoggerFactory.getLogger(LogInjection.class);

    // /bad?username=Guest'%0AUser:'Admin
    @GetMapping("/bad")
    public String bad(@RequestParam(value = "username", defaultValue = "name") String username) {
        log.warn("User:'{}'", username);
        // The logging call above would result in multiple log entries as shown below:
        // User:'Guest'
        // User:'Admin'
        return username;
    }
}

在第二个示例(/good 端点)中,使用 matches() 来确保用户输入仅包含字母数字字符。如果恶意用户提供 `Guest’%0AUser:’Admin` 作为用户名参数,则根本不会记录日志条目,从而防止注入。

package com.example.restservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LogInjection {

    private final Logger log = LoggerFactory.getLogger(LogInjection.class);

    // /good?username=Guest'%0AUser:'Admin
    @GetMapping("/good")
    public String good(@RequestParam(value = "username", defaultValue = "name") String username) {
        // The regex check here, allows only alphanumeric characters to pass.
        // Hence, does not result in log injection
        if (username.matches("\\w*")) {
            log.warn("User:'{}'", username);

            return username;
        }
    }
}

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私