PHPのタスクの1つで追加のCPU命令を使用してパフォーマンスを高速化する

大規模なPHPプロジェクトを構築する場合、多くのユーザーは、強力なサーバー上でもパフォーマンスの低下に直面していました。 ほんのわずかなコードでさえ、全体としてリソース全体に大きな影響を与える可能性があります。利益の観点から、およびこのリソースの維持と維持のコストの観点からです。 1つの問題を解決するための非標準的なアプローチに関する私の経験をお話しします。



年間を通して、新しい機能を絶えず追加しました。より多くのコードを作成し、より多くのモジュールを作成し、モジュールからモジュールを作成し、クロスセレクションに参加した数百万のレコードを持つテーブルを追加しました。 プロジェクトは高速で成長しました。 開発者の構成は複数回変更されており、これは必須ではありませんが、プロジェクトに悪影響を及ぼし、不必要な問題も追加しました。 一般に、大企業の場合のように、かなり大きなプロジェクトです。



すでにすべてが書かれているとき、それは機能し、さらに開発され続け、生産性を向上させるために何かを作り直す時間や予算はありませんが、あなたは前進する必要があり、できるだけ早く次のタスクを取得します。 最初に、私はそれを通常のチケットと見なしました:すべてのユーザーの個人情報:姓、住所、電話番号、識別コード-暗号化された形式でデータベースに保存されるべきであり、解読のためのキーで要求された場合にのみ利用可能であるべきです これはデータ暗号化における私の最初の真剣な経験であるため、PHPツールを使用して問題の可能な解決策をGoogleで検索し始めました。もちろん、よく知られているmcryptライブラリに出会いました。 作業方法を理解するのにそれほど時間はかかりません。 ライブラリは機能しました-フォーラムでは、多くの例、コメント、ディスカッションを見つけることができます。 特に時間がないことを考えると、私の問題を解決するための理想的な選択肢のように思えました。



その結果、mcrypt_encrypt関数の説明ページに直接あるコードを使用しました。

http://us2.php.net/manual/en/function.mcrypt-encrypt.php

<?php

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);

$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

$key = "This is a very secret key";

$text = "Meet me at 11 o'clock behind the monument.";

echo strlen($text) . "\n";

$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);

echo strlen($crypttext) . "\n";

?>









1つの小さなBUTを除き、すべてが正常に機能します。mcrypt_encrypt関数の5番目のパラメーター$ iv(別名IVは初期化ベクトル)は、ECB( 電子コードブック )暗号化モードではまったく使用されないため、不適切です。 そして、私は一般的にこの例がドキュメントに存在する理由を疑問に思っています-それは紛らわしいです。



エンジニアリードがコードレビューを実施し、2つの理由のあるコメントを作成しました。

  1. IVはECBモードでは使用されません(上で書いたように)-これは安全に関する限り、パフォーマンスには影響しません。
  2. mcryptは重すぎて遅いため、ページを読み込むたびに呼び出すことができません。このデータが本当に必要なコードを見つけて、その場合にのみ復号化することをお勧めします。


最初のものは問題ではありません、グーグルはさらに、CBC( 暗号ブロック連鎖 )モードにすぐに出くわします。 しかし、2番目の方法は次のとおりです。結局のところ、ユーザーの名前はサイトのほぼすべてのページで使用されるため、すべてのモジュールを検索する必要があります。 タイミングとリスクを考えると、これは多すぎると思います-結局のところ、QAはまだ通過する必要があります。



ある夜、仕事に関連する日々の問題について議論し、PHPから遠く離れた友人とビールを飲みます。これらの問題は、低レベルのプログラミングとC ++の経験が豊富です。楽しい娯楽であるだけでなく、非常に有用であることがわかりました仕事のため。

彼は私に1つの秘密を明らかにしました(実際、それは私だけの秘密でしたが、もちろんC ++プログラマの世界にとってこれは明らかです):特定のプロセッサ命令を使用すると、計算タスクのパフォーマンスを10倍上げることができますデータ暗号化に関連するタスクを含みます。 新しいインテルプロセッサは、データの暗号化と復号化を高速化するための命令であるAdvanced Encryption Standard(AES)命令セットをすでにサポートしています。 そして幸いなことに、プロジェクトはインテルXeon E5645プロセッサーを搭載したサーバー上で実行されます。これらのプロセッサーには既にこれらの命令があります( AES New Instructions )。



しかし、これらすべてをPHPで使用する方法は?


PHPから値を取得し、プロセッサ機能を使用して暗号化/復号化する独自のPHPモジュールを作成します。 いくつかの眠れぬ夜の後、パフォーマンス結果と一般的な比較-モジュールが何をすべきか、データとベクトルをどこにどのように保存するかの概念(復号化に必要なため)-以下が判明しました。

2つの部分で構成されるPHPモジュール:

  1. Botan( http://botan.randombit.net/ )は、C ++で書かれたオープンライブラリであり、必要なAES256を含む多くの暗号化アルゴリズムを実装し、同時にAES-NIを使用する機能があります。

  2. libaecrypt-すでに一部です-モジュールのメインCファイルから呼び出すことができるCインターフェイス(クラスではなく、関数)へのBotanライブラリのC ++インターフェイスのアダプターとして機能します。


モジュールでは、3つの関数を実装しました。

  1. ランダムキージェネレーター-Nバイトの長さのランダムデータを返します。これは、キーまたはベクターとして使用できます。
  2. 暗号化
  3. 解読


暗号化/復号化-パラメーターとして使用:





アルゴリズムは次のようになります。ランダムなIVが生成され、データキーとIVを使用してデータが暗号化されます。 ベクトルは、ベクトルキーを使用して暗号化されます。 暗号化されたベクトルは、セパレーター#で暗号化されたデータに追加され、データベースに保存されます。復号化は逆の順序で実行されます。



ボタンの主な特徴:


記事にコードリストを掲載するのは合理的ではないと思います。多くのコードリストがあるため、モジュールの初期化について説明します。これはジュースそのものです。

Botanライブラリを使用して、プロセッサとその命令(botan / cpuid.h)を判別できる補助ツールがあります。 暗号化/復号化を高速化するために、プロセッサにAES-NIがあるかどうかが確認されます。 そうでない場合は、SSSE3があります。

int Init()

{

//

pInitObj = new Botan::LibraryInitializer();



//

CPUID::initialize();



//

if(CPUID::has_aes_ni())

global_state().algorithm_factory().set_preferred_provider("AES-256", "aes_isa");

else if(CPUID::has_ssse3())

global_state().algorithm_factory().set_preferred_provider("AES-256", "simd");

else

global_state().algorithm_factory().set_preferred_provider("AES-256", "core");



return 1;

}









その結果、Apache負荷テストツールab(Apache Benchmark)は、モジュールとmcryptを使用した同じアルゴリズムの実装との違いを示しました:約600リクエスト/秒に対して、1400リクエスト/秒がモジュールに有利です。



結論:




2012年3月14日にリリースされたバージョン1.0.1以降のPHPに付属するOpenSSL(すべての苦痛の後)は、AES-NI(およびSSSE3)命令の使用方法と、PHPで記述された同様のアルゴリズムを既に知っています。 c OpenSSL、1秒あたりわずか200リクエストでモジュールに劣る( AES命令セットをサポートするソフトウェア 、バージョン1.0.1のOpenSSLがリストにあります)。

個人的には、将来、MCryptの代わりにOpenSSLを使用します。 mcryptが遅いという事実に加えて、初期化ベクトルとして32バイトのキーが必要です! -OpenSSL、Botan、および私が理解しているように、AES256-CBCモードで暗号化を実装する他の多くのライブラリがIVの16バイトキーを受け入れるため、これは標準ではありません。 mcryptを使用する場合、データを復号化できるのは彼らだけです。



-UPD1:コード例と私のモジュールへのリンクに関して :問題は、プロジェクトのソースコードを公開することを許可しない契約に署名したことです。 これはセキュリティに影響する可能性があります(私たちは約10万人の米国ユーザーについて話している)。 しかし、今日は、契約の条件に違反しないように、表示するためにモジュールの修正版をレイアウトしようとします。



UPD2:私はカルマのシャープなネガティブとネガティブに驚いたので、言いたいです:PHPで作業して暗号化を扱う場合、このライブラリにはパフォーマンスの問題があるため、mcryptは最良の選択ではないことを伝えたいと思います。 phpバンドルにはOpenSSLも付属しています。これは、バージョン1.0.1(上で書いたように)以降、プロセッサ命令を使用し、はるかに高速に動作し、データ暗号化を完全に実行します。 OpenSSLの新しいバージョンのリリース後、私たちの自作モジュールは重要ではなくなりましたが、残念なことに、これはリリース前でした。また、時間が少なすぎることに気付きました。



UPD3:繰り返しますがプロジェクトの25kページビューとパフォーマンスの問題は主要なポイントではないことに注意してください。私の経験からの主要な結論に注目してください。 MCrypt コメントして意見を表明したすべての人のおかげで、AES-NI、OpenSSL vs.にもっと注意を払うために、できるだけ早く記事を書き直そうとします。 MCryptとPHP用のモジュールの作成方法。



All Articles