PHP seems to fail to parse into $_COOKIE correctly - php

I have an incoming HTTP request that looks like this (standard stuff with a couple of cookies being returned from the client):
GET /loggedin.php HTTP/1.1
Cookie: name=Server+Side+Name; path=/
Cookie: role=Role+From+DB; path=/
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.1; Build/JRO03C)
Host: www.example.com
Connection: Keep-Alive
Accept-Encoding: gzip
Some PHP which reads:
<?php
$body = "\nThe request's _SERVER['HTTP_COOKIE'] is: " . print_r($_SERVER['HTTP_COOKIE'], true);
$body .= "\nThe request's _COOKIE is: " . print_r($_COOKIE, true);
echo $body;
?>
Which results in the following output:
The request's _SERVER['HTTP_COOKIE'] is: name=Server+Side+Name; path=/, role=Role+From+DB; path=/
The request's _COOKIE is: Array
(
[name] => Server Side Name
[path] => /, role=Role From DB
)
Note the second entry in $_COOKIE is "path" not "role" and its value is incorrect.
It certainly looks like the cookies which appear correctly (albeit concatenated) in $_SERVER['HTTP_COOKIE'] are being parsed incorrectly to populate $_COOKIE but I can't believe that's the case. I control all of the elements so what should I be doing differently to get the correct values into $_COOKIE?
If it matters this is on PHP 5.3.27 on an EC2 Amazon Linux instance.

The answer is: it's easy to confuse the Set-Cookie and Cookie headers.
Only the Set-Cookie header has options like path and expires. The Cookie header does not because those options are consumed by the client when deciding whether to send the cookie or not.
The reason my request contains these options on the Cookie header is that I'm trying to work around an issue with certain Android HTTP implementations and got the headers confused in my code logic.

Related

How to set cookie in Laravel Blade File?

I want to set cookies in laravel blade.php file, not in the controller. How can I set it?
Disclaimer: I will focus my answer on PHP and laravel.
Why not set in controller?
It would really help to know why you cannot / or do not want to set cookies using laravel's cookie Facade in the controller - eg. Cookie::queue, as it's very easy to do!
Here are two ways, from this source.
Via response:
return response(view('welcome'))->cookie('name','value',$min);
Via Queue: Cookie::queue(Cookie::make('name','value',$min)); return view('welcome');
Set-Cookie is a response header, not the body!
Assuming you would set these cookies in PHP , they need to come as part of a response header, and not part of the body (view). This is why you would need to set these in the controller, where you are sending a response!
If you try to use PHP functions to set cookies, you will be met with errors "headers have already been sent"
Per the docs: https://www.php.net/setcookie
setcookie() defines a cookie to be sent along with the rest of the HTTP headers. Like other headers, cookies must be sent before any output from your script (this is a protocol restriction). This requires that you place calls to this function prior to any output, including and tags as well as any whitespace.
To understand what this means, it's helpful to understand the structure of requests and responses:
Requests and Responses are made up of headers and possibly a body.
Note: You can see these in the network tab of your browser's dev tools.
The request headers are like meta data about the request that can tell the server what kind of content is being requested, and who is requesting.
The response headers are like meta data about the response returned that can tell the server what kind of content is being delivered, how long to cache it for, associated cookies that got set.
Example Request Headers:
Content-Type: 'application/json'
Content-Type: 'application/pdf'
Content-Type: 'text/html'
Content-Type: 'text/css'
User-Agent: 'Mozilla/5.0 (<system-information>) <platform> (<platform-details>) <extensions>'
Authorization: 'Bearer <token>'
Example Response Headers:
Content-Type as it may differ from what was requested
Expires: 'Wed, 07 Sep 2022 19:26:49 GMT'
Cross-Origin-Resource-Policy: 'cross-origin'
Date: 'Wed, 07 Sep 2022 19:26:49 GMT'
Content-Length: 0,
Set-Cookie: test_cookie=CheckForPermission; expires=Wed, 07-Sep-2022 19:41:49 GMT; path=/; domain=.doubleclick.net; Secure; HttpOnly; SameSite=none
Notably: - Set-Cookie - tells the browser to add these cookies to application storage (you can view these in application / storage tabs in dev tools)
The response header can have Set-Cookie, not the request header. This makes sense, as usually the cookie information is going to come from the "answer" (response) to the "question" (request) by way of performing some logic, eg - this user is authenticated, here's a cookie to keep their session in place.
Also: Secure & HTTP only Cookies
Cookies can get set with a few options - secure only, and http only. These mean that the cookie must be Set on secure connections (https) and the http only can come from a response and cannot be overridden by JavaScript adjusting (client side)
Example of options for Laravel's Cookie::queue facade:
// $name, $value, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true
Cookie::queue($name, $value, $ttl, $path, $domain, $secure, $httpOnly);
ttl = "time to live" or how long until it expires eg. 2 minutes

php://input contains data for a GET request

I am running Apache2 and PHP 5 on Linux, and I'm getting some strange behavior with the php://input stream.
For some GET requests the stream is not empty like it should be. Instead, the php://input stream contains the entire GET request. I have worked around the issue but I would like to know if I should file a bug about this, or if it is "desired but undocumented" behavior.
Details
Early in the request processing, I call:
$in = file_get_contents('php://input');
if ( !empty($in) )
$post_data = json_decode($in);
if ( !empty($in) && is_null($post_data) ) {
// output some error info and exit
}
Usually when a request does not have a body then $in is empty and all is right with the world. But sometimes a GET request will have a body, and that body will be the entire request. Of course you can't json-decode that data, and the error condition gets hit.
This only happens with some requests. For example, this request does not exhibit the error:
GET /os/invitations/kkkkkk HTTP/1.1
Host: our.machine.com
Content-Type: application/json
Authorization: Basic aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa==
But this request, which is routed through some proxies and VPNs, does trigger the error.
GET http://some.proxy.at.some.big.company.com:7080/cvp-out/cmmproxy/os/invitations/d66065566dba541c8ba6a70329684645 HTTP/1.1
Content-Type: application/json
Authorization: Basic aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa==
Clientid: abc
User-Agent: Java/1.6.0
Host: some.proxy.at.some.big.company.com:7080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
X-Remote-Addr: 53.231.244.171
X-Remote-Host: 53.231.244.171
X-Server-Name: some.proxy.at.some.big.company.com
X-Server-Port: 7080
X-Scheme: http
I spent hours treating this like a routing/dispatch problem, but it turned out to be our code. The fix was, of course, to only read from the input stream when you are expecting data:
if ( in_array( $_SERVER['REQUEST_METHOD'], array('PUT', 'POST') )) {
$in = file_get_contents('php://input');
if ( !empty($in) )
$post_data = json_decode($in);
}
Is this a known issue? Does it happen unpredictably? Should I file a bug?
As far as i know, that's not an error. We understand that a GET request shouldnt have a body, but in the docs of php:// they say nothing about wich types of requests will generate an input, so it could be any method. And for sure it is not limited to POST, since the mention at least PUT and PROPFIND.
So at any rate, your solution is a must.

PHP cookie handling

A centain web client that I need to support, is sending back the Cookies header to my application twice in the HTTP headers, this in turn is making PHP unable to read the correct value for the cookie thus ignoring the session.
Here is the relevant part of the request I am seeing:
GET / HTTP/1.1
Cache-Control: max-age=0
Accept-Language: en-US
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a
Connection: Keep-Alive
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a
[...] Other irrelevant headers
I have two questions:
Is that a PHP bug? or is the behavior undefined when the client sends that same header twice?
Is there a quick workaround to make things work without having to manually parse the HTTP headers so I can read the right value of the cookie (and session) in my application? Or should I manually parse the HTTP header to set the session to its correct value?
According to the HTTP spec, a double header simply concatenates the values together with a comma, making it:
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a, PHPSESSID=49af82ddf12740e6a35b15985e93d91a
PHP should be able to parse the cookies, but the behavior of sessions is undefined when there are two session IDs.
I strongly recommend fixing the client. If that's not an option, you'll have to parse the headers manually.

sessions not working

So I had developed a basic site, using $_SESSION superglobal variable for the logging in.
so the code basically after checking the login details are valid i store the users details into the session like so:
note I am starting the session before storing these values.
$_SESSION['myusername'] = $myusername;
$_SESSION['myuserid'] = $userid;
$_SESSION['logged_in'] = true;
$_SESSION['mystatus'] = $res['user_status'];
it all worked fine, throughout the time i made the site and tested etc.
now all of a sudden, the sessions are not working, so obviously the users cannot get access after logging in because the site is checking data which isnt in the session.
on the page I store the data like above, straight after i can use this:
echo "username".$_SESSION['myusername'];
echo "status".$_SESSION['mystatus'];
and its there. But when the user is directed to another page and i try:
<?php
session_start();
include ('functions.php');
echo "username".$_SESSION['myusername'];
echo "status".$_SESSION['mystatus']; ....
the values aren't in the session. I have checked that the session id is the same, which it is.
This has always worked, so I am really puzzled.
somebody please help.
EDIT
request header & response header from firebug (page where session appears to be empty)
Response Headersview source
Date Sat, 11 Jun 2011 15:18:48 GMT
Server Apache/2.2.3 (Red Hat)
X-Powered-By PHP/5.1.6
Expires Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma no-cache
Content-Length 3772
Connection close
Content-Type text/html; charset=UTF-8
Request Headersview source
Host students.ee.port.ac.uk
User-Agent Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-gb,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Cookie PHPSESSID=1jqqa2oeivq76h2vhtk4uflkv1
Authorization Basic ZWNlNzAxNDE6cGllczRtZTIy
So it seems you have a problem with keeping your session on a second request.
Session tracking is done via cookies, you should check (with Live HTTP Headers or firebug) the real cookie content sent by the server. In this cookie check the path setting and the server name given, check as well time validity settings, if something is wrong there the browser won't send back the cookie and you'll get a new session on each request.
The web developper Toolbar contains some nice cookie tools as well, where you can display a page containing all cookies details for a given page. If the cookie receveid is not there then the browser assume this cookie is not related to this page. Most of the time a php setting is enforcing the cookie.domain setting to something other than the used DNS.
Given the fact that you haven't changed a thing in the last few weeks and that it used to work, you should check that your server didn't run out of disk space. If it did, it may create a reference to a session but might not be able to serialize the data to disk once the page has been rendered.
This could explain why outputting the $_SESSION[...] works on the same page and why the cookie is set in the response.
Check whether the session id on the second request is the same as the one on the first request.

CURL response different than response to request sent from browser

Attempting to submit a form with CURL, both via PHP and the command line. The response from the server consists of null content (the headers posted below).
When the same URL is submitted via a browser, the response consists of a proper webapge.
Have tried submitting the CURL request parameters via POST and GET via each of the following command line curl flags "-d" "-F" and "-G".
If the query string parameters are posted with "-d" flag, resulting header is:
HTTP/1.1 302 Moved Temporarily
Date: Thu, 02 Jun 2011 21:41:54 GMT
Server: Apache
Set-Cookie: JSESSIONID=DC5F435A96A353289F58593D54B89570; Path=/XXXXXXX
P3P: CP="CAO PSA OUR"
Location: http://www.XXXXXXXX.com/
Content-Length: 0
Connection: close
Content-Type: text/html;charset=UTF-8
Set-Cookie: XXXXXXXXXXXXXXXX=1318103232.20480.0000; path=/
If the query string parameters are posted with "-F" flag, the resulting header is:
HTTP/1.1 100 Continue
HTTP/1.1 500 Internal Server Error
Date: Thu, 02 Jun 2011 21:52:54 GMT
Server: Apache
Content-Length: 1677
Connection: close
Content-Type: text/html;charset=utf-8
Set-Cookie: XXXXXXXXXXXXXX=1318103232.20480.0000; path=/
Vary: Accept-Encoding
<html><head><title>Apache Tomcat/5.5.26 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 500 - </h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>message</b> <u></u></p><p><b>description</b> <u>The server encountered an internal error () that prevented it from fulfilling this request.</u></p><p><b>exception</b> <pre>javax.servlet.ServletException: Servlet execution threw an exception<br>
</pre></p><p><b>root cause</b> <pre>java.lang.NoClassDefFoundError: com/oreilly/servlet/multipart/MultipartParser<br>
com.corsis.tuesday.servlet.mp.MPRequest.<init>(MPRequest.java:27)<br>
com.corsis.tuesday.servlet.mp.MPRequest.<init>(MPRequest.java:21)<br>
com.corsis.tuesday.servlet.TuesdayServlet.doPost(TuesdayServlet.java:494)<br>
javax.servlet.http.HttpServlet.service(HttpServlet.java:710)<br>
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)<br>
</pre></p><p><b>note</b> <u>The full stack trace of the root cause is available in the Apache Tomcat/5.5.26 logs.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/5.5.26</h3></body></html>
Questions:
What might cause a server to respond different depending on the nature of the CURL request.
How to successfully submit request via CURL?
HTTP/1.1 100 Continue
I had problems associated with this header before. Some servers simply do not understand it. Try this option to override Expect header.
curl_setopt( $curl_handle, CURLOPT_HTTPHEADER, array( 'Expect:' ) );
To add to what Richard said, I have seen cases where servers check the User-Agent string and behave differently based on its value.
I have just had an experience with this and what fixed it was surprising. In my situation I was logging into a server so I could upload a file, have the server do work on it, and then download the new file. I did this in Chrome first and used the dev tools to capture over 100 HTTP requests in this simple transaction. Most are simply grabbing resources I don't need if I am trying to do all of this from the command line, so I filtered out only the ones I knew at a minimum I should need.
Initially this boiled down to a GET to set the cookie and log in with a username and password, a POST to upload the file, a POST to execute the work on the file, and a GET to retrieve the new file. I could not get the first POST to actually work though. The response from that POST is supposed to be information containing the upload ID, time uploaded, etc, but instead I was getting empty JSON lists even though the status was 200 OK.
I used CURL to spoof the requests from the browser exactly (copying the User-Agent, overriding Expect, etc) and was still getting nothing. Then I started arbitrarily adding in some of the requests that I captured from Chrome between the first GET and POST, and low and behold after adding in a GET request for the JSON history before the POST the POST actually returned what it was supposed to.
TL;DR Some websites require more requests after the initial log in before you can POST. I would try to capture a successful exchange between the server and browser and look at all of the requests. Some requests might not be as superfluous as the seem.

Categories