serialize/unserialize vs json_encode/json_decode 速度比較

テストデータとして 金沢市オープンデータ から取得した JSON 形式で 351 KB になるデータを使用した。PHP 5.4.17 (cli) でそれぞれ 1000 回実行したときの実行時間を以下に示す。

エンコード

実行時間 [s]
serialize (array) 2.06 +26%
serialize (stdClass) 2.06 +26%
json_encode (array) 1.58 -
json_encode (stdClass) 1.58 -

配列でも stdClass でも速度は変わらない。

デコード

実行時間 [s]
unserialize (array) 3.84 +3%
unserialize (stdClass) 3.73 -
json_decode (array) 4.44 +19%
json_decode (stdClass) 4.91 +32%

unserialize は stdClass のほうが僅かに速い。いずれも json_decode よりも高速である。

json_decode (array) は第2引数に true を設定して連想配列として返した場合。配列のほうが速い。

エンコードとデコードを同数行う場合

実行時間 [s]
serialize + un〜 (array) 5.90 +2%
serialize + un〜 (stdClass) 5.79 -
json_encode + decode (array) 6.02 +4%
json_encode + decode (stdClass) 6.49 +12%

JSON + stdClass を除いて実行時間はほとんど同じになった。

PHPソースコード難読化・暗号化ツール所感


2014年7月1日追記:この記事は情報が古い可能性があります

PHP ソースコードの難読化・暗号化ツール php-compressor, ibLind, プラセンス SPHP, ionCube PHP Encoder を試してみた。

php-compressor

php-compressor はコード実行に必要のない空白や改行、コメントを削除し、変数名を短縮してくれる。フロントエンドで行う JavaScriptCSS の圧縮と同じ状態のものが得られる。

このプログラム自体も PHP で書かれているので、PHP が動作すれば環境を問わず使える。構文解析PHP の標準関数 token_get_all を使っているところが面白い。

クラスの変数定義を書く場所によってうまく変換できないことがあるのを修正したものがこちら。

ibLind

php-compressor と似たツールとして ibLind という Windows 用のアプリがある。ライセンスは PC 1台につき9800円 (2013年6月10日現在)。体験版ではファイル数 3、ファイルサイズ 10 KB までの制限がある。

設計思想は次のように書かれている。

『こんなものを解読するくらいなら自分で一から同じ仕様のものを作った方が早い』と思わせる程度に難読化できれば良しというコンセプトの元に開発しました

http://apps.ibyte.jp/iblind.html

php-compressor, ibLind と次で紹介する SPHP に関してはこの程度の認識が妥当であろう。

ibLind では出力されるコードはクラス名やメソッド名も短縮されるため、ライブラリ部分だけを難読化することはできない。public メンバ名が短縮されたり、private メンバ名がそのまま残ったり、ロジックが不安定と思われる個所がある。

<?php class _1 { private static $_2 = false; public static function _3($_4, $_5, $_6
= array()) { if (self::$_2) return; if ($_SERVER['REQUEST_METHOD'] !== 'GET') return;
 self::_18($_4, $_5, $_6); } public static function _7($_4, $_5, $_6 = array()) {
 if (self::$_2) return; if ($_SERVER['REQUEST_METHOD'] !== 'POST') return; self::_18($_4,
$_5, $_6); } public static function _8($_4, $_5, $_6 = array()) { if (self::$_2) return;

値段や使い勝手も考えると、今のところ ibLind を選ぶメリットはないだろう。

プランセス SPHP

難読化、使用期限の設定、サーバのドメインによる制限、コード改変の検出など、機能が盛りだくさんの SPHP。

有償で1万2600円の買い切り型。難読化ツールを動かすには Windows 2000/XP/7/8 で Internet Explorer 6 以降と Flash Player 10 以降がインストールされている必要がある。

ライトユーザ向けに作られているためか、多少お節介とも思える画面が特徴である。またディレクトリごとにまとめて処理するなどのバッチ処理は行えないため、今後の改良が望まれる。

このプログラムを使うと次のようなコードが出力される。unSS_sh2b 以前が復号用のコード、それ以降がユーザのコードである。

<?php $fMOz1404_sh=6983;eval(urldecode(str_replace(chr(40),chr(37).chr(54),
str_replace(chr(41),chr(37),"(9(6)28 ... )3b"))));eval(urldecode(str_replace(chr(35),
chr(37).chr(54),str_replace(chr(96),chr(37),"#9#6`28`21#9`73`5f ... `7d"))));
unSS_sh2b("=`6649fe0=======`5eb)90 ... bda"); ?>

一見するとコードが読めないようになっているが、難読化されたコード内でエラーを発生させることで、スタックトレースに元のコードを表示することができる。eval() を使用したツールに共通の問題だ。変数名を短縮しないぶん、php-compressor より読みやすい。

実行時にコードを復号するためコード量が多いと遅くなるかもしれない。また使用期限が設定されている場合、PHP 5.1 以降で mktime() に関する notice が発行される問題がある。

ionCube PHP Encoder

ionCube PHP Encoder は PHP の拡張モジュールを読み込んで、暗号化された独自のバイトコードを実行するタイプの難読化ツールである。

日本の代理店アシアルから購入すると8万4800円(Pro, ダウンロード版)だが、開発元の ionCube から直接購入すると同じものが234米ドルと、約6万円も安い。違いは翻訳されたマニュアルと日本語によるサポートだけで、ソフト自体は英語版しか提供されない。

動作も高速で非常によくできたエンコーダだが、サーバに拡張モジュールをインストールする必要があるため、PHP のコードのみを納品する場合には使えない。

以下の記事も参照されたい。

総括

もし高度な暗号化が必要で、サーバに拡張モジュールをインストールできるなら、ionCube PHP Encoder を選ぼう。

ソースコードを読みたくなくなる程度の難読化でよければ、php-compressor が一番良い選択だ。サーバに何か特別な設定をする必要がなく、しかも無料だ。

セキュリティ上の理由で preg_replace が制限されている環境での代替関数

preg_replace 関数は使い方によっては任意のコードを実行できる脆弱性を生むことがあるため、php.ini の disable_functions によって使用を禁止されている場合がある。

しかしライブラリ中でも preg_replace が使われる機会は多い。そこで次の関数を定義する。(PHP 5 >= 5.3.0)

<?php
function preg_replace2($pattern, $replacement, $subject, $limit = -1, &$count = '__preg_replace2_args_4') {
	if ($count === '__preg_replace2_args_4') {
		unset($count);
	}
	return preg_replace_callback($pattern, function() use($replacement) { return $replacement; }, $subject, $limit, $count);
}

// test
echo preg_replace2('{/usr\b}', '/opt', '/usr/local/bin/php:/usr/local/bin/ruby', -1, $count);
echo '<br>', $count;

ToDo

  • “\1” などによる参照を展開する

cURLでアクセスできるサイトとできないサイト

MacPorts で入れた php5 (5.3.12) と php5-curl でテスト。

接続 ドメイン 接続方式 暗号化方式 ビット数 digest 鍵交換 認証局
OK twitter.com TLS 1.0 RC4_128 128 SHA1 RSA VeriSign Class 3 Extended Validation SSL CA
OK account.edit.yahoo.co.jp TLS 1.0 AES_256_CBC 256 SHA1 RSA Cybertrust Japan Public CA G1
OK admin.shop.pro.jp TLS 1.0 AES_256_CBC 256 SHA1 DHE_RSA Alpha CA
NG mainmenu.rms.rakuten.co.jp TLS 1.0 AES_256_CBC 256 SHA1 RSA VeriSign Trust Network
NG glogin.rms.rakuten.co.jp TLS 1.0 RC4_128 128 SHA1 RSA VeriSign Trust Network
NG card.rakuten.co.jp TLS 1.0 RC4_128 128 MD5 RSA VeriSign Trust Network

cURL曰く、君の言ってること理解できないよ

* About to connect() to glogin.rms.rakuten.co.jp port 443 (#0)
*   Trying 203.190.60.32...
* connected
* Connected to glogin.rms.rakuten.co.jp (203.190.60.32) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: /opt/local/share/curl/curl-ca-bundle.crt
  CApath: none
* Unknown SSL protocol error in connection to glogin.rms.rakuten.co.jp:443 
* Closing connection #0

Windows でも非同期で exec()

<?php
function exec_async($command) {
	if (PHP_OS !== 'WIN32' && PHP_OS !== 'WINNT') {
		exec($command . ' >/dev/null 2>&1 &');
	} else {
		$fp = popen('start "" ' . $command, 'r');
		pclose($fp);
	}
}

解説

  • PHP_OS で WindowsUNIX 系 OS かを判断する(他の処理系については考えていない)
  • UNIX 系 OS の場合、標準出力を /dev/null に捨て、バックグラウンドで実行 (&) することで非同期実行になる(念のため標準エラー出力も捨てている)
  • Windows では popen() でプロセスを作り、start コマンドで実行することで非同期になる
  • start コマンドの第一引数に "〜" で囲われた文字列がある場合、ウィンドウ タイトルとして認識される。そのためここでは空のタイトルを設定してコマンドがタイトルとして認識されることを防いでいる