PHPのOpenSSLで暗号化して復号する
OpenSSLを使った暗号化の実装にあたり、ざっと調べました。
案件
「ユーザーごとに一意なIDをパラメータに付けたURLを発行するが、IDは分からないように暗号化したうえで受信側で復号したい」という要件があり、OpenSSLを使用しました。
PHPの暗号化:OpenSSLを選択
PHPにおける暗号化はmcryptがありましたがPHP 7.1.0 で非推奨となり、 PHP 7.2.0 で削除されPECL(拡張モジュール)に移行されました。
この機能は PHP 7.1.0 で 非推奨 となり、 PHP 7.2.0 で削除 されました。
この機能の代替として、これらが使えます。
PHP マニュアル > 関数リファレンス > 暗号 > Mcrypt > はじめに
- Sodium (PHP 7.2.0から利用可能)
- OpenSSL
という訳でOpenSSLを使っていこうと思います。
なお、そもそもの暗号化の概論というか用語については以下が簡潔で分かりやすくて良かった。押さえておくとググりが捗りました。
OpenSSLの実装:openssl_encrypt/openssl_decrypt とパラメータ
// 暗号化
openssl_encrypt( $data, $method, $key, $options, $iv );
// 復号
openssl_decrypt( $data, $method, $key, $options, $iv );
$data
暗号化/複合するデータ。
$method
暗号化メソッド。暗号化の方法もなんやら色々あるのでどれを使うかを指定する。
環境によって使用可能なメソッドが異なる。openssl_get_cipher_methods()
で使用可能なメソッドの一覧を取得できる。
ローカルでは使えたけど本番環境では対応していないとか発生する可能性がある(実際した)ので、事前に確認しておくべき。
どのmethodを使うのがベストか?
$methodは何を使うのが最適かわからないが、AESが標準的っぽく、調べた感じでは AES-256-CBC が無難な人気どころのようである。
AESのビット数とモード
AESにもたくさんの種類があり、AES-ビット数-モード
であらわされる。
ビット数
ビット数は128/192/256 から選択できる。大きいほど暗号強度が強くなる。暗号の長さには影響しない。
モード
モードについては以下の記事が分かりやすかった。
主要モードのうちECB以外ではiv(初期ベクトル)を使用する。
$key
共通鍵。暗号化・復号に共通して使うパスフレーズ。
$options
ここは分かりにくい。以下を参照。
調べた感じだと0
(デフォルト)を使ってる人が多いように感じた。
$iv
初期化ベクトル。暗号を複雑化して平文を推測されにくくする。
暗号化メソッドによって必要な長さが変わるので、openssl_cipher_iv_length()
で暗号 iv の長さを取得する。
暗号化と復号で同じ iv を使用する必要があるので、DBなどに保存するか、共通の値を元に何らかの規則で生成するのがよさそう。
省略すると Warning が出るが、暗号化はやってくれる。
初期化ベクトルに関する解説では以下が一番平易に感じた。
URLのパラメータに渡す
基本的にはPOSTで渡した方が良さそうだが、GETでやる場合は英数字のみに変換する。
暗号化時はbin2hex
、復号時はhex2bin
を用いる。
$enc_string = bin2hex( openssl_encrypt( $data , $method, $key, 0, $iv ) );
$dec_string = openssl_decrypt( hex2bin( $data ), $method, $key, 0, $iv );
なお、hex2bin()
はPHP5.3以下にはない関数なので以下で対応する。
if ( !function_exists( 'hex2bin' ) ) {
function hex2bin( $str ) {
$sbin = "";
$len = strlen( $str );
for ( $i = 0; $i < $len; $i += 2 ) {
$sbin .= pack( "H*", substr( $str, $i, 2 ) );
}
return $sbin;
}
}
※そもそもPHPのバージョンを上げるべきである。