PHPという言語は、Webアプリケーションを組む上で安全なのか危険なのか 第01回 17年11月 / 最終更新:2017.11.17

このたび、こちらで「PHPでRASIS四方山話」を書かせていただく事になりました。古庄、と申します。よろしくお願いいたします。
「PHPでRASIS四方山話」では、PHPを「業務で扱うシステム構築言語」として考える上での、発注から受注、設計から作成運用まで、様々な観点の四方山話をしていきたいと思っております。

PHPといえば「Webアプリケーション」で使われる事が多い言語かと思いますが。
Webアプリケーションと言いますと、昨今「セキュリティ」のお話が、兎角賑やかに、かつ定期的に、色々なお話が勃発する領域でもあります。
本稿では、まず「PHPでWebアプリケーションを組む」事を、セキュリティの観点から少し考えてみたいと思います。

まず、前提といたしまして。
例えば「Webサーバのhttpdが古いバージョンでそもそもセキュリティホールがあった」ですとか「WebサーバやDBサーバのOS設定にそもそものミスがあって誰でもtelnetで簡単にログインができた」といった状態は、これは「どの言語でWebアプリケーションを組むか」以前の問題になりますので、一端、お話からは除外いたします。
ただ現実問題としては「知らないから考える事が出来なくて気づかないからクラックされたんだけど侵入された事にも気づいていない」といったケースが、0、ではないので。
インフラ回りについても、適切なスキルや知識をもつ、ないしは「適切なスキルと知識がある運用会社に依頼する」事は、忘れずにやっておきたいところでございます。

話をもとにもどしまして。
PHPという言語について、考えてみましょう。

言語に依存するセキュリティで、比較的「利用側では対処がしにくい」ものの一つが「バッファオーバーフロー」になります。
5~10年ほど前のPHPですと、比較的あちらこちらにバッファオーバーフローのお話が散見されていたのですが。近年は、見る事も滅多になくなってきました。
とはいえ、最も根っこにあたるライブラリの一つであるglibc(標準Cライブラリ実装の一つ)にも、いまだに時々ですが「バッファオーバーフロー脆弱性が」というお話はありますので。
そのあたりには、ある程度の精度のアンテナを張っておきたいところだなぁ、とは思うところです。

もう少し「利用側で意識できる、かもしれない(けど面倒)」あたりに、バイナリセーフ、というお話があります。
端的には。バイナリデータ(もう少し端的かつポピュラーなあたりで、例えば \0)を含む文字列を渡した時に「ちゃんと処理できる関数」と「うまく処理できない関数」がPHPにはあるよ、というお話です。
こちらは5年くらい前までは比較的高頻度で言及をされていたところなのですが。最近の「非推奨ではない」関数であれば、おおむね問題ない状態になっているかと思います。
「古いバージョンのPHPを使っている」場合だと少し気を付けた方がよい可能性もあるのですが、そもそも「古いバージョンのPHPを使っている」場合は「ほかにも色々と不具合や問題がある」ので、先に「PHP自体のバージョンアップ」を検討したほうがよいように思われます。

これ以降は、所謂「一般的なWebセキュリティ」の問題が、色々と出てきます。
曰く「HTMLとして出力する時はエスケープをしないとXSSが発生する」。
曰く「SQL文は、値をエスケープするか或いは静的プリペアドステートメントを使わないとSQL-Injectionが発生する」。
曰く「ページ遷移で手を抜くとCSRFが発生する」。
曰く「パスワードを平文で保存していると、DBにクラックがあった時にゼロタイムで悪用されうる」。
等々。
このあたりはたしかに「とても重要」なポイントではありますが、本質的には「言語に依らず発生しうる、考慮しなければならない」項目になります。

また、ではPHPが「考慮するにしても対応や実装が面倒なのか?」と言いますと。
XSSについては、htmlspecialchars()やhtmlentities()が存在します(余談ですが。日本ではhtmlspecialchars()で言及している事が多いように思われるのですが、海外だとhtmlentities()で言及している事が多いように思われます。興味深い差異です……が、セキュリティ上は、どちらも同じです。内部実装も一か所にまとまってますし)。
SQL-Injectionについては、PDOなどで静的プリペアドステートメントの実装(と、必要であればquote()でのエスケープ)が存在します。
CSRFの実装はまちまちですが、比較的使われる事の多い要素である「CSRFトークン」を作るために必要な「十分な強度のトークンの作成」には、openssl_random_pseudo_bytes()やrandom_bytes()などの「暗号強度を持つ乱数発生装置」が出てきたので、ずいぶんと楽になったように思われます。
パスワードの保存は、password_hash()関数が便利に使えます。

こういったあたりから、「PHPではセキュリティが確保しにくい」のか? と問われると「とりあえず標準(ないし標準に近い)実装があるので、実装に一苦労するような状況ではない」と思われます。
なお、知っている限り、PHP以外の「ポピュラーな」ほかの言語でも、大体似たり寄ったりで「この脆弱性対策のためにはこの実装を使うのが標準で」といったものはあるようです。

したがって「この言語だから安全危険」は基本的にはあんまりないと思われますし、その中にPHPも入るので。
結果として「特段に"PHPだから危険"というほど特別視をする状況ではない」と思われます。

また。一時期は実際に「アンセキュアなサイトが、PHPによって量産されていた」実態はあるので。
その反動でなのか、PHPを使う上での「Webセキュリティ」のお話は色々と盛り上がりをみせているため、最近のPHPはむしろ「セキュアな方向」に傾いてきている傾向があるようにも思われます。

さて「一時期は実際に「アンセキュアなサイトが、PHPによって量産されていた」実態」は、何が問題だったのでしょうか?
そのあたりを、少し検討してみたいと思います。

一つには。
広く悪名高いregister_globals 、公式に「もはやマジッククオートを使う理由はありません」とまで言わしめた、中途半端なSQL-Injection対応としてのMagic Quotes、など。
「実際にPHPの実装や推奨が脆弱だった」時期も、ありました(どちらも、現在は「無くなっている」機能です)。

さらに「PHPは初心者にとってわかりやすい言語である」といったお話や(それ自体はおおむね間違ってないと思います)、そのために書かれた初心者本のうち、昔の書籍では「サンプルコード自体に大量のセキュリティホールが埋め込まれていた」事、など。
「初心者に優しい」という、本来利点であるはずのポイントが「学ばなくてもよい」という勘違いを生みかねない状態になっていたこと、など。

そういった事が「PHPで作られた、脆弱なサイト」を大量に生み出す一因であったように思います。

専門学校の講師なども兼業さていただいている経験から感じるのは。
実際問題、PHPは「プログラム初学者」にとって、学びやすい言語のひとつであろう、と思われます。
ただ、それは「入口のハードルが低い」のであって、そこから先には、他言語と同様に「ある程度の高さと険しさを持つ山」が、しっかりと待っているのです。
そのあたりを「ちゃんと理解できている」か「理解できていない」か、は、大きなポイントなのではなかろうか、と思います。

プログラミングのスキルを向上させるためには、広範囲な知識が必要になりますが。
「Webアプリケーションとしてのセキュアな実装」へのチェックとしては、IPAさんの「安全なウェブサイトの作り方」や、OWASP( Japan)さんの「Webシステム/Webアプリケーションセキュリティ要件書」などで学ぶ事が出来るので。
こういった「知識の入力」は、十分に行っておきたいところです。

一方で。
クラックをする方々は(ある意味)大変に勤勉なので、どうしても「新しい脅威」というのが発見される、という状況は、時々に発生します。
例えばCSRFは「ぼくはまちちゃん」で、2005年頃に「一気に表舞台に躍り出た」感があったように思われます。
例えばパスワードクラックのための「レインボーテーブル」は、昔々は「机上の空論。そんなマシンリソースは確保が難しい」と言われていたものが、年々のコンピュータ技術の進化によって「現実味を帯びてきて実体化した」ように思われます。

こういった事が、今までもありましたし、今後もまず間違いなく発生するので。
「既存の、既知のセキュリティ技術を学んだ」後は、或いは同時並行で「新しい脅威」へのアンテナを、軽めでもよいので、はっておくとよいと思われます。

ただ「いざ情報が引っかかってきたのはよいけれども、プログラムを………どうやって、これ、修正するんだ?」といった事象も、出来事としては、時々、散見されます。
そういったあたりから、プログラム(やもうちょっと広域にシステム設計)では「変更容易性」というものが、重要になってきます。

原稿も少し長くなってきましたので。
変更容易性については、次回、言及をさせていただければと思います。