PHPにおけるAES暗号化アルゴリズムGCM/CBCモードの使用
PHPではopenssl_encrypt、openssl_decryptを用いて対称暗号化アルゴリズムを使用できます。
クセモノなのが、$options引数と、$iv引数です。
ここを誤ると脆弱性を書き込んでしまうので、知識を共有します。
$options引数
公式マニュアルにある通り、以下の定数が使用できます。
OPENSSL_RAW_DATA = 1
暗号化データを生データとして処理します。
なお、指定がない場合は、暗号化データをBASE64として処理します。
$iv引数
初期ベクトルはnonceであることが求められます。
nonceとは一意値(重複しない値)であり、単純にランダム値だけで構成することは誤りです。
また、UNIXタイムでは完全同時アクセスに脆弱であり、UUIDは確率的に衝突はゼロではありません。
PHPにはopenssl_random_pseudo_bytes関数が用意されていますので、これを使いましょう。
ちなみに、インクリメント値もnonceとして妥当ですが、攻撃を受けやすい弱点がありますから注意してください。
実装
実装は次のようになります。
ライセンスはMITライセンスね。
<?php function enc_aes_gcm($plain, string $passwd, bool $base64_mode = true){ $key = hash('sha256', $passwd, true); $options = OPENSSL_ZERO_PADDING; if(! $base64_mode){ $options = $options|OPENSSL_RAW_DATA; } $ivleng = openssl_cipher_iv_length('aes-256-gcm'); $iv = openssl_random_pseudo_bytes($ivleng); $tag = null; $cipherdata = false; try{ $cipherdata = openssl_encrypt( $plain, 'aes-256-gcm', $key, $options, $iv, $tag ); }catch(\Throwable $e){ ; } return ['data'=>$cipherdata, 'iv'=>$iv, 'tag'=>$tag]; } function dec_aes_gcm($cipherdata, string $passwd, string $iv, string $tag, bool $base64_mode = true){ $key = hash('sha256', $passwd, true); $options = OPENSSL_ZERO_PADDING; if(! $base64_mode){ $options = $options|OPENSSL_RAW_DATA; } $plain = false; try{ $plain = openssl_decrypt( $cipherdata, 'aes-256-gcm', $key, $options, $iv, $tag ); }catch(\Throwable $e){ ; } return $plain; } function enc_aes_cbc($plain, string $passwd, bool $base64_mode = true){ $key = hash('sha256', $passwd, true); $options = 0; if(! $base64_mode){ $options = OPENSSL_RAW_DATA; } $ivleng = openssl_cipher_iv_length('aes-256-cbc'); $iv = openssl_random_pseudo_bytes($ivleng); $cipherdata = false; try{ $cipherdata = openssl_encrypt( $plain, 'aes-256-cbc', $key, $options, $iv ); }catch(\Throwable $e){ ; } return ['data'=>$cipherdata, 'iv'=>$iv]; } function dec_aes_cbc($cipherdata, string $passwd, string $iv, bool $base64_mode = true){ $key = hash('sha256', $passwd, true); $options = 0; if(! $base64_mode){ $options = OPENSSL_RAW_DATA; } $plain = false; try{ $plain = openssl_decrypt( $cipherdata, 'aes-256-cbc', $key, $options, $iv ); }catch(\Throwable $e){ ; } return $plain; }
実際に使う時はこんな感じです。
<?php $password = 'aaaaa'; $plaintext = 'テスト'; $enc = enc_aes_gcm($plaintext, $password, false); $dec = dec_aes_gcm($enc['data'], $password, $enc['iv'], $enc['tag']); echo $dec; $enc = enc_aes_cbc($plaintext, $password, false); $dec = dec_aes_cbc($enc['data'], $password, $enc['iv']); echo $dec; exit();