質問: 双方向暗号化:取得できるパスワードを保存する必要があります ユーザーが取得して表示できるパスワードを保存するアプリケーションを作成しています。パスワードはハードウェアデバイス用であるため、ハッシュに対するチェックは問題外です。私が知る必要があるのは: phpでパスワードを暗号化および復号化するにはどうすればよいですか? パスワードを暗号化する最も安全なアルゴリズムは何ですか? 秘密鍵はどこに保存しますか? 秘密キーを保存する代わりに、パスワードを復号化する必要がある場合は常にユーザーに秘密キーの入力を要求することをお勧めしますか? (このアプリケーションのユーザーは信頼できます) パスワードはどのようにして盗まれ、解読されますか?何に注意する必要がありますか?
回答: 個人的には、他の人が投稿したようにmcryptを使用します。しかし、注意すべき点はまだまだあります... phpでパスワードを暗号化および復号化するにはどうすればよいですか? あなたのためにすべてを処理する強力なクラスについては、以下を参照してください: パスワードを暗号化する最も安全なアルゴリズムは何ですか? safest?それらのいずれか。暗号化する場合の最も安全な方法は、情報漏えいの脆弱性(xss、リモートインクルージョンなど)から保護することです。それが外に出れば、攻撃者は最終的に暗号化を解読できます(暗号化はキーなしでは100%不可逆ではありません-@nulluserexceptionが指摘するように、これは完全に真実ではありません。 onetimepad )。 秘密鍵はどこに保存しますか? 私がすることは3つのキーを使用することです。 1つはユーザー指定、1つはアプリケーション固有、もう1つはユーザー固有(ソルトなど)です。アプリケーション固有のキーはどこにでも保存できます(web-rootの外部の設定ファイル、環境変数など)。ユーザー固有のパスワードは、暗号化されたパスワードの横のdbの列に保存されます。ユーザーが指定したものは保存されません。次に、次のようなことをします。 $key = $userkey . $serverkey . $usersuppliedkey; そこでの利点は、データを危険にさらすことなく、任意の2つのキーを危険にさらすことができることです。 sqlインジェクション攻撃がある場合、それらは$userkeyを取得できますが、他の2は取得できません。ローカルサーバーの悪用がある場合、$userkeyと$serverkeyを取得できますが、3番目の$usersuppliedkeyは取得できません。彼らがレンチでユーザーを倒すと、彼らは$usersuppliedkeyを取得できますが、他の2は取得できません(しかし、ユーザーがレンチでbeatられた場合は、とにかく遅すぎます)。 秘密キーを保存する代わりに、パスワードを復号化する必要がある場合は常にユーザーに秘密キーの入力を要求することをお勧めしますか? (このアプリケーションのユーザーは信頼できます) 絶対に。実際、それが私がそれをする唯一の方法です。それ以外の場合は、暗号化されていないバージョンを永続ストレージ形式(apcやmemcachedなどの共有メモリ、またはセッションファイル)に保存する必要があります。それは、さらなる妥協にさらされています。暗号化されていないバージョンのパスワードをローカル変数以外に保存しないでください。 パスワードはどのようにして盗まれ、解読されますか?何に注意する必要がありますか? システムを侵害すると、暗号化されたデータが表示されます。コードを挿入したり、ファイルシステムにアクセスできる場合、解読されたデータを表示できます(データを解読するファイルを編集できるため)。リプレイまたはmitm攻撃のあらゆる形式も、関係するキーへのフルアクセスを提供します。生のhttpトラフィックをスニッフィングすることも、それらにキーを与えます。 すべてのトラフィックにsslを使用します。また、サーバー上にいかなる種類の脆弱性(csrf、xss、sqlインジェクション、特権エスカレーション、リモートコード実行など)も含まれていないことを確認してください。 編集:強力な暗号化メソッドのphpクラス実装です:/** * a class to handle secure encryption and decryption of arbitrary data * * note that this is not just straight encryption. it also has a few other * features in it to make the encrypted data far more secure. note that any * other implementations used to decrypt data will have to do the same exact * operations. * * security benefits: * * - uses key stretching * - hides the initialization vector * - does hmac verification of source data * */ class encryption { /** * @var string $cipher the mcrypt cipher to use for this instance */ protected $cipher = ''; /** * @var int $mode the mcrypt cipher mode to use */ protected $mode = ''; /** * @var int $rounds the number of rounds to feed into pbkdf2 for key generation */ protected $rounds = 100; /** * constructor! * * @param string $cipher the mcrypt_* cypher to use for this instance * @param int $mode the mcrypt_mode_* mode to use for this instance * @param int $rounds the number of pbkdf2 rounds to do on the key */ public function __construct($cipher, $mode, $rounds = 100) { $this->cipher = $cipher; $this->mode = $mode; $this->rounds = (int) $rounds; } /** * decrypt the data with the provided key * * @param string $data the encrypted datat to decrypt * @param string $key the key to use for decryption * * @returns string|false the returned string if decryption is successful * false if it is not */ public function decrypt($data, $key) { $salt = substr($data, 0, 128); $enc = substr($data, 128, -64); $mac = substr($data, -64); list ($cipherkey, $mackey, $iv) = $this->getkeys($salt, $key); if (!hash_equals(hash_hmac('sha512', $enc, $mackey, true), $mac)) { return false; } $dec = mcrypt_decrypt($this->cipher, $cipherkey, $enc, $this->mode, $iv); $data = $this->unpad($dec); return $data; } /** * encrypt the supplied data using the supplied key * * @param string $data the data to encrypt * @param string $key the key to encrypt with * * @returns string the encrypted data */ public function encrypt($data, $key) { $salt = mcrypt_create_iv(128, mcrypt_dev_urandom); list ($cipherkey, $mackey, $iv) = $this->getkeys($salt, $key); $data = $this->pad($data); $enc = mcrypt_encrypt($this->cipher, $cipherkey, $data, $this->mode, $iv); $mac = hash_hmac('sha512', $enc, $mackey, true); return $salt . $enc . $mac; } /** * generates a set of keys given a random salt and a master key * * @param string $salt a random string to change the keys each encryption * @param string $key the supplied key to encrypt with * * @returns array an array of keys (a cipher key, a mac key, and a iv) */ protected function getkeys($salt, $key) { $ivsize = mcrypt_get_iv_size($this->cipher, $this->mode); $keysize = mcrypt_get_key_size($this->cipher, $this->mode); $length = 2 * $keysize + $ivsize; $key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length); $cipherkey = substr($key, 0, $keysize); $mackey = substr($key, $keysize, $keysize); $iv = substr($key, 2 * $keysize); return array($cipherkey, $mackey, $iv); } /** * stretch the key using the pbkdf2 algorithm * * @see http://en.wikipedia.org/wiki/pbkdf2 * * @param string $algo the algorithm to use * @param string $key the key to stretch * @param string $salt a random salt * @param int $rounds the number of rounds to derive * @param int $length the length of the output key * * @returns string the derived key. */ protected function pbkdf2($algo, $key, $salt, $rounds, $length) { $size = strlen(hash($algo, '', true)); $len = ceil($length / $size); $result = ''; for ($i = 1; $i <= $len; $i++) { $tmp = hash_hmac($algo, $salt . pack('n', $i), $key, true); $res = $tmp; for ($j = 1; $j < $rounds; $j++) { $tmp = hash_hmac($algo, $tmp, $key, true); $res ^= $tmp; } $result .= $res; } return substr($result, 0, $length); } protected function pad($data) { $length = mcrypt_get_block_size($this->cipher, $this->mode); $padamount = $length - strlen($data) % $length; if ($padamount == 0) { $padamount = $length; } return $data . str_repeat(chr($padamount), $padamount); } protected function unpad($data) { $length = mcrypt_get_block_size($this->cipher, $this->mode); $last = ord($data[strlen($data) - 1]); if ($last > $length) return false; if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) { return false; } return substr($data, 0, -1 * $last); } } php 5.6で追加された関数 hash_equals を使用していることに注意してください。 5.6未満の場合は、 二重hmac検証 を使用して タイミングセーフ比較 関数を実装するこの代替関数を使用できます。function hash_equals($a, $b) { $key = mcrypt_create_iv(128, mcrypt_dev_urandom); return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key); } 使用法:$e = new encryption(mcrypt_blowfish, mcrypt_mode_cbc); $encrypteddata = $e->encrypt($data, $key); 次に、復号化するには:$e2 = new encryption(mcrypt_blowfish, mcrypt_mode_cbc); $data = $e2->decrypt($encrypteddata, $key); 2回目に$e2を使用して、異なるインスタンスがデータを適切に復号化することを示していることに注意してください。さて、それはどのように機能するのか/別のソリューションでそれを使用する理由: キー キーは直接使用されません。代わりに、キーは標準のpbkdf2派生によって引き伸ばされます。 暗号化に使用されるキーは、暗号化されたテキストブロックごとに一意です。したがって、指定されたキーは「マスターキー」になります。したがって、このクラスは、暗号キーと認証キーのキーローテーションを提供します。 重要な注意事項、$roundsパラメーターは、十分な強度の真のランダムキー(最低128ビットの暗号化セキュアランダム)に設定されます。パスワード、またはランダムでないキー(またはランダムではない128ビットのcsランダム)を使用する場合、mustこのパラメーターを増やします。パスワードには最低10000をお勧めします(余裕があればあるほど良いですが、ランタイムに追加されます)... データの整合性 更新されたバージョンはencrypt-then-macを使用します。これは、暗号化されたデータの信頼性を確保するためのはるかに優れた方法です。 暗号化: mcryptを使用して、実際に暗号化を実行します。モードにはmcrypt_blowfishまたはmcrypt_rijndael_128 cyphersとmcrypt_mode_cbcを使用することをお勧めします。それは十分に強力であり、それでもかなり高速です(私のマシンでは暗号化と復号化のサイクルに約1/2秒かかります)。 さて、最初のリストからポイント3に関して、それがあなたに与えるものはこのような関数です:function makekey($userkey, $serverkey, $usersuppliedkey) { $key = hash_hmac('sha512', $userkey, $serverkey); $key = hash_hmac('sha512', $key, $usersuppliedkey); return $key; } makekey()関数で引き伸ばすこともできますが、後で引き伸ばされるので、そうすることには大きな意味はありません。ストレージサイズに関しては、プレーンテキストに依存します。 blowfishは8バイトのブロックサイズを使用するため、次のようになります。 ソルト用の16バイト hmacの場合は64バイト データ長 データ長%8 == 0になるようにパディングする したがって、16文字のデータソースの場合、16文字のデータが暗号化されます。つまり、実際の暗号化されたデータサイズは、パディングのために16バイトです。次に、saltに16バイト、hmacに64バイトを追加すると、保存される合計サイズは96バイトになります。したがって、最大で80文字のオーバーヘッドがあり、最悪でも87文字のオーバーヘッドがあります...私はそれが役立つことを願っています...注:12/11/12:私はこのクラスを、はるかに優れた暗号化方法、より良い派生鍵の使用、およびmac生成の修正で更新しました。 。