crypt

(PHP 4, PHP 5, PHP 7, PHP 8)

crypt文字列の一方向のハッシュ化を行う

警告

この関数は (まだ)バイナリデータ対応ではありません!

説明

crypt(string $string, string $salt): string

crypt() 文字列のハッシュを返します。 Unix 標準の DES ベースのアルゴリズムか、 代替のアルゴリズムを使用します。 password_verify()crypt() と互換性があるので、 crypt() が作ったパスワードハッシュは password_verify() でも使えます。

PHP 8.0.0 より前のバージョンでは、salt パラメータは必須ではありませんでした。ただ、これを省略すると crypt() が作るハッシュが弱いものになってしまっていました。 このパラメータを省略した場合には、E_NOTICE が発生していました。 十分に強い salt を指定して、セキュリティを確保しましょう。

password_hash() は、強力なハッシュを使い、強力なソルトを生成して、それを複数回自動的に適用します。 password_hash()crypt() のシンプルなラッパーであり、既存のパスワードハッシュと互換性があります。 password_hash() を使うことを推奨します。

ハッシュ方式は、salt 引数によって決まります。 salt を省略した場合は、標準の 2 文字 (DES) の salt を自動生成します。 あるいは、MD5 crypt() が使えれば 12 文字 (MD5) の salt を自動生成します。 PHP の定数 CRYPT_SALT_LENGTH には、 ハッシュで使用できる salt の最大長が格納されています。

標準の DES ベースの場合、crypt() は出力の最初の 2 文字を salt として使用します。また、 string の最初の 8 文字しか使用しません。 つまり、最初の 8 文字が同じである長い文字列は、 同じ salt を使う限り同じ結果となります。

以下のハッシュ形式がサポートされています:

  • CRYPT_STD_DES - 標準の DES ベースのハッシュで、 アルファベット "./0-9A-Za-z" からなる 2 文字の salt を使用するもの。 salt として無効な文字を使うと crypt() は失敗します。
  • CRYPT_EXT_DES - 拡張した DES ベースのハッシュ。 "salt" は 9 文字で、 アンダースコアの後に 4 文字の反復回数と 4 文字の salt が続きます。 これらの4文字それぞれが、24ビットにエンコードされ、 下位の文字から順に並びます。 0 から 63 までの値は ./0-9A-Za-z の範囲内の文字でエンコードされます。 salt に無効な文字を使うと crypt() は失敗します。
  • CRYPT_MD5 - $1$ ではじまる 12 文字の salt を使用する MD5 ハッシュ。
  • CRYPT_BLOWFISH - Blowfish ハッシュ。salt の形式は、 "$2a$" か "$2x$" あるいは "$2y$"、2 桁のコストパラメータ、"$"、そして文字 "./0-9A-Za-z" からなる 22 文字となります。 この範囲外の文字を salt に使うと、crypt() は長さゼロの文字列を返します。 2 桁のコストパラメータは反復回数の 2 を底とする対数で、 これは Blowfish ベースのハッシュアルゴリズムで使います。 この値は 04 から 31 までの範囲でなければならず、 それ以外の値の場合は crypt() は失敗します。 "$2x$" ハッシュは潜在的に弱いハッシュです。 "$2a$" ハッシュはこれと互換性があり、この弱点を軽減します。 新しくハッシュを生成する場合は、"$2y$" を使うべきです。
  • CRYPT_SHA256 - SHA-256 ハッシュに $5$ で始まる 16 文字の salt を組み合わせたもの。salt 文字列が 'rounds=<N>$' で始まる場合は、数値 N がハッシュループの実行回数を表します。 これは Blowfish のコストパラメータのようなものです。 rounds のデフォルトは 5000 で、1000 から 999,999,999 までの値を指定できます。 この範囲外の N を指定すると、近い方の境界値に切り詰められます。
  • CRYPT_SHA512 - SHA-512 ハッシュに $6$ で始まる 16 文字の salt を組み合わせたもの。salt 文字列が 'rounds=<N>$' で始まる場合は、数値 N がハッシュループの実行回数を表します。 これは Blowfish のコストパラメータのようなものです。 rounds のデフォルトは 5000 で、1000 から 999,999,999 までの値を指定できます。 この範囲外の N を指定すると、近い方の境界値に切り詰められます。

パラメータ

string

ハッシュしたい文字列。

警告

CRYPT_BLOWFISH を使うと、 string が最大 72 バイトまでに切り詰められます。

salt

ハッシュのもととなる salt 文字列。省略した場合の挙動は アルゴリズムの実装によって決まるので、予期せぬ結果となることがあり得ます。

戻り値

ハッシュした文字列を返します。 失敗した場合は、salt とは異なることが保証されている 13 文字未満の文字列を返します。

警告

パスワードを検証するときの文字列比較関数は、 タイミング攻撃に対して脆弱ではないものでなければいけません。 これをもちいて、crypt() の出力と既知のハッシュとを比較します。 この比較を行うために、hash_equals() 関数が使えます。

変更履歴

バージョン 説明
8.0.0 salt は、オプションではなくなりました。

例1 crypt() の例

<?php
$user_input
= 'rasmuslerdorf';
$hashed_password = '$6$rounds=1000000$NJy4rIPjpOaU$0ACEYGg/aKCY3v8O8AfyiO7CTfZQ8/W231Qfh2tRLmfdvFD6XfHk12u6hMr9cYIA4hnpjLNSTRtUwYr9km9Ij/';

// PHP 以外のソフトウェアと互換性がある方法で、既存の crypt() ハッシュを検証します
if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) {
echo
"Password verified!";
}
?>

注意

注意: 復号するための関数はありません。 crypt() が使用しているのは単方向アルゴリズムだからです。

参考

  • hash_equals() - タイミング攻撃に対しても安全な文字列比較
  • password_hash() - パスワードハッシュを作る
  • 暗号化関数についての Unix man ページ

add a note add a note

User Contributed Notes 8 notes

up
66
bob dot orr at mailinator dot com
9 years ago
The #2 comment on this comments page (as of Feb 2015) is 9 years old and recommends phpass.  I have independently security audited this product and, while it continues to be recommended for password security, it is actually insecure and should NOT be used.  It hasn't seen any updates in years (still at v0.3) and there are more recent alternatives such as using the newer built-in PHP password_hash() function that are much better.  Everyone, please take a few moments to confirm what I'm saying is accurate (i.e. review the phpass code for yourself) and then click the down arrow to sink the phpass comment to the bottom.  You'll be increasing security across the Internet by doing so.

For those who want details:  md5() with microtime() are a fallback position within the source code of phpass.  Instead of terminating, it continues to execute code.  The author's intentions of trying to work everywhere are admirable but, when it comes to application security, that stance actually backfires.  The only correct answer in a security context is to terminate the application rather than fallback to a weak position that can potentially be exploited (usually by forcing that weaker position to happen).
up
22
Marten Jacobs
10 years ago
As I understand it, blowfish is generally seen a secure hashing algorithm, even for enterprise use (correct me if I'm wrong). Because of this, I created functions to create and check secure password hashes using this algorithm, and using the (also deemed cryptographically secure) openssl_random_pseudo_bytes function to generate the salt.

<?php
/*
* Generate a secure hash for a given password. The cost is passed
* to the blowfish algorithm. Check the PHP manual page for crypt to
* find more information about this setting.
*/
function generate_hash($password, $cost=11){
       
/* To generate the salt, first generate enough random bytes. Because
         * base64 returns one character for each 6 bits, the we should generate
         * at least 22*6/8=16.5 bytes, so we generate 17. Then we get the first
         * 22 base64 characters
         */
       
$salt=substr(base64_encode(openssl_random_pseudo_bytes(17)),0,22);
       
/* As blowfish takes a salt with the alphabet ./A-Za-z0-9 we have to
         * replace any '+' in the base64 string with '.'. We don't have to do
         * anything about the '=', as this only occurs when the b64 string is
         * padded, which is always after the first 22 characters.
         */
       
$salt=str_replace("+",".",$salt);
       
/* Next, create a string that will be passed to crypt, containing all
         * of the settings, separated by dollar signs
         */
       
$param='$'.implode('$',array(
               
"2y", //select the most secure version of blowfish (>=PHP 5.3.7)
               
str_pad($cost,2,"0",STR_PAD_LEFT), //add the cost in two digits
               
$salt //add the salt
       
));
      
       
//now do the actual hashing
       
return crypt($password,$param);
}

/*
* Check the password against a hash generated by the generate_hash
* function.
*/
function validate_pw($password, $hash){
       
/* Regenerating the with an available hash as the options parameter should
         * produce the same hash if the same password is passed.
         */
       
return crypt($password, $hash)==$hash;
}
?>
up
8
steve at tobtu dot com
11 years ago
To generate salt use mcrypt_create_iv() not mt_rand() because no matter how many times you call mt_rand() it will only have at most 32 bits of entropy. Which you will start seeing salt collisions after about 2^16 users. mt_rand() is seeded poorly so it should happen sooner.

For bcrypt this will actually generate a 128 bit salt:
<?php $salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.'); ?>

*** Bike shed ***
The last character in the 22 character salt is 2 bits.
base64_encode() will have these four character "AQgw"
bcrypt will have these four character ".Oeu"

You don't need to do a full translate because they "round" to different characters:
echo crypt('', '$2y$05$.....................A') . "\n";
echo crypt('', '$2y$05$.....................Q') . "\n";
echo crypt('', '$2y$05$.....................g') . "\n";
echo crypt('', '$2y$05$.....................w') . "\n";

$2y$05$......................J2ihDv8vVf7QZ9BsaRrKyqs2tkn55Yq
$2y$05$.....................O/jw2XygQa2.LrIT7CFCBQowLowDP6Y.
$2y$05$.....................eDOx4wMcy7WU.kE21W6nJfdMimsBE3V6
$2y$05$.....................uMMcgjnOELIa6oydRivPkiMrBG8.aFp.
up
6
kaminski at istori dot com
13 years ago
Here is an expression to generate pseudorandom salt for the CRYPT_BLOWFISH hash type:

<?php $salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22); ?>

It is intended for use on systems where mt_getrandmax() == 2147483647.

The salt created will be 128 bits in length, padded to 132 bits and then expressed in 22 base64 characters.  (CRYPT_BLOWFISH only uses 128 bits for the salt, even though there are 132 bits in 22 base64 characters.  If you examine the CRYPT_BLOWFISH input and output, you can see that it ignores the last four bits on input, and sets them to zero on output.)

Note that the high-order bits of the four 32-bit dwords returned by mt_rand() will always be zero (since mt_getrandmax == 2^31), so only 124 of the 128 bits will be pseudorandom.  I found that acceptable for my application.
up
0
jette at nerdgirl dot dk
11 years ago
The crypt() function cant handle plus signs correctly. So if for example you are using crypt in a login function, use urlencode on the password first to make sure that the login procedure can handle any character:

<?php
$user_input
'12+#æ345';
$pass = urlencode($user_input));
$pass_crypt = crypt($pass);

if (
$pass_crypt == crypt($pass, $pass_crypt)) {
  echo
"Success! Valid password";
} else {
  echo
"Invalid password";
}
?>
up
-10
Joey
7 years ago
While the documentation says that crypt will fail for DES if the salt is invalid, this turns out to not be the case.

The crypt function will accept any string of two characters or more for DES as long as it doesn't match the pattern for any other hashing schema. The remaining characters will be ignored.
up
-15
Anonymous
7 years ago
steve at tobtu dot com was right 4 years ago, but now mcrypt_create_iv()  (and bcrypt in general) is deprecated!

Use random_bytes() instead:

<?php
$salt
= base64_encode(random_bytes(16));
up
-18
ian+php dot net at eiloart dot ocm
10 years ago
If you're stuck with CRYPT_EXT_DES, then you'll want to pick a number of iterations: the 2nd-5th characters of the "salt".

My experimentation suggests that the 5th character is the most significant. A '.' is a zero and 'Z' is the highest value. Using all dots will create an error: all passwords will be encrypted to the same value.

Here are some encryption timings (in seconds) that I obtained, with five different iteration counts over the same salt, and the same password, on a quad core 2.66GHz Intel Xeon machine.

_1111 time: 0.15666794776917
_J9.Z time: 1.8860530853271
_J9.. time: 0.00015401840209961
_...Z time: 1.9095730781555
_ZZZZ time: 1.9124970436096
_...A time: 0.61211705207825

I think a half a second is reasonable for an application, but for the back end authentication? I'm not so sure: there's a significant risk of overloading the back end if we're getting lots of authentication requests.
To Top