[php-src] Issue #8397: crypt() accepts invalid characters in salt

From: Date: Mon, 18 Apr 2022 18:51:48 +0000
Subject: [php-src] Issue #8397: crypt() accepts invalid characters in salt
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-241058@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/8397 Author: martenlehmann ### Description The following code: ```php <?php printf( "%s\n%s\n", crypt('whatever', '$6$+=./CIKSWMxwyohL$'), crypt('whatever', '$6$+=.äöü😀bCI$') ); ``` Resulted in this output: ``` $6$+=./CIKSWMxwyohL$sb5N9Nb5Bt2qDbLbOQ9RtoHxhC1K8w7ijWOzmio17b1CMLcjBGG3m8amcWHCoXJB0G25aZ4uutgnxjKbliUbQ/ $6$+=.äöü😀bCI$Xe9mv1hkb03sKiDi6JRdRbd02i0tz/46XpLPHoLB1pq0qSZV7To.NWmQieb7mA4eZ.kZBmfuCRJYlpDgi59Hu0 ``` But I expected an exception or *0 instead of this output, because both salts contain invalid characters. Background: In our setup, PHP is used to create a hash, that will be distributed to and consumed by different other services (OpenLDAP, Apache HTTP Server, Python Backends etc.). Therefore we cannot use the latest hashing functions or algorithms, but the highest common denominator, which is the OS's crypt() function with SHA512. Up until (including) Ubuntu 18.04, the OS's crypt() supported salts including +=./. Starting Ubuntu 20.04, the special characters have been limited to ./. However, PHP doesn't seem to care about that. - In the first example, the salt contains +=./, which once worked, yet has later been restricted. - In the second example, the salt contains even an emoji, which wasn't allowed ever. What's so bad about this? There is no way to tell - using PHP - that the salts and generated hashes are invalid and will cause verification problems in other systems. Even PHP's hash_equals() and password_verify() functions have zero issue when verifying these hashes against the original passwords. Unfortunately, all services consuming these hashes and relying on the system's crypt() for verification do. In Python, the following code (using the first password and salt as above) will return *0: ```python import crypt crypt.crypt('whatever', '$6$+=./CIKSWMxwyohL$') ``` So does Perl: ```perl printf( "%s\n", crypt('whatever', '$6$+=./CIKSWMxwyohL$') ); ``` The passlib library for Python will even raise a more explicit ValueError exception: invalid characters in sha512_crypt salt ```python from passlib.hash import sha512_crypt sha512_crypt.verify('whatever', '$6$+=./CIKSWMxwyohL$sb5N9Nb5Bt2qDbLbOQ9RtoHxhC1K8w7ijWOzmio17b1CMLcjBGG3m8amcWHCoXJB0G25aZ4uutgnxjKbliUbQ/') ``` [passlib](https://passlib.readthedocs.io/en/stable/lib/passlib.hash.sha256_crypt.html) documents the allowed characters for the salt as follows: [./0-9A-Za-z] Perldoc on [crypt()](https://perldoc.perl.org/functions/crypt) does it as well: [./0-9A-Za-z] So I see two issues: 1. As PHP doesn't provide a function to create a valid salt (like crypt.mksalt(crypt.METHOD_SHA512) in Python), this step relies on the creativity of the developer and since the allowed characters aren't well documented, there is a big chance that invalid characters will be part of the salt and thus the hash. 2. As PHP doesn't seem to use the OS's crypt() and it's own implementation doesn't seem to validate the input as strict, there is a big chance this happens without noticing and unexpected errors in other systems will occur. A naive upgrade from Ubuntu 18.04 to 20.04 would have created tons of failed logins in our setup. ### PHP Version PHP 7.4.3 ### Operating System Ubuntu 20.04

« previous php.bugs (#241058) next »