Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:57672 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 36002 invoked from network); 3 Feb 2012 23:02:10 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 3 Feb 2012 23:02:10 -0000 Authentication-Results: pb1.pair.com smtp.mail=keisial@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=keisial@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.212.170 as permitted sender) X-PHP-List-Original-Sender: keisial@gmail.com X-Host-Fingerprint: 209.85.212.170 mail-wi0-f170.google.com Received: from [209.85.212.170] ([209.85.212.170:51266] helo=mail-wi0-f170.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id C6/04-08838-0776C2F4 for ; Fri, 03 Feb 2012 18:02:09 -0500 Received: by wibhm4 with SMTP id hm4so3729214wib.29 for ; Fri, 03 Feb 2012 15:02:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:cc:subject :references:in-reply-to:content-type; bh=S7l7kY/RbinPWFFLjPfGebONdEkfDlbVLPwQh6lHW+E=; b=TFbhnmCEHFnIR2ES6kOh22LI8IO/avXM8Q8CVU93bA9kp9KNlRzNx3H7MXb1yAyGX7 wGH3FXs7FUdlD1SrJ22/PNEa8/2/rRG3+hjz7iRaGjtAGoGqiDgsZM7HTTgNmLs3sKGj FpZkel2FEK3ym8jsBXoGJFF6sAlMShP9TQ8Ws= Received: by 10.180.96.8 with SMTP id do8mr107254wib.21.1328310126068; Fri, 03 Feb 2012 15:02:06 -0800 (PST) Received: from [192.168.1.26] (210.Red-83-52-184.dynamicIP.rima-tde.net. [83.52.184.210]) by mx.google.com with ESMTPS id q7sm9618004wix.5.2012.02.03.15.02.04 (version=SSLv3 cipher=OTHER); Fri, 03 Feb 2012 15:02:05 -0800 (PST) Message-ID: <4F2C6885.8080007@gmail.com> Date: Sat, 04 Feb 2012 00:06:45 +0100 User-Agent: Thunderbird MIME-Version: 1.0 To: Gustavo Lopes CC: internals@lists.php.net References: <4F2C4743.8070609@gmail.com> In-Reply-To: <4F2C4743.8070609@gmail.com> Content-Type: multipart/mixed; boundary="------------050304010104070504030503" Subject: Re: [PHP-DEV] The case of HTTP response splitting protection in PHP From: keisial@gmail.com (=?UTF-8?B?w4FuZ2VsIEdvbnrDoWxleg==?=) --------------050304010104070504030503 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On 03/02/12 21:44, Ángel González wrote: > On 03/02/12 15:01, Gustavo Lopes wrote: >> I've committed a different version that also forbids \0 (since, as >> Stefan says, a NUL byte can result in the truncation of the rest of >> the header) and that accepts a CRLF: >> >> http://svn.php.net/viewvc/php/php-src/trunk/main/SAPI.c?r1=323043&r2=323042&pathrev=323043 >> > Looks good. But given that the goal is to make this robust, I would go > further: > a) Replace any CRLF + [ \r] with SP > (rfc2616 allows us "A recipient MAY replace any linear white space > with a single SP before forwarding the message downstream.", and > this also protects UAs not following the spec) > > b) Bail out on any header_line[i] < ' ' (ie. fail on any special char) I've gone ahead and written code for that feature. Comments welcome. --------------050304010104070504030503 Content-Type: text/x-patch; name="HTTP_Response_splitting2.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="HTTP_Response_splitting2.diff" Index: main/SAPI.c =================================================================== --- main/SAPI.c (revision 323049) +++ main/SAPI.c (working copy) @@ -710,27 +710,30 @@ efree(header_line); return SUCCESS; } else { - /* new line/NUL character safety check */ - int i; - for (i = 0; i < header_line_len; i++) { - /* RFC 2616 allows new lines if followed by SP or HT */ - int illegal_break = - (header_line[i+1] != ' ' && header_line[i+1] != '\t') - && ( - header_line[i] == '\n' - || (header_line[i] == '\r' && header_line[i+1] != '\n')); - if (illegal_break) { - efree(header_line); - sapi_module.sapi_error(E_WARNING, "Header may not contain " - "more than a single header, new line detected"); - return FAILURE; + /* Forbid new lines or control characters in the headers */ + int i, j; + for (i = 0, j = 0; i < header_line_len; i++, j++) { + header[i] = header[j]; + if (i < header_line_len - 2 && + header_line[i] == '\r' && header_line[i+1] == '\n' && + (header_line[i+2] == ' ' || header_line[i+2] == '\t')) { + + /* This is a line continuation. Collapse this little-known + * feature into a single SP, as allowed by RFC 2616 + */ + header_line[i] = ' '; + j = i + 2; } - if (header_line[i] == '\0') { + + if (header_line[i] < 32) { efree(header_line); - sapi_module.sapi_error(E_WARNING, "Header may not contain NUL bytes"); + sapi_module.sapi_error(E_WARNING, "header() accepts only a single header, " + "new lines and control characters are forbidden"); return FAILURE; } } + header_line[j] = '\0'; + header_line_len = j; } sapi_header.header = header_line; --------------050304010104070504030503--