6

I have the code which checks user cookies:

echo 'prev: ' . $prevCookie;
echo 'curr: ' . $currentCookie;
if ((string) $prevCookie == (string)$currentCookie) {
  echo 'same cookies';
} else {
  echo 'different cookies';
}

In approximatelly 0.1% of cases I'm getting following:

// prev: xxx
// curr: xxx
// different cookies

How that stuff can be happening?

PS. Strings are trimmed, no extra spaces.

UPDATE

I can provide you with real values which are stored in db, but they're the same: (direct), (organic), YandexCPC etc. It would be very easy if I could to reproduce it somehow.

  • 3
    Does `echo strlen($prevCookie);` and `echo strlen($currentCookie);` produce the same result? – MonkeyZeus May 19 '15 at 17:56
  • 5
    do `var_dump($prevCookie)` and `var_dump($currentCookie)` and post the results in your question – John Conde May 19 '15 at 17:58
  • 4
    0.1% of the time the strings are not the same. – AbraCadaver May 19 '15 at 17:58
  • Maybe this question can provide a clue: http://stackoverflow.com/questions/1562703/make-encoding-uniform-before-comparing-strings-in-php – MonkeyZeus May 19 '15 at 17:58
  • Good question. I don't know yet, strings are logged in json_encode and they are the same there. –  May 19 '15 at 17:58
  • What @MonkeyZeus says might be something to look at. But if you want a literal comparison of cookies I'd just hash them and compare the hashes. `md5($cookie1) === md5($cookie2)` This way you don't have to care about removing spaces or other erroneous data. If the cookies aren't absolutely the same, then they're different. – Squeegy May 19 '15 at 17:59
  • Related, [String comparison using == vs. strcmp](http://stackoverflow.com/q/3333353). – jww May 19 '15 at 18:00
  • What is your PHP version? Are you using UTF-8 **everywhere**? – MonkeyZeus May 19 '15 at 18:01
  • 3
    @jww With MD5 it is possible that another cookie value generates the same hash! – GuyT May 19 '15 at 18:01
  • 1
    @survex - please provide *real* values for `prev` and `curr`. Otherwise, based on what you posted (*"XXX"*), they are equal and this question should be closed as unable to duplicate. – jww May 19 '15 at 18:02
  • 1
    Still waiting on the data requested in the first two comments. Please add them or expect close votes since we do not have enough information to answer this question. – John Conde May 19 '15 at 18:03
  • 2
    @survex Looks like the two strings seemingly equal have something different that is not visible, it might be a leading or suffixed space, or a `\0` anywhere. There are symbols that might not print or not be noticed when printed that will cause two strings to be logically different but look the same. – Havenard May 19 '15 at 18:04
  • @Havenard in addition to your comment: or a zero width space. – GuyT May 19 '15 at 18:04
  • @GuyT - I think this question is about two strings that are supposed to be equal, and not two different strings and hashing collisions. But I would like to hear about how the collisions are being generated, even with MD5. – jww May 19 '15 at 18:05
  • @jww wait a minute, I will look for an example. This is a security risk(same as download checks with md5). See http://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value for more information. If you use MD5 for instance to auto login you can imagine the security flaw ;) – GuyT May 19 '15 at 18:06
  • @MonkeyZeus PHP5.4 What do you mean by saying everywhere? –  May 19 '15 at 18:13
  • @survex Everywhere = Web browser, PHP, Database. Can anyone else think of something I am missing? – MonkeyZeus May 19 '15 at 18:15
  • @survex Could you output every value of your database with `var_dump`(as John Conde already mentioned). And why are you comparing strings instead of comparing keys? – GuyT May 20 '15 at 06:28

3 Answers3

4

My guess is that your strings have some characters that aren't visible.

Consider the following:

$a = "abc";
$b = "abc\0";
echo (int)( $a == $b );

This will echo 0.

But, if you echo $a and $b, you will see "abc".


Another possibility might be that you are reading/writting a file using fopen() without the b option.

Windows has a feature that converts \r and \n into \r\n.

When you read back, it comes with Windows' line endings.

When you compare, the values are different, since \r (in memory/cookie) isn't the same as \r\n (read from the file).

When you output it to a browser, it is shown as abc. Browsers ignore whitespaces in the output (changeable with CSS).


Another thing might be implicit numeric convertion.

Consider the following code:

echo '1000000000000000000000000000000000000000000000000' == '1.0E+48';

This will echo 1. Check it here: http://sandbox.onlinephpfunctions.com/code/1debf1a505793e3d0fde3b174c26e1f1454ea1e2

Using === solves this.

Something similar might be happening. But this shows the oposite (2 different strings that aren't equal being compared that returns true).


The source and the environment itself are unknown.

How the values are obtained is unknown as well.

There are too many unknowns...

Ismael Miguel
  • 4,185
  • 1
  • 31
  • 42
  • I think this is pretty much it! Now I have reproduced the bug and tomorrow I'll deploy changes. What you think is the best way to escape such problem? –  May 19 '15 at 18:17
  • @survex Filter the cookies with `trim()` before comparing them. This function removes the most common invisible symbols from the beginning and the end of a string. – Havenard May 19 '15 at 18:19
  • @survex There are many unknowns. I don't know where was the `$prevCookie` stored before. I don't know what comes in `$currentCookie`. But the best would be to `trim()` and to use `str_replace("\0",'',$cookie)`. Also, use `===`instead. – Ismael Miguel May 19 '15 at 18:23
  • @IsmaelMiguel No need to enforce type comparison when he is explicitly casting both variables to `(string)`. – Havenard May 19 '15 at 18:24
  • @Havenard http://sandbox.onlinephpfunctions.com/code/1debf1a505793e3d0fde3b174c26e1f1454ea1e2 --> this proves you wrong. Try running this: `echo '1000000000000000000000000000000000000000000000000' == '1.0E+48';` (should output `1`). – Ismael Miguel May 19 '15 at 18:26
  • @Havenard I added it to the answer as a possibility. You need to be really careful with string comparisson. – Ismael Miguel May 19 '15 at 18:31
0

Use strcmp (case sensitive) or strcasecmp (case insensitive).

For string compare, in most of languages, is not a good solution use direct compare to strings.

echo 'prev: ' . $prevCookie;
echo 'curr: ' . $currentCookie;
if (strcmp($prevCookie,$currentCookie) === 0) {
  echo 'same cookies';
} else {
  echo 'different cookies';
}
Victor
  • 8,309
  • 14
  • 80
  • 129
  • I don't see how using `strcmp()` it will produce any different behaviour compared to using `==`. – Havenard May 19 '15 at 18:09
  • If the function exists means that the equals `==` will check if the variable is the same, not the same value. – Victor May 19 '15 at 18:12
  • Nothing wrong about comparing them as far as I know. Whats to be avoided is to check if a string is empty with `if ($string)`, because the interpreter will understand this as `if ((bool)$string)` and if the string is not empty, but is equal to `"0"` or `"0000"` it will evaluate to `false`. – Havenard May 19 '15 at 18:13
  • @Havenard There are other dangers. Try running this: `echo '1000000000000000000000000000000000000000000000000' == '1.0E+48';` (it will echo `1`, while it should echo nothing at all). The string `'1000000000000000000000000000000000000000000000000'` will be converted to the integer `1.0E+48`, while the string `'1.0E+48'` will be converted to the same exact value. Using `===` or `strcmp()` will solve this. – Ismael Miguel May 21 '15 at 17:09
-3

Sometimes when debuggin the ide shows the same value but for example if you are comparing a global value inside function and you do not have declare the result will not be the same example

Not work

function compare($p){
  return $p == $z;
}

Will work

function compare($p){
global $z;
return $p == $z;
}