News for hackers. Stuff that matters.
Re:PHP: md5('240610708') == md5('QNKCDZO') by dietrichepp on Monday May 04, 2015 @03:47AM (#9484856)
I'm not exactly clear on how PHP == works, but you can see the MD5 for yourself:

    $ echo -n 240610708 | md5sum
    0e462097431906509019562988736854  -
    $ echo -n QNKCDZO | md5sum
    0e830400451993494058024219903391  -
    $ echo -n aabg7XSs | md5sum
    0e087386482136013740957780965295  -
All of them start with 0e, which makes me think that they're being parsed as floats and getting converted to 0.0. This is why "magic" operators like == in PHP and JavaScript never should have existed in the first place. Operators like == should be, by default, extremely boring. PHP's just happens to be a bit more magical even than JavaScript's.
Re:PHP: md5('240610708') == md5('QNKCDZO') by DoubleMalt on Monday May 04, 2015 @06:29AM (#9485296)
Once I wrote a little PHP application to manage a clan in a browser game. I used an MD5 hash as session id that I checked with if(session_id)

When users started reporting that their logins would sometimes not work at the first time, I found out that strings that start with zero are coerced to 0 and then interpreted as false.

Never used PHP for anything important since.

Re:PHP: md5('240610708') == md5('QNKCDZO') by ams6110 on Monday May 04, 2015 @09:07AM (#9485868)
To be fair, this kind of thing (maybe not exactly this, but type-coercion bugs) can happen in JavaScript, which is all the rage now for "important" stuff.
Re:PHP: md5('240610708') == md5('QNKCDZO') by shuzchen on Monday May 04, 2015 @04:10PM (#9488798)
This is levels worse than what Javascript does though. Most high-level languages have some sort of implicit coercion (even python lets you do truth tests on non-boolean values). The problem here is the programmer isn't confused about types at all. They're comparing two things of the same type: two strings! Nevertheless, given two strings PHP tries to coerce them into ints before carrying out the equality test. Yes, you will have coercion bugs in other languages if you're testing things of different types, but I don't know any other language where a equality test between two things of the same type are automatically coerced into another.
Re:PHP: md5('240610708') == md5('QNKCDZO') by wmil on Monday May 04, 2015 @09:24AM (#9485945)
It can happen in a few languages, but PHP is notably more aggressive in trying to convert to int.

Actually a common way to grief new websites is to try to register '0' as a username. `if (string)` is a common way to check for null, and '0' will often fail.

Re:PHP: md5('240610708') == md5('QNKCDZO') by bcruddy on Monday May 04, 2015 @10:50AM (#9486461)
Yeah but javascript has 'use strict' whereas PHP decided that the easter egg "looks like you're using the wrong language!" was more important than actually allowing a 'use strict' to force === instead of ==.
Re:PHP: md5('240610708') == md5('QNKCDZO') by Silhouette on Monday May 04, 2015 @01:35PM (#9487645)
While that's true, JavaScript is still horribly error-prone because of this. The suggestion that JS would be a much better language if the == operator worked more like === in the first place is very reasonable.
Re:PHP: md5('240610708') == md5('QNKCDZO') by samspot on Wednesday May 06, 2015 @10:34AM (#9498891)
I don't think strict mode affects == vs. === Best bet is to use a linter to catch that.
Re:PHP: md5('240610708') == md5('QNKCDZO') by return0 on Monday May 04, 2015 @08:41AM (#9485737)
What did you use for the important stuff that was 100% predictable?
Re:PHP: md5('240610708') == md5('QNKCDZO') by merb on Monday May 04, 2015 @02:45PM (#9488180)
zeroes and ones.
Re:PHP: md5('240610708') == md5('QNKCDZO') by tveita on Monday May 04, 2015 @11:56AM (#9486961)
This does not appear to the case in PHP 5.6, even for most strings with '==' gotchas:

  <?php
  if ('0e24') echo 'true'; else echo 'false';

  outputs:
  true
As far as I know the only strings that fail an if check are "" and "0". (Which is still a pitfall, but not one you'd hit with an MD5 hash)
Re:PHP: md5('240610708') == md5('QNKCDZO') by rimantas on Monday May 04, 2015 @07:26AM (#9485475)
Yeah, documentation is for pussies.
Re:PHP: md5('240610708') == md5('QNKCDZO') by imakesnowflakes on Monday May 04, 2015 @08:17AM (#9485661)
Here is one PHP core developer claiming that PHP documentation is wrong, even on fundamental things...

http://www.reddit.com/r/lolphp/comments/2md8c0/new_safe_cast...

Just saying....

Re:PHP: md5('240610708') == md5('QNKCDZO') by rimantas on Monday May 04, 2015 @09:47AM (#9486075)
But it is not wrong in this case.
Re:PHP: md5('240610708') == md5('QNKCDZO') by lectrick on Wednesday May 06, 2015 @03:22PM (#9501169)
PHP doesn't even pass its own test suite.

That's right... they consider certain tests failing OK, a certain number of failures OK, tests failing nondeterministically OK.

Just saying... Who gives a shit about programmer headaches? ;)

Re:PHP: md5('240610708') == md5('QNKCDZO') by sarciszewski on Monday May 04, 2015 @07:27AM (#9485479)
> I used an MD5 hash as session id

> Never used PHP for anything important since.

The problem here isn't PHP, the problem here is you.

Re:PHP: md5('240610708') == md5('QNKCDZO') by FeepingCreature on Monday May 04, 2015 @07:33AM (#9485515)
Nah, the problem is PHP.

See: http://blog.codinghorror.com/falling-into-the-pit-of-success...

> When you write code in [PHP], you're always circling the pit of despair, just one misstep away from plunging to your doom.

Re:PHP: md5('240610708') == md5('QNKCDZO') by Navarr on Monday May 04, 2015 @10:11AM (#9486206)
I'd be willing to say this is true for any language in varying ways.
Re:PHP: md5('240610708') == md5('QNKCDZO') by Silhouette on Monday May 04, 2015 @01:44PM (#9487708)
But to widely varying degrees. This kind of problem is a direct consequence of having a relatively weak and dynamic type system (or other semantics that mean you might as well have).

Plenty of people have warned about this kind of danger for a very long time. However, there seems to be a significant subset of the web development community that only has experience with languages like JS and PHP and to a lesser extent other dynamic languages like Ruby and Python, who simply fail to realise how many of these bugs should have been entirely prevented by using better tools by now. The usual counter seems to be something about unit tests, at which point anyone following the discussion who actually knows anything about type systems and the wider world of programming languages dies a little inside.

It is entirely fair to criticise bad tools for being bad, particularly in specific ways and with clearly identified problems that can result as in this case. It's bad enough that we are stuck with JS for front-end web development these days, but there aren't many good arguments for using something as bad as PHP on the back-end in 2015.

Re:PHP: md5('240610708') == md5('QNKCDZO') by BasDirks on Monday May 04, 2015 @10:57AM (#9486516)
And what is gained from doing so? We must remain critical.
Re:PHP: md5('240610708') == md5('QNKCDZO') by sarciszewski on Monday May 04, 2015 @11:15AM (#9486643)
No, the problem is using a shitty function like MD5 for any practical purpose.
Re:PHP: md5('240610708') == md5('QNKCDZO') by Shish2k on Wednesday May 06, 2015 @08:47AM (#9498186)
The hash function is completely irrelevant to this bug - whether you use a hash that returns 0 for every input, or invent a hash function that returns a perfectly unique and unpredictable hash for all inputs, PHP will still shoot you in the foot.
Re:PHP: md5('240610708') == md5('QNKCDZO') by jrockway on Monday May 04, 2015 @07:35AM (#9485519)
I didn't hear him blaming PHP. Defensive, are we?
Re:PHP: md5('240610708') == md5('QNKCDZO') by roblfrieburg on Monday May 04, 2015 @08:11AM (#9485631)
He didn't blame PHP, just never used it again for anything important. Did we read the same comment?
Re:PHP: md5('240610708') == md5('QNKCDZO') by ilyazub on Friday May 08, 2015 @08:53AM (#9511311)
Please, read "The Design of Everyday Things": http://www.amazon.com/Design-Everyday-Things-Donald-Norman/d...
Re:PHP: md5('240610708') == md5('QNKCDZO') by lectrick on Wednesday May 06, 2015 @03:23PM (#9501180)
If you don't like "keep it simple stupid" and determinism in your language of choice (much less immutability)... you're basically everything wrong with programming in the year 2015
Re:PHP: md5('240610708') == md5('QNKCDZO') by imakesnowflakes on Monday May 04, 2015 @08:41AM (#9485741)
You bought a new car. You took it out for a ride. a tree falls before you. You brake, but the car proceeded to hit the tree anyway.

You call the car company and talk to their engineers. One of them ask. 'Did this happen on a Friday evening, when it was raining?' You say 'Yes, how do you know?'

The engineer replies.

"Our brakes does not work on rainy Friday evenings. If you REALLY want to brake on a rainy Friday evening, you should also pull the lever under the dash board that is normally used to open the hood. It is very clearly printed on our manual. Didn't you read it? Our car is not the problem. You are the problem"

You were enlightened. You came back home. You never took the car out on rainy Friday evenings. When Somebody asks about the car, You said. "Yea, it is a great car. But you got to know how to use it".

You took great pride in knowing how to drive this car, which can easily kill someone who hasn't read the manual. When you hear that someone got killed while driving this car, you simply said. 'That car is Ok. but you should really know how to drive it, sadly this guy didn't. He was the problem, the car ain't...

Re:PHP: md5('240610708') == md5('QNKCDZO') by picks_at_nits on Monday May 04, 2015 @08:53AM (#9485801)
From now on, when I write “RTFM,” I will also link to this comment.
Re:PHP: md5('240610708') == md5('QNKCDZO') by sarciszewski on Monday May 04, 2015 @12:01PM (#9487003)
> You bought a new car.

There's your problem, wasting money on something that only depreciates in value. Tsk tsk.

Re:PHP: md5('240610708') == md5('QNKCDZO') by laptopleon on Monday May 11, 2015 @09:33AM (#9524554)
It's not the complexity of the car manual, but the falling tree that I fear.
Re:PHP: md5('240610708') == md5('QNKCDZO') by codeinchaos on Monday May 04, 2015 @09:01AM (#9485832)
sure, everything should be done perfectly or not at all ...
Re:PHP: md5('240610708') == md5('QNKCDZO') by picks_at_nits on Monday May 04, 2015 @09:12AM (#9485898)
We can accept that perfection may be impossible, difficult to obtain, or a poor tradeoff against other factors.

But that doesn’t mean that all imperfect designs are of equal merit.

Re:PHP: md5('240610708') == md5('QNKCDZO') by lars on Monday May 04, 2015 @04:31AM (#9484981)
This, combined with the fact that you can increment strings gives some 'interesting' results:

    $a = "2d9"; 
    $a++; 
    echo $a . "\n"; 
    $a++; 
    echo $a . "\n"; 
Output

    2e0
    3
Re:PHP: md5('240610708') == md5('QNKCDZO') by ulrikrasmussen on Monday May 04, 2015 @05:30AM (#9485132)
This is wonderful, I love it!

Interestingly [1], this echoes "2e0" followed by "3" in hhvm-3.7.0, but "3" followed by "4" in hhvm-3.6.0.

[1] http://3v4l.org/sJhP8

Re:PHP: md5('240610708') == md5('QNKCDZO') by debacle on Monday May 04, 2015 @11:16AM (#9486654)
That means that someone was using this "feature" in a relatively core piece of code from the PHP ecosystem. Enough that hhvm felt they needed to support it.
Re:PHP: md5('240610708') == md5('QNKCDZO') by jlouis on Monday May 04, 2015 @09:14AM (#9485904)
There is some nasty type conversion going on here, from the type of stochastic random throws of two nine-sided dice to floats to integers. Where is your type preservation, PHP?
Re:PHP: md5('240610708') == md5('QNKCDZO') by Someone1234 on Monday May 04, 2015 @10:03AM (#9486170)
Is there any way to defend against this one? I know === to turn off type conversion with the equality operator, but what about here?
Re:PHP: md5('240610708') == md5('QNKCDZO') by digitalzombie on Monday May 04, 2015 @11:04AM (#9486566)
If condition check type and not try to increment a string...

if (!is_string($notstr)) ++$notstr;

edit:

I think checking for integer is better, if it's just incrementing integer.

I was going to say type hinting but I just realized php's primitive cannot be type hinted.

Re:PHP: md5('240610708') == md5('QNKCDZO') by marcofiset on Monday May 04, 2015 @09:58AM (#9486137)
Who tries to increment strings anyway? What is your point here?
Re:PHP: md5('240610708') == md5('QNKCDZO') by mikeash on Monday May 04, 2015 @10:37AM (#9486367)
Given how happy PHP is about converting strings to integers on demand, it would be pretty easy to take string input intended to be a number, forget to actually convert it, and go around using it happily until one day you accidentally set off a bomb.
Re:PHP: md5('240610708') == md5('QNKCDZO') by BasDirks on Monday May 04, 2015 @11:13AM (#9486627)
Yeah, who would do that? Nobody. So why the _ is it possible in the first place?
Re:PHP: md5('240610708') == md5('QNKCDZO') by pionar on Monday May 04, 2015 @10:03AM (#9486171)
because PHP is dynamically typed, it's easier to accidentally increment a string.
Re:PHP: md5('240610708') == md5('QNKCDZO') by kyllo on Monday May 04, 2015 @09:04AM (#9485855)
Ahh PHP, the language where true == false

    php > if ((true == "foo") && ("foo" == 0) && (0 == false)) echo "yay!";
    yay!
Re:PHP: md5('240610708') == md5('QNKCDZO') by Silhouette on Monday May 04, 2015 @01:56PM (#9487812)
I've never seen one, but somewhere there must surely be a PHP version of the infamous 'WAT' talk about JavaScript, full of examples like this and the "2d9"->"2e0"->3 example mentioned by lars.
Re:PHP: md5('240610708') == md5('QNKCDZO') by kuschku on Monday May 04, 2015 @03:24PM (#9488432)
Re:PHP: md5('240610708') == md5('QNKCDZO') by jakcst on Monday May 04, 2015 @10:00AM (#9486157)
This truly just bummed me out :(
Re:PHP: md5('240610708') == md5('QNKCDZO') by Navarr on Monday May 04, 2015 @10:18AM (#9486241)
Don't let it - he understands exactly how the types are being converted in order to make it appear that true == false.

This sort of thing happens in type conversion languages. You can either use === to stop conversion or you can understand how conversion works.

I'm not sure how the order of conversions is decided by PHP, but here's a brief explanation:

Compare "foo" to true. Convert the string "foo" to a boolean value. As it is desirable that a non-empty string evaluate to true, we will say they are "equal."

Compare "foo" to 0. Convert the string "foo" to a numeric value. As "foo" does not start with 0x it cannot be hex, and as it does not start with 0 it cannot be octal, so evaluate it as decimal - there are no numbers before the first letter so the string foo, when numerical, is 0.

Evaluate 0 to false. Well, that's just binary now isn't it? Of course false and 0 are equal!

The moral of the story, == is not "exactly equal" it is "relatively equal."

Re:PHP: md5('240610708') == md5('QNKCDZO') by kyllo on Monday May 04, 2015 @10:44AM (#9486427)
The problem with designing a language that does these sorts of implicit type conversions is that the "equality" operator violates the fundamental properties of equality. Since grade school mathematics we are all taught that equality is symmetric and transitive, and PHP's == operator is neither.
Re:PHP: md5('240610708') == md5('QNKCDZO') by spdionis on Wednesday May 06, 2015 @12:40PM (#9499914)
AFAIK == is always symmetric.
Re:PHP: md5('240610708') == md5('QNKCDZO') by Silhouette on Monday May 04, 2015 @02:03PM (#9487860)
You can either use === to stop conversion or you can understand how conversion works.

It has been my experience that, to a first approximation, no-one fully understands how conversion works in such languages to the point of never getting it wrong in practice.

Of course we didn't know that would be the case when some of these languages were first created, but I think it is a compelling argument for making an actual-equality == operator the default in any new programming language design. There are enough plausible differences because of things like reference vs. value semantics already, without breaking basic intuitions about what comparisons mean as well.

Re:PHP: md5('240610708') == md5('QNKCDZO') by nathan_long on Wednesday May 06, 2015 @12:03PM (#9499642)
> This sort of thing happens in type conversion languages. You can either use === to stop conversion or you can understand how conversion works.

You must admit that this is a lot of behavior to keep in mind.

Eg, there is no pattern like "try converting the value on the right to the type of the value on the left".

> Compare "foo" to 0. Convert the string "foo" to a numeric value.

I would expect this to convert 0 to "0" and fail. I suppose it's done this way because there's no way to represent a hexadecimal number except as a string.

> The moral of the story, == is not "exactly equal" it is "relatively equal."

The moral of the story for me would be "never use ==", if I were using PHP. I don't want to think about so many rules when trying to do a simple comparison.

FWIW, Ruby allows type conversion, but it generally must be explicit: `5 == "5"` is false; you must either do `5.to_s` or `"5".to_i` to compare, therefore nothing unexpected can happen. `if some_var` does "convert" to a boolean, but the rule is "nil and false are falsey, everything else is truthy", so again, not much to remember.

"Hard to mess up" is better than "easy to mess up", even if it's possible to avoid the mistake.

Re:PHP: md5('240610708') == md5('QNKCDZO') by Nemcue on Monday May 04, 2015 @11:00AM (#9486542)
"This sort of thing happens in type conversion languages. You can either use === to stop conversion or you can understand how conversion works."

Even JavaScript isn't insane enough to somehow coerce a string to 0.

Re:PHP: md5('240610708') == md5('QNKCDZO') by adamzochowski on Monday May 04, 2015 @11:18AM (#9486671)
Yes, JavaScript will convert strings into numbers

    console.log(5*"12");
    60
    console.log(5*"0x0C");
    60
Re:PHP: md5('240610708') == md5('QNKCDZO') by fidz on Monday May 04, 2015 @03:31PM (#9488488)
Actually, sometimes type conversion make some code become a little bit handy.

We use Java at the backend and of course Javascript for frontend. When serializing, in Java we should

        String dataRaw = "42";
        int objectId = Integer.parseInt(dataRaw);
Meanwhile, in JS, it is fairly simple:

        dataRaw = "42";
        var objectid = +dataRaw;
Re:PHP: md5('240610708') == md5('QNKCDZO') by sukilot on Monday May 04, 2015 @01:42PM (#9487692)
12 is not 0.
Re:PHP: md5('240610708') == md5('QNKCDZO') by Navarr on Monday May 04, 2015 @02:30PM (#9488074)
Yes and no. JavaScript gives you the good ol' "NaN" which is a number (despite not being a number).

PHP doesn't have that concept in it.

Re:PHP: md5('240610708') == md5('QNKCDZO') by grkvlt on Tuesday May 05, 2015 @10:04AM (#9492515)
Sort of. 'NaN' is one of the IEEE 754 floating point constants, along with 'Inf' for infinity. They are numeric types, in that they can be returned via operations on numbers, such as dividing zero by zero or adding '-Inf' to 'Inf'. See https://en.wikipedia.org/wiki/NaN

I always understood that the 'isNaN()' function was required to check if a numeric variable is equal to 'NaN' directly, since normal equality cannot be used as there are multiple valid bitwise representations of 'NaN' in the standard - it is a float with an exponent of all ones and a non-zero fraction. However, 'isNaN()' now seems to have been co-opted into being used to check if a string is not a number, i.e. does not represent a numeric value, and in fact I believe this is now the documented description of the function in ECMAScript?

Re:PHP: md5('240610708') == md5('QNKCDZO') by DonHopkins on Tuesday May 05, 2015 @08:51AM (#9492146)
gNaN's Not a Number
Re:PHP: md5('240610708') == md5('QNKCDZO') by FractalNerve on Monday May 04, 2015 @08:07AM (#9485615)
I tried replacing == with === and it gave me bool(false) in all cases.

Here's the code: http://3v4l.org/15hr7

Re:PHP: md5('240610708') == md5('QNKCDZO') by VMG on Monday May 04, 2015 @05:36AM (#9485146)
Same goes for the `0E` prefix with an uppercase E

The likelihood of generating a hash value with that kind of prefix is 2 in 65536.

Finding a collision `hash(a) == hash(b)` with this "weak" equality comparison is approximately 1 in 256 if I'm not mistaken.

Re:PHP: md5('240610708') == md5('QNKCDZO') by dietrichepp on Monday May 04, 2015 @06:33AM (#9485308)
You are mistaken: my guess is that you are taking the square root because of the birthday paradox, but that is incorrect, and the birthday paradox does not apply here anyway.

The probability of generating a hash with the right prefix is 10 in 16^3, or about 0.25%. Finding a 0e... == 0e... collision has probability ~6e-6, if both inputs are random. The chance that two hashes collide in this way given N random inputs is 1-(1-p)^(N-1), for N>0.

Re:PHP: md5('240610708') == md5('QNKCDZO') by masklinn on Monday May 04, 2015 @06:20AM (#9485269)
> The likelihood of generating a hash value with that kind of prefix is 2 in 65536.

The prefix is not sufficient though, the suffix must be entirely decimal otherwise it's not a valid number in scientific notation.

Re:PHP: md5('240610708') == md5('QNKCDZO') by dietrichepp on Monday May 04, 2015 @06:35AM (#9485318)
The prefix is sufficient. Any hash matching /0e[0-9].*/ works.
Re:PHP: md5('240610708') == md5('QNKCDZO') by masklinn on Monday May 04, 2015 @06:43AM (#9485340)
As far as I can see the prefix is not sufficient, a single non-digit character in the tail fails the conversion (and the equality check): http://3v4l.org/ctASF (vs http://3v4l.org/5FvJu, exact same strings but for the last character replaced by a digit)
Re:PHP: md5('240610708') == md5('QNKCDZO') by userbinator on Monday May 04, 2015 @07:05AM (#9485401)
Which means the probability of generating a hash value of the form 0[eE][0-9]{30} is (1/128)(10/16)^30 or 5.9e-9.

It certainly reduces the strength of the hash (and MD5 shouldn't be used anymore in any case), but still a roughly 6 in a billion chance of someone choosing e.g. a password and it happening to be exploitable in this manner.

Re:PHP: md5('240610708') == md5('QNKCDZO') by rimantas on Monday May 04, 2015 @07:50AM (#9485561)
From the manual:

  > The value is given by the initial portion of the string.
  > If the string starts   with valid numeric data, this will
  > be the value used. Otherwise, the value will be 0 (zero).
  > Valid numeric data is an optional sign, followed by one
  > or more digits (optionally containing a decimal point),
  > followed by an optional exponent. The exponent is an 'e'
  > or 'E' followed by one or more digits.
Also:

  > If you compare a number with a string or the comparison
  > involves numerical strings, then each string is converted
  > to a number and the comparison performed numerically.
Re:PHP: md5('240610708') == md5('QNKCDZO') by sigsergv on Monday May 04, 2015 @04:15AM (#9484930)
You're right:

% php -r 'var_dump("0e1" == "0e2");' bool(true)

Re:PHP: md5('240610708') == md5('QNKCDZO') by maw on Monday May 04, 2015 @08:20AM (#9485670)
I don't think I'd use the word "magic" here, because it implies that == works when, by any reasonable standard, it does not.
Re:PHP: md5('240610708') == md5('QNKCDZO') by redthrowaway on Monday May 04, 2015 @02:16PM (#9487973)
Type coercion is fine so long as you recognize it as the syntactic sugar that it is. JS and PHP support easy type coercion because HTTP is string-only and it would be a pain in the ass to explicitly cast every value you get over the wire. You just have to be sure that, when you use it, you do so intentionally and not out of laziness.
Re:PHP: md5('240610708') == md5('QNKCDZO') by steego on Monday May 04, 2015 @04:01PM (#9488715)
Here's what I took away::

It's pain in the ass to validate and sanitize your input.

Re:PHP: md5('240610708') == md5('QNKCDZO') by protomyth on Monday May 04, 2015 @09:09AM (#9485882)
So, you should use the old shell trick of adding an "X" to the front of the strings before comparing?
Re:PHP: md5('240610708') == md5('QNKCDZO') by maxlybbert on Monday May 04, 2015 @10:09AM (#9486197)
Or use === instead of ==.

The PHP developers have been pretty honest about the mistakes they made early on because they didn't know better. Unfortunately, many of those mistakes persist. The difference between == and === is one of the more well-known mistakes.