# HG changeset patch # User Igor Sysoev # Date 1184319421 0 # Node ID 458e041fc90285a58dc17f7d871988e2eeba8d75 # Parent 4ec0bc95172b59f433ec1505153e95098fce5ab3 msie_refresh should escape at least '"' to prevent XSS diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -1039,18 +1039,30 @@ ngx_escape_uri(u_char *dst, u_char *src, 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; + /* " ", """, "'", %00-%1F, %7F-%FF */ - switch (type) { - case NGX_ESCAPE_HTML: - escape = html; - break; - case NGX_ESCAPE_ARGS: - escape = args; - break; - default: - escape = uri; - break; - } + static uint32_t refresh[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + static uint32_t *map[] = { uri, args, html, refresh }; + + + escape = map[type]; if (dst == NULL) { diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -142,6 +142,7 @@ u_char *ngx_utf_cpystrn(u_char *dst, u_c #define NGX_ESCAPE_URI 0 #define NGX_ESCAPE_ARGS 1 #define NGX_ESCAPE_HTML 2 +#define NGX_ESCAPE_REFRESH 3 #define NGX_UNESCAPE_URI 1 diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -315,6 +315,7 @@ ngx_http_special_response_handler(ngx_ht { u_char *p; size_t msie_refresh; + uintptr_t escape; ngx_int_t rc; ngx_buf_t *b; ngx_str_t *uri, *location; @@ -496,17 +497,19 @@ ngx_http_special_response_handler(ngx_ht r->headers_out.content_length = NULL; } - msie_refresh = 0; - location = NULL; - if (clcf->msie_refresh && r->headers_in.msie && (error == NGX_HTTP_MOVED_PERMANENTLY || error == NGX_HTTP_MOVED_TEMPORARILY)) { + location = &r->headers_out.location->value; + + escape = 2 * ngx_escape_uri(NULL, location->data, location->len, + NGX_ESCAPE_REFRESH); + msie_refresh = sizeof(ngx_http_msie_refresh_head) - 1 - + location->len + + escape + location->len + sizeof(ngx_http_msie_refresh_tail) - 1; r->err_status = NGX_HTTP_OK; @@ -514,6 +517,11 @@ ngx_http_special_response_handler(ngx_ht r->headers_out.content_length_n = msie_refresh; r->headers_out.location->hash = 0; r->headers_out.location = NULL; + + } else { + location = NULL; + escape = 0; + msie_refresh = 0; } ngx_http_clear_accept_ranges(r); @@ -595,7 +603,13 @@ ngx_http_special_response_handler(ngx_ht p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head, sizeof(ngx_http_msie_refresh_head) - 1); - p = ngx_cpymem(p, location->data, location->len); + if (escape == 0) { + p = ngx_cpymem(p, location->data, location->len); + + } else { + p = (u_char *) ngx_escape_uri(p, location->data, location->len, + NGX_ESCAPE_REFRESH); + } b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail, sizeof(ngx_http_msie_refresh_tail) - 1);