Projects
home:rottame:vhosts-ng:php74
php7
php7-CVE-2026-91766.patch
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File php7-CVE-2026-91766.patch of Package php7
From 5af9465ca890be679033d5fb0dc89b6ea071326b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <alex.daubois@gmail.com> Date: Mon, 14 Sep 2026 09:12:03 +0200 Subject: [PATCH] Fix GHSA-fpwc-w8rq-cr92: strip credentials from user headers on cross-origin redirects Authorization, Cookie and Proxy-Authorization set through the http context were forwarded verbatim when follow_location sent the request to another origin. They are now stripped from the user header bag whenever the redirect target differs in scheme, host or port, including when the header name is repeated or the bag uses malformed line endings. Co-authored-by: Jakub Zelenka <bukka@php.net> --- ext/standard/http_fopen_wrapper.c | 109 +++++++++--- ext/standard/tests/http/bug61548.phpt | 2 - .../tests/http/ghsa-fpwc-w8rq-cr92.phpt | 159 ++++++++++++++++++ 3 files changed, 248 insertions(+), 22 deletions(-) create mode 100644 ext/standard/tests/http/ghsa-fpwc-w8rq-cr92.phpt Index: php-7.4.33/ext/standard/http_fopen_wrapper.c =================================================================== --- php-7.4.33.orig/ext/standard/http_fopen_wrapper.c +++ php-7.4.33/ext/standard/http_fopen_wrapper.c @@ -83,6 +83,8 @@ #define HTTP_WRAPPER_HEADER_INIT 1 #define HTTP_WRAPPER_REDIRECTED 2 +#define HTTP_WRAPPER_STRIP_AUTH 8 + zend_string *zend_string_concat3( const char *str1, size_t str1_len, @@ -99,27 +101,68 @@ zend_string *zend_string_concat3( return res; } +static char *next_header_line(char *line) +{ + while (*line != '\0' && *line != '\r' && *line != '\n') { + line++; + } + if (*line == '\r') { + line++; + } + if (*line == '\n') { + line++; + } + + return line; +} +/* Removes every line whose header name matches, along with the folded + * continuation lines carrying the rest of its value. Neither a repeated header + * nor an occurrence of the name inside another header's value may leave the real + * header behind, as that would defeat HTTP_WRAPPER_STRIP_AUTH. */ static inline void strip_header(char *header_bag, char *lc_header_bag, const char *lc_header_name) { - char *lc_header_start = strstr(lc_header_bag, lc_header_name); - if (lc_header_start - && (lc_header_start == lc_header_bag || *(lc_header_start-1) == '\n') - ) { - char *header_start = header_bag + (lc_header_start - lc_header_bag); - char *lc_eol = strchr(lc_header_start, '\n'); + size_t name_len = strlen(lc_header_name); + char *lc_line = lc_header_bag; - if (lc_eol) { - char *eol = header_start + (lc_eol - lc_header_start); - size_t eollen = strlen(lc_eol); + while (*lc_line != '\0') { + if (strncmp(lc_line, lc_header_name, name_len) != 0) { + lc_line = next_header_line(lc_line); + continue; + } - memmove(lc_header_start, lc_eol+1, eollen); - memmove(header_start, eol+1, eollen); - } else { - *lc_header_start = '\0'; - *header_start = '\0'; + /* the whitespace RFC 7230 forbids before the colon is tolerated by some + * servers, so it must not hide the header from us either */ + const char *lc_colon = lc_line + name_len; + while (*lc_colon == ' ' || *lc_colon == '\t') { + lc_colon++; + } + + if (*lc_colon != ':') { + lc_line = next_header_line(lc_line); + continue; + } + + char *lc_next = next_header_line(lc_line); + while (*lc_next == ' ' || *lc_next == '\t') { + lc_next = next_header_line(lc_next); + } + + if (*lc_next == '\0') { + /* drop the preceding line break too, or the one appended after the bag + * would close the header block early */ + while (lc_line > lc_header_bag + && (*(lc_line - 1) == '\r' || *(lc_line - 1) == '\n')) { + --lc_line; + } } + + size_t tail_len = strlen(lc_next) + 1; + char *line = header_bag + (lc_line - lc_header_bag); + + memmove(line, header_bag + (lc_next - lc_header_bag), tail_len); + memmove(lc_line, lc_next, tail_len); } } @@ -688,8 +731,23 @@ finish: if (!header_init) { /* strip POST headers on redirect */ - strip_header(user_headers, t, "content-length:"); - strip_header(user_headers, t, "content-type:"); + strip_header(user_headers, t, "content-length"); + strip_header(user_headers, t, "content-type"); + } + + if (flags & HTTP_WRAPPER_STRIP_AUTH) { + strip_header(user_headers, t, "authorization"); + strip_header(user_headers, t, "cookie"); + if (!use_proxy) { + strip_header(user_headers, t, "proxy-authorization"); + } + } + + if (*user_headers == '\0') { + /* everything got stripped, keeping the empty bag would append a + * stray CRLF and end the header block early */ + efree(user_headers); + user_headers = NULL; } if (check_has_header(t, "user-agent:")) { @@ -1082,14 +1140,22 @@ finish: header_info.location = NULL; } - php_url_free(resource); - /* check for invalid redirection URLs */ - if ((resource = php_url_parse(new_path)) == NULL) { + php_url *new_resource = php_url_parse(new_path); + if (new_resource == NULL) { php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); efree(new_path); goto out; } + int default_port = use_ssl ? 443 : 80; + bool same_origin = zend_string_equals_ci(resource->scheme, new_resource->scheme) + && zend_string_equals_ci(resource->host, new_resource->host) + && (resource->port ? resource->port : default_port) + == (new_resource->port ? new_resource->port : default_port); + + php_url_free(resource); + resource = new_resource; + #define CHECK_FOR_CNTRL_CHARS(val) { \ if (val) { \ unsigned char *s, *e; \ @@ -1111,9 +1177,14 @@ finish: CHECK_FOR_CNTRL_CHARS(resource->pass); CHECK_FOR_CNTRL_CHARS(resource->path); } + + int new_flags = HTTP_WRAPPER_REDIRECTED | (flags & HTTP_WRAPPER_STRIP_AUTH); + if (!same_origin) { + new_flags |= HTTP_WRAPPER_STRIP_AUTH; + } stream = php_stream_url_wrap_http_ex( wrapper, new_path, mode, options, opened_path, context, - --redirect_max, HTTP_WRAPPER_REDIRECTED, response_header STREAMS_CC); + --redirect_max, new_flags, response_header STREAMS_CC); efree(new_path); } else { php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line);
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.