web-dev-qa-db-ja.com

2つの方法PHP双方向暗号化-どちらが望ましいですか?

一部のデータを暗号化して、後で復号化する必要があります。データは特定のユーザーに関連付けられています。私は2つの可能な解決策を集めました...

1:最初のものは公式ドキュメントから派生しています(例#1 @ http://php.net/manual/en/ function.mcrypt-encrypt.php ):

function encrypt($toEncrypt)
{
    global $key;
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_Rand);
    return base64_encode($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $toEncrypt, MCRYPT_MODE_CBC, $iv));
}

function decrypt($toDecrypt)
{
    global $key;
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $toDecrypt = base64_decode($toDecrypt);
    return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
}

鍵は、次を使用して一度生成されます。

pack('H*', bin2hex(openssl_random_pseudo_bytes(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC))));

1.1:暗号化された結果は常に2つの等号( '==')で終わることに気付きました。どうして? -それぞれbase64_encode()/ base64_decode()の代わりに、encrypt()およびdecode()でbin2hex()およびhex2bin()を使用しても、これらの結果は得られません。

1.2:bin2hex()/ hex2bin()を使用すると、結果(長さ以外)に何らかの影響がありますか?

1.3:解読時に返される結果に対してトリム関数を呼び出すかどうかについて議論があるようです(これは以下のソリューションにも適用されます) )。なぜこれが必要なのでしょうか?


2:2番目の解決策はここから来ます、Stackoverflow( https://stackoverflow.com/questions/9262109/php-simplest-two- way-encryption ):

function encrypt($key, $toEncrypt)
{
    return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $toEncrypt, MCRYPT_MODE_CBC, md5(md5($key))));
}

function decrypt($key, $toDecrypt)
{
    return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($toDecrypt), MCRYPT_MODE_CBC, md5(md5($key))), "\0");
}

キー処理への両方のアプローチは互換性があることを承知しています。考えられる解決策を強調するために、意図的にそれらを異なるようにしました。自由に組み合わせて一致させてください。

個人的には、キーと初期化ベクトルの両方が適切にランダム化されているので、最初のものはより厳しいセキュリティを提供すると思います。ただし、2番目のソリューションは、キーが暗号化されたデータの各部分に対して一意であるため(md5()の弱いランダム化の影響を受けるにもかかわらず)、何らかの形の予測不可能性を提供します。キーは、たとえばユーザーの名前にすることができます。

3:では、どちらが望ましいですか? Stackoverflowの回答がなんと105票を獲得したので、私は少し暗闇にいます。他の考え、ヒント?

4:おまけの質問!:私はサーバーのセキュリティの面で信じられないほど聡明ではありませんが、明らかにPHP =ファイルはキーを公開しますが、直接の結果として、攻撃者もDBにアクセスできると想定すると、暗号化は役に立たなくなります。キーを隠す方法はありますか?

お読みいただきありがとうございます。良い一日を!

7
user2026991

2つの暗号化は同等です。ラッパーを追加して、一方のアプローチをもう一方のドロップイン置換として使用できます。

/**
* Uses the one-parameter method as if it was the 2-parameter one.
*/
function buhlencrypt($keya, $data) {
   global $key;
   $key = $keya;
   return encrypt($data);
}

/**
* Uses the two-parameter method as if it was the one-parameter.
*/
function weidencrypt($data) {
   global $key;
   return encrypt($key, $data);
}

MD5の「予測可能性」は、「安全でない」場合、無視できるレベルを追加します。ユーザーの99.99999%は、簡単なパスワードを選択したり、アドレス帳に書き留めたり、キーボードの下の黄色の付箋に残したりしてデータがハッキングされます。プロの攻撃はサーバーをハッキング、MD5をブルートフォースするのではなく、すべてのパスワードを収集します(質問4の回答で以下を参照)。

1.1:暗号化された結果は常に2つの等号( '==')で終わることに気付きました。どうして? -それぞれbase64_encode()/ base64_decode()の代わりに、encrypt()およびdecode()でbin2hex()およびhex2bin()を使用しても、これらの結果は得られません。

Base64エンコードは、3つのバイナリバイト(3 x 8 = 24ビット)のグループを4つのASCII-6文字(4 x 6 = 24ビット)に置き換えることで機能します。つまり、入力テキストの長さは3の倍数でなければならず、出力の長さは常に4の倍数になります。

これを確実にするために、Base64は出力に特別なパディングを追加して、「無視できる」文字が1つまたは2つあるかどうかを示します。そして、Rijndaelは16の倍数の出力を生成するため、最後の16ブロックは常に長さ18までパディングされます。

1.2:bin2hex()/ hex2bin()を使用すると、結果(長さ以外)に何らかの影響がありますか?

まったくありません。

1.3:解読時に返された結果に対してtrim関数を呼び出すかどうかについての議論があるようです

Rijndaelも固定サイズのブロックを使用するため、17バイトを暗号化すると、2つの16バイトブロックに分割され、32バイトに最後の15バイトが埋め込まれます。 Rijndaelはパッドの削除を示すための準備を行わないため(たとえば、クリアテキストの長さを格納しない)、出力には削除が必要になる可能性のあるゼロ化されたパディングが含まれます。コンテキストによっては、ゼロパディングが無視される場合があるため、これは不要な場合があります。

4:おまけの質問!:サーバーのセキュリティの面で私は信じられないほど聡明ではありませんが、PHPファイルへのアクセス権を取得するとキーが公開され、直接の結果として、暗号化が役に立たなくなります、攻撃者がDBにもアクセスできると想定しています。キーを隠す方法はありますか?

対称的に行う場合は、PHPファイルにアクセスすることで、覆い隠しを削除することもできます。PHP = files。彼のアクセスが完全に近い場合、キーを難読化することはほとんど意味がありません。

ただし、これは迂回的な方法ですが可能です。

UserKey、RootKey、およびRootKeyOKという3つのプロパティ(つまり、2つの列テーブル)をユーザーモデルに追加します。各ユーザーはトリプレットを取得します。

ユーザーが登録すると、ランダムなIVが生成され、ユーザーのパスワードを使用して暗号化され、UserKeyに保存されます。また、それをRootKeyにクリアで保存し、RootKeyOKをFALSEに設定します。

ユーザーがパスワードを変更すると、古いパスワードを使用してUserKeyを復号化し、新しいパスワードで再暗号化します。 RootKeysに触れる必要はまったくありません。

adminがログインするたびに、ユーザーデータベースを参照してOKでないキーを探します。それらのそれぞれについて、管理者のパスワードでRootKeyを暗号化し、RootKeyOKをTRUEに設定します。これは自動的に行うことができます。

これで、データベースには次のようになります。

  • HashedPassword ---ハッシュであるため、攻撃者は使用できません。
  • UserKey ---暗号化されているため使用できません
  • RootKey ---使用可能、まだ平文であるが、このユーザーのデータのみ

ユーザーがログインしていて、ユーザー(および管理者)のみが読み取ることができるものを暗号化する必要がある場合は、ユーザーのパスワード(ログインしているため、メモリにある)を使用してuserKeyを復号化し、userKeyを使用してデータを暗号化します。

もちろんユーザーも管理者もログインしていないときに何かを暗号化する必要がある場合、このアプローチは機能しません-暗号化せずに保存する必要があります暗号化する必要があることを示すフラグを付け、ユーザーまたは管理者がログインするとすぐにそれを実行します。あなたができる間これを両方に対して透過的に行います。実際には、サーバー上で長期間データがクリアに残る可能性があり、データベースキャプチャに対して脆弱です(リモートの侵入からハードディスクの強盗まで)。

1
LSerni

これ気に入った openssl_encrypt、さまざまなものがあります。試すことができます:

http://www.the-art-of-web.com/php/two-way-encryption/

0
T.Todua