PHP
Apache

PHPの思考盗聴のしくみ〜あるいはApacheとの関係性〜

理由あってLAMP構成のアプリケーションを触るようになって勉強してるんですが、PHPっておもむろにクッキーを読みだしたりできて怖くないですか。

言語処理系に思考(?)盗聴されてないかどうか確かめるために、Apacheサーバー上のPHPアプリケーションがどのように実行されているのかを軽く調べたので、そのメモです。


PHPの実行形式とかの話

Rails生まれUnicorn育ち、rackインターフェスのやつは大体友達。あ、リバースプロキシはnginxでよろしくな!という人生を送ってきたので適当な場所に.phpファイルを置いただけでなにやらアプリケーションが動き出してる現象が理解できず、まずこの辺を調べました。phpのデーモンプロセスとかどこにも見当たらないのに、なんか結果は帰ってくるの心霊現象っぽい。

Apache-PHPの構成には大きく分けてmodule形式とcgi形式というものがあります。主にこの記事が詳しいです。ざっくり区別すると


  • module形式はapacheのphp用のモジュールくんがPHP処理系に.phpファイルを食わせて帰ってきた結果をHTTPレスポンスに加工して返す感じ。

  • cgi形式はphp-cgiというやつに投げてphp cgiくんが.phpファイルを〜以下略

という感じっぽいです。各手法のメリットデメリットはここでは触れません(そもそもcgi形式の中でもいろいろあるらしいので。また気になったときに調べます)。普通に構成してればmodule形式になってるはずなので、じゃあ実際にapacheのモジュールがどうやってphpを実行してるの、という話に進みます。


Apacheがどこで読み込んでるのかという話

多くのケースで/etc/httpd/http.confとかにある設定ファイルをgrepすると、

LoadModule php7_module libexec/apache2/libphp7.so

みたいな行が見つかるかと思います。 なんと今はmod_phpという名前じゃないんですね。ちなみにこのモジュールは

PHPの処理系ビルドの際に生成される
っぽいので、apacheの方のコードを探さないように注意。

さて、loadされたmoduleがどう使われてるかという話ですが… apacheは基本的にリクエストに対してフックが発生し、そのフックについて対応できるモジュールを順に探すみたいなやり方を採用してるらしいです。

Apacheモジュールのフック関数リスト

フックからApacheの全体像を追う

mod_phpモードの原理解析

三つ目のエントリでも解説されてますが、php処理系はこういうフックを新たに定義、登録しています(なんか微妙に名前がちがうけど)。メソッドとしてはこれですね。この中で、特に重要なのが、

ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);

ですね。handlerフックはリクエストごとにその処理をハンドルする候補になるフックで、phpはそのタイミングにこういう場合はphpとして実行してくれみたいな設定をしてるという感じっぽいです(ちゃんと読んでないのであやふや)。

sapiとかいう単語なんやねんという話についてはこのスライドにあるので気になったら読んでください。


で、結局クッキーはどこなの

https://github.com/php/php-src/blob/2dd2dcaf9c8bcdda9c687660da887c5fddeb7448/sapi/apache2handler/sapi_apache2.c#L752

https://github.com/php/php-src/blob/2dd2dcaf9c8bcdda9c687660da887c5fddeb7448/sapi/apache2handler/sapi_apache2.c#L486

https://github.com/php/php-src/blob/2dd2dcaf9c8bcdda9c687660da887c5fddeb7448/sapi/apache2handler/sapi_apache2.c#L400

https://github.com/php/php-src/blob/2dd2dcaf9c8bcdda9c687660da887c5fddeb7448/sapi/apache2handler/sapi_apache2.c#L230

static char *

php_apache_sapi_read_cookies(void)
{
php_struct *ctx = SG(server_context);
const char *http_cookie;

http_cookie = apr_table_get(ctx->r->headers_in, "cookie");

/* The SAPI interface should use 'const char *' */
return (char *) http_cookie;
}

読んでた。PHPは無実であった。


さいごに

やっぱりソースコードリーディングは目的があった方が捗るというのと、動作についての納得は大事ですね。

C言語ガバなので完全に追い切れてるわけではないのがかなしい。おべんきょうしよ。。。

ついき

nginxの場合はこれを見れば良さそうです。