HTTP header field name case-insensitivity is not being respected which leads to improper Session handling.

Preface

I did file this on the Laravel repo, laravel/framework#1639 and was directed to report it here.

Overview

Symfony is incorrectly handling HTTP request header field names that are lowercase, which causes the Session handler to improperly issue a new session cookie on every request.

I encountered this bug when working with a NodeJS app configured to proxy requests to Laravel, which sits on top of Symfony. The HTTP spec is pretty clear that header field names are case-insensitive:

4.2 Message Headers

... Each header field consists
of a name followed by a colon (":") and the field value. Field names
are case-insensitive.
...

Reproducing the Bug

I've recorded a video of this bug in action, consider it the tl;dr :)

Here's the breakdown of how I tested this and my configuration that lead me to believe the problem exists in the way Symfony parses request headers:

  1. I have laravel configured to run php artisan serve --host=127.0.0.1 --port=3000
  2. I have node running on port 8000, using the npm module http-proxy to forward requests to port 3000

To rule out node and http-proxy as a potential source of problems, I first used netcat to listen on port 3000 (nc -l 3000) and capture requests that http-proxy was sending, here's a dump of what http-proxy is forwarding to Laravel (note the lower case header field-names):

davidmosher@localhost:~/code/temp/laravel 
$ nc -l 3000
GET /auth/csrf_token HTTP/1.1
host: localhost:8000
connection: keep-alive
cache-control: no-cache
pragma: no-cache
accept: application/json, text/javascript
x-requested-with: XMLHttpRequest
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36
content-type: application/x-www-form-urlencoded
referer: http://localhost:8000/
accept-encoding: gzip,deflate,sdch
accept-language: en-US,en;q=0.8
cookie: laravel_session=doh0284ujk57ebnldlm2plp795
x-forwarded-for: 127.0.0.1
x-forwarded-port: 53245
x-forwarded-proto: http

When I use telnet 127.0.0.1 3000 in another terminal session I paste the above request that has lowercase header field-names in and receive the following response from Laravel:

HTTP/1.1 200 OK
Connection: close
X-Powered-By: PHP/5.4.14
Set-Cookie: laravel_session=kicg6iu0aobufkl036itar6km6; expires=Thu, 13-Jun-2013 18:48:12 GMT; path=/; HttpOnly
Set-Cookie: laravel_session=kicg6iu0aobufkl036itar6km6; expires=Thu, 13-Jun-2013 18:48:12 GMT; path=/; httponly
Cache-Control: no-cache
Date: Thu, 13 Jun 2013 16:48:12 GMT
Content-Type: application/json

Note the double Set-Cookie and the laravel_session has a different value than what was sent in the request. If I modify the header keys in my request to uppercase all the words like so:

GET /auth/csrf_token HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: application/json, text/javascript
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:8000/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: laravel_session=doh0284ujk57ebnldlm2plp795
X-Forwarded-For: 127.0.0.1
X-Forwarded-Port: 53245
X-Forwarded-Proto: http

Then I get a correct response from Laravel, as follows:

HTTP/1.1 200 OK
Host: localhost:8000
Connection: close
X-Powered-By: PHP/5.4.14
Set-Cookie: laravel_session=doh0284ujk57ebnldlm2plp795; expires=Thu, 13-Jun-2013 18:49:40 GMT; path=/; httponly
Cache-Control: no-cache
Date: Thu, 13 Jun 2013 16:49:40 GMT
Content-Type: application/json

Conclusion

Having ruled out my proxy completely, using netcat and telnet I can only conclude that Laravel or Symfony is treating http request header field names differently depending on the casing; this causes major problems for anyone setting up a proxy that may use lower case request header field names to forward requests to laravel and expects Session management to work properly.

Is this something that should be fixed by Laravel or at a lower level in Symfony?