発端
- PHP8.0以下からPHP8.1以上へバージョンアップした際に
openssl_encrypt
に失敗するようになってしまった - 実際は
openssl_encrypt
がfalse
を返してくるが、特にそれ以上に例外やエラーを直接吐いたりはしない -
openssl_error_string
で失敗の原因を調べるとerror:0308010C:digital envelope routines::unsupported
と返ってくる
test.php
<?php
$enc = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
if ($enc === false) {
echo openssl_error_string(); // => error:0308010C:digital envelope routines::unsupported
}
echo $enc;
- しかし、全く心当たりがない
原因と対策
OpenSSL 3.0 がサポートされるようになりました。 多くの暗号が(レガシーなプロバイダの一部となっているため) デフォルトで有効ではなくなり、 引数の検証 (たとえば鍵の最小サイズ) が厳しくなっています。
- PHP8.1でopenssl 3.0がサポートされるようになった影響
- openssl 3.0でlegacy扱いになった暗号アルゴリズムを利用していた場合に起こりえる問題だった
- 利用している暗号アルゴリズムがサポートされているかどうかは
var_dump(in_array($method, openssl_get_cipher_methods()));
で確認できる
- 利用している暗号アルゴリズムがサポートされているかどうかは
対策A: 暗号アルゴリズムの変更
- 暗号化/復号の際の暗号アルゴリズムの指定をlegacyではないものに変更する
-
aes-256-xxx
など
-
- それまでに暗号化された文字列との互換性は当然なくなるので注意
- ユーザ環境のアプリやブラウザなどに暗号化された文字列を既に保持しているような場合はかなり注意が必要
- 場合によっては、旧暗号アルゴリズムと新暗号アルゴリズムをある程度の期間併存させつつ、徐々に移行させていくような対応が必要になる
対策B: legacyな暗号アルゴリズムをPHP8.1以降でも利用できるようにする
- openssl 3.0でlegacy扱いになった暗号アルゴリズムはデフォルトでは無効になっているものの、設定で有効にできる
- /etc/ssl/openssl.cnfを以下のように変更する
/etc/ssl/openssl.cnf (変更前抜粋)
[provider_sect]
default = default_sect
[default_sect]
activate = 1
/etc/ssl/openssl.cnf (変更後抜粋)
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1
- php環境をdockerイメージで管理している場合、Dockerfileに以下のようなsedを追加すればOK
Dockerfile
RUN sed -ri -e '/^default\s*=\s*default_sect/a legacy = legacy_sect' \
-e '/^\[default_sect\]/a activate = 1' \
-e '/^\[default_sect\]/a \[legacy_sect\]' \
-e '/^\[default_sect\]/a activate = 1' \
/etc/ssl/openssl.cnf
参考