web-dev-qa-db-ja.com

暗号化および復号化する方法 PHP 文字列?

つまり、

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

たぶん何かのような:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • PHPでは、どうすればこれができますか?

Crypt_Blowfishを使おうとしましたが、うまくいきませんでした。

192
夏期劇場

してはいけないこと

警告:
この回答では _ ecb _ を使用しています。 ECBは暗号化モードではなく、構築ブロックにすぎません。この回答で示されているようにECBを使用しても、実際には文字列を安全に暗号化することはできません。コードにECBを使用しないでください。 Scottの答え を見てください。

私は自分でそれを手に入れました。実際に私はグーグルでいくつかの答えを見つけただけで何かを修正しました。 結果は完全に安全ではありません。

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_Rand);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_Rand);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>
44
夏期劇場

さらに何かをする前に、 encryptionauthenticationの違い と、おそらくなぜ認証された暗号化が必要かを理解するように努めてくださいだけではなくencryption

認証された暗号化を実装するには、暗号化してからMACにする必要があります。 暗号化と認証の順序は非常に重要です! この質問に対する既存の回答の1つがこの間違いを犯しました。 PHPで書かれた多くの暗号化ライブラリがそうであるように。

独自の暗号化の実装を避ける を使用し、代わりに暗号化の専門家によって作成およびレビューされた安全なライブラリを使用する必要があります。

更新:PHP 7.2はlibsodiumを提供するようになりました!最高のセキュリティを確保するには、PHP 7.2以降を使用するようにシステムを更新し、この回答のlibsodiumアドバイスのみに従ってください。

PECLアクセスがある場合はlibsodiumを使用 (または sodium_compat PECLなしのlibsodiumが必要な場合);そうでなければ...
defuse/php-encryptionを使用 ;独自の暗号を転がさないでください!

上記の両方のライブラリを使用すると、認証された暗号化を独自のライブラリに簡単に実装できます。

インターネット上のすべての暗号化の専門家の常識に反して、独自の暗号化ライブラリを作成および展開したい場合は、これらの手順を実行する必要があります。

暗号化:

  1. CTRモードでAESを使用して暗号化します。 GCMを使用することもできます(これにより、別個のMACが不要になります)。さらに、ChaCha20およびSalsa20( libsodium で提供)はストリーム暗号であり、特別なモードを必要としません。
  2. 上記のGCMを選択した場合を除き、HMAC-SHA-256で暗号文を認証する必要があります(または、ストリーム暗号の場合はPoly1305-ほとんどのlibsodium APIがこれを行います)。 MACはIVと暗号文をカバーする必要があります!

復号化:

  1. Poly1305またはGCMを使用しない限り、暗号文のMACを再計算し、 hash_equals() を使用して送信されたMACと比較します。失敗した場合は中止します。
  2. メッセージを解読します。

その他の設計上の考慮事項:

  1. 何も圧縮しないでください。暗号文は圧縮できません。暗号化の前にプレーンテキストを圧縮すると、情報漏えいにつながる可能性があります(たとえば、TLSでのCRIMEおよびBREACH)。
  2. '8bit'文字セットモードを使用して、mbstring.func_overloadの問題を防ぐために、必ずmb_strlen()およびmb_substr()を使用してください。
  3. IVは CSPRNG ;を使用して生成する必要があります。 mcrypt_create_iv()を使用している場合は、MCRYPT_Randを使用しないでください
  4. AEADコンストラクトを使用していない限り、 常に暗号化してからMAC!
  5. bin2hex()base64_encode()などは、キャッシュのタイミングを介して暗号化キーに関する情報を漏らす可能性があります。可能な場合は避けてください。

ここで与えられたアドバイスに従ったとしても、暗号化に関して多くのことがうまくいかない可能性があります。 暗号化の専門家に実装のレビューを常に依頼してください。地元の大学で暗号化の学生と個人的な友達になれなかった場合、いつでも試すことができます Cryptography Stack Exchange アドバイスのためのフォーラム。

実装の専門的な分析が必要な場合は、いつでも PHP暗号化コードを確認するための信頼できるセキュリティコンサルタントチーム (開示:雇用主)を雇うことができます。

重要:暗号化を使用しない場合

Do n'tencryptpasswords。代わりに、これらのパスワードハッシュアルゴリズムのいずれかを使用してhashしたい:

パスワードの保存に汎用ハッシュ関数(MD5、SHA256)を使用しないでください。

RLパラメータを暗号化しないこれは、ジョブにとっては間違ったツールです。

Libsodiumを使用したPHP文字列暗号化の例

PHP <7.2を使用している場合、またはlibsodiumがインストールされていない場合は、 sodium_compat を使用して同じ結果を得ることができます(ただし遅い)。

<?php
declare(strict_types=1);

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 * @throws RangeException
 */
function safeEncrypt(string $message, string $key): string
{
    if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new RangeException('Key is not the correct size (must be 32 bytes).');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

    $cipher = base64_encode(
        $nonce.
        sodium_crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
    sodium_memzero($message);
    sodium_memzero($key);
    return $cipher;
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 * @throws Exception
 */
function safeDecrypt(string $encrypted, string $key): string
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    $plain = sodium_crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
    if (!is_string($plain)) {
        throw new Exception('Invalid MAC');
    }
    sodium_memzero($ciphertext);
    sodium_memzero($key);
    return $plain;
}

それをテストするには:

<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

ハライト-Libsodiumがより簡単に

私が取り組んできたプロジェクトの1つは、 Halite と呼ばれる暗号化ライブラリです。これは、libsodiumをより簡単で直感的にすることを目的としています。

<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;

// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');

$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

基礎となる暗号化はすべてlibsodiumによって処理されます。

Defuse/php-encryptionを使用した例

<?php
/**
 * This requires https://github.com/defuse/php-encryption
 * php composer.phar require defuse/php-encryption
 */

use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

require "vendor/autoload.php";

// Do this once then store it somehow:
$key = Key::createNewRandomKey();

$message = 'We are all living in a yellow submarine';

$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Crypto::encrypt()は、16進数でエンコードされた出力を返します。

暗号化キー管理

「パスワード」を使用したい場合は、今すぐ停止してください。人間が記憶できるパスワードではなく、ランダムな128ビット暗号化キーが必要です。

次のような長期使用のために暗号化キーを保存できます。

$storeMe = bin2hex($key);

そして、オンデマンドで、次のように取得できます。

$key = hex2bin($storeMe);

I strongly任意の種類のパスワードをキーとして使用する代わりに、ランダムに生成されたキーを長期間使用するために保存することをお勧めします(またはキーを導出します)。

Defuseのライブラリを使用している場合:

「しかし、私は本当にパスワードを使いたい」

それは悪い考えですが、大丈夫、ここでそれを安全に行う方法です。

まず、ランダムキーを生成し、定数に保存します。

/**
 * Replace this with your own salt! 
 * Use bin2hex() then add \x before every 2 hex characters, like so:
 */
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");

余分な作業を追加することに注意してください。この定数をキーとして使用するだけで、多くの苦痛を軽減できます。

次に、PBKDF2(など)を使用して、パスワードで直接暗号化するのではなく、パスワードから適切な暗号化キーを取得します。

/**
 * Get an AES key from a static password and a secret salt
 * 
 * @param string $password Your weak password here
 * @param int $keysize Number of bytes in encryption key
 */
function getKeyFromPassword($password, $keysize = 16)
{
    return hash_pbkdf2(
        'sha256',
        $password,
        MY_PBKDF2_SALT,
        100000, // Number of iterations
        $keysize,
        true
    );
}

16文字のパスワードを使用しないでください。暗号化キーはコミカルに壊れます。

375

パーティーには間に合いませんが、このページに出くわしたときに正しい方法を探していたところ、これがGoogleのトップの検索結果の1つだったので、問題についての見解を共有したいと思います。この記事を書いている時点の最新情報(2017年初め)。 PHP 7.1.0からmcrypt_decryptmcrypt_encryptは廃止予定となっていますので、将来のプルーフコードを作成する場合は openssl_encryptopenssl_decrypt を使用してください

次のようなことができます。

$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

重要 :これは ECBモード を使いますが、安全ではありません。暗号工学の分野でクラッシュコースを踏まずに簡単な解決策が必要な場合は、自分で作成しないでください。単に ライブラリを使用してください と入力してください。

セキュリティの必要性に応じて、他のチッパー方法も使用できます。利用可能なチッパーメソッドを見つけるためには openssl_get_cipher_methods 関数を見てください。

46
Emil Borconi

Laravelフレームワーク用

Laravelフレームワークを使用している場合は、内部関数を使用して暗号化および復号化する方が簡単です。

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

注意:config/app.phpファイルのkeyオプションには、必ず16、24、または32文字のランダムな文字列を設定してください。それ以外の場合、暗号化された値は安全ではありません。

12
Somnath Muluk

歴史的注記: これはPHP 4の時に書かれました。これが、私たちが現在「レガシーコード」と呼んでいるものです。

私は歴史的な目的のためにこの答えを残しました - しかし、いくつかの方法は現在推奨されていません、DES暗号化方法はお勧めできません、など。

私はこのコードを2つの理由で更新していません。1)PHPで暗号化方式を手作業で操作しないこと、および2)このコードはまだ意図した目的にかなっていることです。 PHPで。

10から20行以下のコードで人々を始めることができる、同様に単純化された、 "ダミー用のPHP暗号化"種類のソースを見つけた場合、私はコメントで知らせてください。

それを超えて、初期の時代のPHP4最小主義の暗号化の答えのこの古典的なエピソードを楽しんでください。


理想的にはmcrypt PHPライブラリへのアクセス権を持っている - あるいは得ることができる - それは確かに人気があり、非常に有用なさまざまなタスクとして。これが、さまざまな種類の暗号化といくつかのサンプルコードの実行です。 PHP の暗号化手法

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

いくつかの警告:

1)一方向ハッシュが可能な場合は、決して可逆的、つまり「対称」暗号化を使用しないでください。

2)クレジットカードや社会保障番号のように、データが本当に機密性が高い場合は、やめます。あなたはどんな単純なコードの塊よりも多くを提供する必要がありますが、むしろこの目的のために設計された暗号ライブラリと必要な方法を研究するためのかなりの時間を必要とします。さらに、ソフトウェア暗号は、機密データのセキュリティの10%未満である可能性があります。それは原子力発電所を再配線するようなものです - その作業が危険で困難であり、それが事実であるならばあなたの知識を超えていることを認めてください。金銭的な罰金は計り知れないので、サービスを利用して責任を負うほうがよいでしょう。

3)ここにリストされているようなどんな種類の簡単に実行可能な暗号化もあなたが詮索好きな目から守るかまたは偶然の/意図的な漏洩の場合に露出を制限したい軽度に重要な情報を合理的に保護できます。しかし、Webサーバー上でキーがプレーンテキストでどのように格納されているかを見れば、データを取得できれば復号化キーを取得できます。

それがそうであるように、楽しんでください:)

6
BrianH

あなたが(あなたがすべきである)ライブラリを使用したくない場合は、このようなものを使用してください(PHP 7):

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);
3
Ascon

以下のコードは、特殊文字を含むすべての文字列に対してPHPで動作します

   // Encrypt text --

    $token = "9611222007552";

      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);  
      $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));  
      $crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
    echo    $crypted_token;
    //unset($token, $cipher_method, $enc_key, $enc_iv);

    // Decrypt text  -- 

    list($crypted_token, $enc_iv) = explode("::", $crypted_token);  
      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
      $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
    echo   $token;
    //unset($crypted_token, $cipher_method, $enc_key, $enc_iv);
0
Javed

更新しました

PHP 7対応バージョン。 PHPの-​​ openssl_encrypt 関数を使用します OpenSSLライブラリ

class Openssl_EncryptDecrypt {
    function encrypt ($pure_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
        $hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        return $iv.$hmac.$ciphertext_raw;
    }
    function decrypt ($encrypted_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = substr($encrypted_string, 0, $ivlen);
        $hmac = substr($encrypted_string, $ivlen, $sha2len);
        $ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
        $calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        if(function_exists('hash_equals')) {
            if (hash_equals($hmac, $calcmac)) return $original_plaintext;
        } else {
            if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
        }
    }
    /**
     * (Optional)
     * hash_equals() function polyfilling.
     * PHP 5.6+ timing attack safe comparison
     */
    function hash_equals_custom($knownString, $userString) {
        if (function_exists('mb_strlen')) {
            $kLen = mb_strlen($knownString, '8bit');
            $uLen = mb_strlen($userString, '8bit');
        } else {
            $kLen = strlen($knownString);
            $uLen = strlen($userString);
        }
        if ($kLen !== $uLen) {
            return false;
        }
        $result = 0;
        for ($i = 0; $i < $kLen; $i++) {
            $result |= (ord($knownString[$i]) ^ ord($userString[$i]));
        }
        return 0 === $result;
    }
}

define('ENCRYPTION_KEY', '__^%&Q@$&*!@#$%^&*^__');
$string = "This is the original string!";

$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);
0
夏期劇場