19

So from my understanding of PHP and cookies, if I use the setcookie() function, then I get a cookie that is automatically url encoded. And when I go to the $_COOKIE array, I should get the cookie back, automatically url decoded. Problem is, it seems to be decoding the cookie twice when I look in $_COOKIE.

Say I have a cookie whose value is "Name|ID|Email", for example:

Joe|123|my+email@somewhere.com

This would be encoded as:

Joe%7C123%7Cmy%2Bemail%40somewhere.com

Notice the plus sign is encoded, so theoretically I ought to get it back if I decode it. Since this is automatically done in $_COOKIE, I ought to get back what I started with. But instead, I'm getting back:

Joe|123|my email@somewhere.com

Notice the space where the plus used to be. This is what I would expect if I ran an additional urldecode() on the cookie. But I'm not, so I have no idea why I would be getting a space instead of a plus.

Another interesting twist. A refresh on the page seems to produce the correct output. Any ideas why it's behaving like this?

FYI, to set the initial cookie, I use javascript and escape() the script to produce the encoded string. Might this be an hand off issue between javascript and PHP?

Thoughts would be appreciated.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
user1630830
  • 307
  • 2
  • 10
  • 1
    Have you tried using JavaScript's `encodeURI()` function instead of escape? – CD001 Jan 17 '13 at 16:45
  • 1
    If You use `urlencode('+')` it will return `%2B` which after `urldecode('%2B')` should return `+` - see [this](http://codepad.org/ZrCamxBm). If You still have some problems You could workaround this by replacing the `+` by some *email-address-forbidden-character* like `>` and after recovering data back from COOKIE replace the `>` back to `+`... – shadyyx Jan 17 '13 at 16:47
  • You might be setting the cookie slightly incorrect with your JavaScript, causing PHP to "repair" it the first time it encounters it. Looks like you got some details wrong, so I assume we won't get further without seeing some of your code. – domsom Jan 17 '13 at 16:53
  • 1
    ignore my previous comment `encodeURI()` won't encode the '+' symbol, you need `encodeURIComponent()` - sorry :\ – CD001 Jan 17 '13 at 17:16
  • Without actual code, it's hard to tell what's happening. But there's little doubt that you're decoding twice, which you need to track down and remove. This can cause other problems besides this one. For example %2540 should not be decoded to %40 not @. Incidentally, I sure hope you're not using that email address to identify the user without encrypting it and using an HMAC. Otherwise, you've got worse problems. See the section on hacking cookies at http://j.mp/learn-state-manipulation – Vroo Sep 27 '13 at 18:16

3 Answers3

6

It's worth noting that both "%20" and "+" are valid encodings of a space character. Per the Wikipedia article on URL encoding (emphasis added):

When data that has been entered into HTML forms is submitted, the form field names and values are encoded and sent to the server in an HTTP request message using method GET or POST, or, historically, via email. The encoding used by default is based on a very early version of the general URI percent-encoding rules, with a number of modifications such as newline normalization and replacing spaces with "+" instead of "%20". The MIME type of data encoded this way is application/x-www-form-urlencoded, and it is currently defined (still in a very outdated manner) in the HTML and XForms specifications.

More specifically related to PHP and JavaScript, see the top answer on this question:

When to encode space to plus (+) or %20?

Community
  • 1
  • 1
DreadPirateShawn
  • 8,164
  • 4
  • 49
  • 71
2

If you don't want to automatically encode the cookie, you can use setrawcookie function.
The exception with this function is, you can not use these characters: (,; \t\r\n\013\014) :

setrawcookie("NAME","Joe|123|my+email@somewhere.com");  

# Output in browser:   
Joe|123|my+email@somewhere.com 

# Output in PHP `echo $_COOKIE['NAME']`:  
Joe|123|my email@somewhere.com

Tested with PHP 5.3

setcookie("NAME","Joe|123|my+email@somewhere.com");

# Output in browser:  
Joe%7C123%7Cmy%2Bemail%40somewhere.com  

# Output in PHP echo $_COOKIE['NAME']`:  
Joe|123|my+email@somewhere.com  

now : As an alternative way, you can use setcookie(), and rawurldecode() to decode it:

 echo rawurldecode($_COOKIE['NAME'])
M Rostami
  • 4,035
  • 1
  • 35
  • 39
2

Firstly, PHP will always run before JavaScript - it's server side rather than client side so the cookie you set with JavaScript won't actually be available to PHP until you refresh the page (hence that issue).

Next JavaScript has different ways to encode the strings; only one will work with PHP automatically.

So:

document.cookie = "testuser=" + "Joe|123|my+email@somewhere.com";
// Joe|123|my email@somewhere.com (when decoded by PHP)

document.cookie = "testuser=" + escape("Joe|123|my+email@somewhere.com");
// Joe|123|my email@somewhere.com (when decoded by PHP)

document.cookie = "testuser=" + encodeURI("Joe|123|my+email@somewhere.com");
// Joe|123|my email@somewhere.com (when decoded by PHP)

document.cookie = "testuser=" + encodeURIComponent("Joe|123|my+email@somewhere.com");
// Joe|123|my+email@somewhere.com 

So, try this for the sake of a test (remember you'll need to refresh the page to see the cookie value):

<html>
<head>
    <title>Cookie Juggling</title>
    <script type="text/javascript">
        document.cookie = "testuser=" + encodeURIComponent("Joe|123|my+email@somewhere.com");
    </script>
</head>

<body>
    <div><?php echo !empty($_COOKIE['testuser']) ? $_COOKIE['testuser'] : "Cookie not set yet"; ?></div>
</body>
</html>
CD001
  • 8,332
  • 3
  • 24
  • 28