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), занимающуюся обработкой заголовков ответа. В ней, в частности есть вот такой вот блок:

01.if (r->headers_out.charset.len) {
02. 
03.    content_type_len = r->headers_out.content_type.len
04.                       + r->headers_out.charset.len
05.                       + ngx_strlen("; charset=") + 1;
06. 
07.    content_type = ngx_palloc(r->pool, content_type_len);
08. 
09.    if (content_type == NULL) {
10.        return NGX_ERROR;
11.    }
12. 
13.    ngx_snprintf(content_type, content_type_len,
14.                 "%V; charset=%V",
15.                 &r->headers_out.content_type,
16.                 &r->headers_out.charset);
17. 
18.    r->headers_out.content_type.data = content_type;
19.    r->headers_out.content_type.len = content_type_len;
20.}

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

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

1.u_char                      *content_type;
2.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 

Rich Text Editor, comment