目次
openssl_decryptで復号したときに発生した問題と対策
ログイン情報を平文で流したくないので、必要な情報をパッケージにして、AESで暗号化した文字列をURLパラメータで渡し、受け取ったプログラムで複合化してから、ログイン認証処理をする・・という仕様です。
暗号化には、AES-128-CBCを使いました。
問題は2つ
PHPで暗号を復号化する処理は、「openssl_decrypt(PHP: openssl_decrypt - Manual
)」を使えば、ごく簡単にかけます。
ただ、今回は他の言語(VBやJAVA)で暗号化した文字列をパラメータで受け取る点がちょっと気になる点ではありました。
なので、念には念をいれて、事前に実際の処理で暗号化したものを受け取って、復号できることを単体テストで確認もしていたわけです。
まあ、当然、開発しているPCでWEBサーバーを立ち上げて、そこでURLを生成して結合テストもやりました。
ここまでは全く問題なかったのですが、本番用のサーバーにデプロイした途端に問題が発生ました。
発生した現象は次の2つです。
① デコードした結果に「00」がパディングされてしまう。
② 特定の変数の時だけ、デコードした結果がemptyになってしまう。
デコードした結果に「00]がバインドされてしまう問題と対策
例えば。
元文字列が「8C89A」、暗号化したものが「XZFupAX4LSDcZzXM6/zR」だとします。
これを受け取って、PHPで復号化します。
printで表示するとちゃんと「8C89A」と表示されます。
ところが、これをDBアクセスのキーにするとDBに存在しているのに不一致になる。
不思議に思ってダンプすると、「38 00 43 00 38 00 39 00 41 00」・・・。
なんと、文字の間に「00」がパディングされてます。
復号の仕方が悪いのかと思うと、復号する部分だけを別のソースに書いて確認するとそうならないわけです。
なったり、ならなかったり・・。
一番、困るパターンでした。
で、しょうがないので、どう対応したかというと。
preg_replace('/\x00/','',openssl_decrypt($encrypted_string, $method, $key, 0, $iv));
と、無理やり00を消すようにしました。
これで問題は発生しなくはなりましたが・・完璧な対処療法ですね。
2018/01/18追記
上記の対応だと、MACアドレスに「0」が含まれていたら正しく復元できないのじゃないか・・との質問をいただきました。
なるほど・・でも、大丈夫です。
文字の「0」はHEXでは「x30」なので消えません。
HEXの「x00」は、恐らく有効な値としてはいってくることはないと思ってます。
デコードした結果がemptyになってしまう問題と対策
これは、もっと不思議な現象です。
復号化処理を「get_decrypted_string」という関数にまとめました。
それで以下のように書きました。
$decrypted_param = get_decrypted_string($encrypted_string);
なんら問題ないはずなんですが。
なんどやっても、$decrypted_paramがemptyになってしまうわけです。
でも、もともと、他のソースで復号化できるかテストしていたコードのままなので、復号ができないわけはないです。
違いは一つだけ。
$dec = get_decrypted_string($enc);
としていた変数を、$decrypted_param に変更しただけ。
それで、まさかと思って、変数名を$decに戻すと、すんなりOKになりました。
これも、未だに気持ち悪いですが、結果オーライみたいになってます。
$decrypted_param という変数が特殊な予約語とか言う情報も特にないし、なんなんでしょうね。
これはいまだに、よくわかりませんが、その後再発もしていません。
ちなみに、本番サーバーのOSはWindowsではありません。
だから・・なんですかね?
でも、こちらからは、許された権限の範囲(デプロイできるだけ・・)しか確認する術がないので、詳しい設定とかを突っ込むことはできてません。
ついでに、他にひっかかったことも書いとこう
これはケアレスミスですが。
テストでうまくいってたのに、リリース環境にうつすとエラーになる。
というのが、他にも発生したのですね。
理由はかんたん。
時刻を比較して規定の時間が経過するとエラーになる処理で、time()で取得した現在時刻が8時間前の時刻になってました。
本番サーバーのPHPのタイムゾーンがデフォルトのままだったそうです。
考えてみれば当然で。
別に日本限定ではないですもんね。
こういう事があるので。
ソース側で、date_default_timezone_set('Asia/Tokyo');を明示的に呼んどかねばいけないなというのを忘れてました。
いろいろありますね。
ではでは。