ModSecurity для Nginx: проблема с двойным Charset’ом

Date October 21st, 2013 Author Vitaly Agapov

Купленному покупатель платит по собственному усмотрению, а оказывающий услугу цену назначает сам.

Анджей Сапковский «Последнее желание»

nginx-modsec-charset

Не так давно я разбирался с применением ModSecurity для Nginx и даже набросал по этому поводу статью. Но в процессе эксплуатации этого решения я натолкнулся на странное поведение связки. Почему-то при работе Nginx в качестве прокси он в заголовок Content-Type ответа добавляет параметр charset независимо от того, присутствует он там уже или нет. Но беда не в этом, а в том, что к значению параметра кодировки он прибавляет случайным образом один байт, который на многих браузерах приводит к нечитаемости приходящего кириллического текста. Быстрое гугление показало, что проблему эту обнаружил не только я (раз, два), но решение для неё отсутствует в обозримых пространствах интернета.

Проверки были проведены и на самих свежих сборках ModSecurity (2.7.5) и Nginx (1.5.6). Везде проблема повторяется.

Тогда пришлось поискать причины странного поведения в коде самого ModSecurity, и они там нашлись. В файле modsecurity-apache_2.7.5/nginx/modsecurity/ngx_http_modsecurity.c я нашёл функцию ngx_http_modsecurity_load_headers_out(ngx_http_request_t *r), занимающуюся обработкой заголовков ответа. В ней, в частности есть вот такой вот блок:

    if (r->headers_out.charset.len) {

        content_type_len = r->headers_out.content_type.len
                           + r->headers_out.charset.len
                           + ngx_strlen("; charset=") + 1;

        content_type = ngx_palloc(r->pool, content_type_len);

        if (content_type == NULL) {
            return NGX_ERROR;
        }

        ngx_snprintf(content_type, content_type_len,
                     "%V; charset=%V",
                     &r->headers_out.content_type,
                     &r->headers_out.charset);

        r->headers_out.content_type.data = content_type;
        r->headers_out.content_type.len = content_type_len;
    }

Здесь в переменную content_type_len записывается вычисленная длина заголовка Content Type, которая получится после добавления к нему строки "; charset=" и самой кодировки. Но зачем-то к полученному значению добавляется лишняя единица. Впоследствии, когда для content_type выделяется память с лишним байтом, этот байт оказывается заполненным непредсказуемым значением.

Если и была у разработчиков какая-то причина сделать так, как они сделали, то я её не понял. А для решения своей проблемы можно либо убрать "+ 1" для верного подсчёта длины, либо вообще убрать весь блок целиком (вместе с определением переменных в начале функции):

u_char                      *content_type;
ngx_uint_t                   content_type_len;

 

После пересборки ModSecurity и Nginx проблема исчезает.

Tags: ,
Category: Nginx, Security | 6 Comments »

Comments

6 комментариев на “ModSecurity для Nginx: проблема с двойным Charset’ом”

  1. Роман

    Господи, спасибо огромное, вы очень помогли, тот-же косяк был с ModSecurity (2.7.5) и Nginx (1.5.6) Этот грёбанный лишний байт дописывал рандомно любой символ в кодировку и получалось вроде UTF-8< после чего ajax подгружал нечитаемые иероглифы.

    Возник ещё вопрос, а вы не сталкивались с 500-ой ошибкой при авторизации? На серваке крутятся несколько приложений, Prelude, Redmine, собственно сам магазин и везде при авторизации 500-я вылезает.

  2. Vitaly Agapov

    Да что угодно это может быть, надо копать. Я столкнулся с другой новой проблемой – при включённом ModSecurity многострочные заголовки Set-Cookie укорачиваются до одной строки, что может приводить также к неожиданным проблемам.
    В общем, ModSecurity для Nginx пока нужно использовать осторожно.

  3. Роман

    https://github.com/SpiderLabs/ModSecurity/commit/1734221d9d3a78f9aafd68e35717da9ee1a4fe51 Оно?

  4. Vitaly Agapov

    Да, похоже на то. Надо будет выделить времени и попробовать пересобрать добро.

  5. Роман

    А про 500-ю я вот о чём говорил https://github.com/SpiderLabs/ModSecurity/issues/115
    Та-же проблема, конфиг nginx-а верный, а оно не пашет.

  6. Vitaly Agapov

    Фиксы по чарсету и многострочным заголовкам уже присутствуют в сборке 2.7.7, так что уже всё хорошо.

Leave a comment

 Comment Form