ModSecurity для Nginx: проблема с двойным Charset’ом
Date October 21st, 2013 Author Vitaly Agapov
Не так давно я разбирался с применением 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: ModSecurity, Nginx
Category:
Nginx, Security |
6 Comments »
18 November 2013 - 9:50
Господи, спасибо огромное, вы очень помогли, тот-же косяк был с ModSecurity (2.7.5) и Nginx (1.5.6) Этот грёбанный лишний байт дописывал рандомно любой символ в кодировку и получалось вроде UTF-8< после чего ajax подгружал нечитаемые иероглифы.
Возник ещё вопрос, а вы не сталкивались с 500-ой ошибкой при авторизации? На серваке крутятся несколько приложений, Prelude, Redmine, собственно сам магазин и везде при авторизации 500-я вылезает.
18 November 2013 - 9:57
Да что угодно это может быть, надо копать. Я столкнулся с другой новой проблемой – при включённом ModSecurity многострочные заголовки Set-Cookie укорачиваются до одной строки, что может приводить также к неожиданным проблемам.
В общем, ModSecurity для Nginx пока нужно использовать осторожно.
18 November 2013 - 10:08
https://github.com/SpiderLabs/ModSecurity/commit/1734221d9d3a78f9aafd68e35717da9ee1a4fe51 Оно?
18 November 2013 - 11:18
Да, похоже на то. Надо будет выделить времени и попробовать пересобрать добро.
18 November 2013 - 12:18
А про 500-ю я вот о чём говорил https://github.com/SpiderLabs/ModSecurity/issues/115
Та-же проблема, конфиг nginx-а верный, а оно не пашет.
24 January 2014 - 9:57
Фиксы по чарсету и многострочным заголовкам уже присутствуют в сборке 2.7.7, так что уже всё хорошо.