PHPカンファレンス2015 スペシャルレポート

PHPカンファレンス2015 当日レポート[更新終了]

この記事を読むのに必要な時間:およそ 27 分

10月3日,大田区産業プラザPiOにて,PHPカンファレンス2015が開催されています。本稿では,イベントの模様を随時レポートしていきます。

イベント開始前の最終確認するスタッフの皆さん。この後,受付が開始されました。受付は1Fです。

画像

本日のイベントの模様は,中継されています。

オープニング

実行委員長の前島有貴さんより,オープニングの挨拶がありました。今年は,PHP誕生から20周年ということで,会場を貸し切って2,000以上の参加者を見込むそうです。

画像

その後,いくつか案内がありました。昨年好評だった,スポンサーブースを回るスタンプラリ―があるとのこと。全部回ると,景品がもらえることを紹介しました。

お昼には,数に限りはありますが,お弁当とオムライスの販売もあることや,書籍ブースでは,サイン会が行われることを案内しました。

  • 12:00~12:30:『Laravelエキスパート養成読本』
  • 12:30~13:00『PHPはどのように動くのか』
  • 15:10~15:40『実践ドメイン駆動設計』

なお,懇親会チケットは,書籍ブース近くで販売されるとのことです。

廣川類さん「PHPの今とこれから2015」

オープニングの挨拶の後,廣川類さんの発表が始まりました。本日最初の発表ということで,会場にも緊張感があったように思えます。

画像

今年でPHPは,20週年を迎えます。最初は,PHPの生みの親,Rasmus Lerdorfさんのホビーなツールだったものが,世界中に広まり世界中のサーバで使われまでになりました。今回は,PHPの今とこれからについて,また「7」をテーマに発表が行われました。

PHP7は,来月リリース予定です。PHP7になることで「何がよくなるのか」「何が変わっていくのか?」「どんなことに気をつけなければいけないのか」ということを吸収してもらえればとのことです。

PHPという言語について次のように説明しました。

  • 主にWebアプリケーションで使用されるスクリプト言語
  • 1995年の誕生以来,Webと共に成長,進化

サーバサイドプログラミング言語では8割ほど使われていて,スクリプト言語としては一番のシェアを持っているそうです。CMSシェアでは,WordPressが6割ほど占めていて,言語としてだけでなくアプリでもよく使われる言語だとのこと。

使用されているPHPのバージョン分布を見るとバージョン5.3が最も使われているそうですが,昨年に比べ減少傾向のようです。現時点で,サポートが切れているバージョン5.4以下を使っているユーザは84%になります。いかに,バージョンを上げていくのが難しいか,分かります。ただし,サポート切れだからといって全く使えないというわけではなくパッチを当てることで対応することも可能です。

バージョン5.0がリリースされたのは,2004年なので11年間バージョン5の期間でした。そんな中,バージョン6.0を出す予定だったのですが,Unicodeの全面サポート計画が失敗し,バージョン6.0がキャンセルとなりました。その代わり, 6.0の機能だった「名前空間」「クロージャ」「Trait」などがバージョン5.4にに搭載されました。バージョン5.4からは非常に完成度が高まりました。バージョン5.5で「キャッシュ」,バージョン5.6で「デバッガ」を搭載し性能を向上してきたのが,ここまでの流れです。

なお,PHPリリース情報に「CVE-XXXX」といった項目がある場合には,今お使いのバージョンが該当するかを確認するように注意しました。この項目は,セキュリティに脆弱性があったことを示すものなので,すみやかにパッチを当てる必要があります。

そして,メジャーバジョンアップとなる7.0が,来月リリース予定です。次の機能が搭載されていることを紹介しました。

  • 大幅高速化
  • 致命的エラーを例外補足可能
  • 古いSAPI,エクステンションの削除
  • ヌル合体演算子(??)
  • 結合比較演算子(<=>)
  • 戻り値型宣言
  • スカラー型宣言
  • 匿名クラス

一番大きなポイントは高速化になります。ベンチマークによるとPHP5.6と7.0では,劇的に実行処理時間が違います。ただし,それでもわずかにHHVMののほうが早いです。

画像

それから,スカラー型宣言と戻り型宣言ができるようになりました。型宣言は,今までのPHPの思想に反するのではないかということで,議論が続けられてきたのですが,時代の流れとともに取り入れることになりました。ただし,型宣言はstrictモードを明示的に指定しないと有効にならないため,今までどおりの型宣言無しでも扱えます。

それから,致命的エラーを例外補足可能になります。今まで,致命的エラーを例外処理できませんでしたが,例外機構の見直しが入り例外処理できるようになります。

PHP7の互換性に関する変更として,次のものを挙げました。

  • エクステンション削除:ereg, mysql, msspl
  • SAPI削除:22種類から7種類
  • ASP(<%...%>),Script(<script language="php"></script>)の廃止
  • newオブジェクトの参照代入廃止
  • PHP4形式のコンストラクタ:E_DEPRECATED
  • エクステンションは要変更:http://gophp7.org/gophp7-ext/

今後の開発は,次の事柄にフォーカスされると話しました。

  • PHP7.1開発が開始
  • PCO(PHP Cryptography Objects)
  • JITコンパイラ

PHP7.1に関しては,どんな機能を入れるかという議論が進められています。ここで,コミュニティに参加し機能の提案できます。

PHPの今とこれからが,詰まった素晴らしい発表でした。

画像

徳丸浩さん「今どきのSQLインジェクションの話題総まとめ」

2009年からPHPカンファレンスでトークをしている徳丸浩さんの発表です。SQLインジェクション対策漏れの責任を開発会社に問う判決の話と,PHP入門書のSQLインジェクションの話からO/RマッパやジェネレータのSQLインジェクションについて話しました。

画像

SQLインジェクション対策もれの責任を開発会社に当判決

2009年の資料で安全なアプリケーションの発注要件の話では,脆弱性の責任は発注者か開発者かという問題を取り上げましたが,何もない場合に脆弱性があった場合に米国の弁護士さんと話したところ発注者の責任があると言うことでしたが,判例はありませんでした。

ところが昨年判例が出ました。その判例が家具インテリアECサイト侵入事件です。家具インテリアのECサイトを運営するX社がECサイトをY社に発注してそのサイトから2011年にクレジットカードの情報が漏洩。X社はY社に損害賠償を起こし,2014年1月に判決が言い渡されました。

裁判の争点は次のとおりです。

  • 特にセキュリティに指示はしてなかった。
  • 当初はカード情報を扱っていなかったが,X社の依頼でカード情報を保持するようになった。
  • カード情報は持たないほうがいいとX社が問い合わせた。
  • ログが他社から見れるようになっておりそのログにも個人情報が入っていた。
  • 管理機能のID/PASSがadmin/passwordになっていた。

画像

判断はいろいろ考えられますが,決定的な証拠はなかったけどSQLインジェクション攻撃によるものと断定さました。そして,セキュリティ対策について指示はなかったが,必要なセキュリティ対策を講じる義務を怠った責務不履行があると判断されました。また,損害賠償責任制限があった契約だとは認めるが,SQLインジェクション対策を怠ったことは重過失であると判断されました。その結果,損害賠償責任制限には該当しないとなりました。これらの判断は重要なポイントだと指摘しました。

よって,裁判所は約3000万円の損害賠償を認定しましたが,カード情報を保持しない20万の提案を却下したX社の過失相殺が考慮されて,3割減の2262万の損害賠償が確定されています。

徳丸さんの感想は,脆弱性を放置していたり管理画面のアカウントをECサイトの運営者は毎日使っていたにもかかわらず使っていたのに放置していたため,どっちもどっちだと話していました。ただし裁判所は,開発会社が専門家として当然はたすべき責務を怠っていたことを重視したということが大切です。そのため,発注者が何も言わないのでセキュリティ対策をしないというのは危ないとも述べていました。

こういった判決が出たことで,IPAの提案している「安全なウェブサイトの作り方」は最低限対策したほうがいい。また,「安全なウェブサイトの作り方」が今年3月に改訂され,「クリックジャッキング」が新しい脆弱性として載りました。採用見込みが薄くてもセキュリティ対策の提案は積極的したほうがあとで過失相殺にもちこめるかもしれないと述べていました。

PHP入門書のSQLインジェクション脆弱性の状況

PHP入門書のセキュリティの批判をしていると,入門書だから良いじゃないかと反論があるそうです。しかし,セキュリティのことは後回しでもいいけど,いつやるのか?と考えた場合,PHPを覚えたら忙しくなるので,できるだけ最初からやったほうが良いと話しました。特に「SQLインジェクション」はただのバグなので最初からやったほうが良いとし,入門書も最低限の対策は入れたほうがいいと言及しました。

そして,いくつかの書籍を取り上げ,そこでのセキュリティについて話しました。

* はじめてのPHPプログラミング

2008年に発行された『はじめてのPHPプログラミング』ではMySQLではなくSQLiteを使用しています。SQLiteはバインド機構を持っていないため,自前バインド関数作成しています。

function dbescape($sql, array $params)
{
    foreach ($params as $param) {
          switch (getup($param)) {
               case “inteher”:
               case “double”:
                    $replasemment = $param;
                    break;

               case “string”:
                    // 文字列の場合はエスケープ処理を行う
                    $replasemment = sprintf(“‘%s’”, sqlite_escape_string($param));
                    break;
               default:
                    die(“パラメータの方が正しくありません");
          }

          // SQLを置換し,パラメータを埋め込む
          $sql = substr_replace($sql, $replasemment, strpos($sql, “?”), 1);
     }

     return $sql;
}

しかしこの場合,バインド値に?があると誤動作すると指摘。

SELECT * FROM foo WHERE bar = ? AND base = ?
                                                         ↑’?’に置き換え

SELECT * FROM foo WHERE bar = ’?’ AND base = ?
                                                           ↑ ‘AAA’に置き換え

SELECT * FROM foo WHERE bar = ’‘AAA’’ AND base = ?
               AAAがリテラル外にはみ出す↑

SQLインジェクション攻撃は次のようになると示しました。

SELECT * FROM foo WHERE bar = ? AND base = ?
                                                         ↑’?’ぶ置き換え

SELECT * FROM foo WHERE bar = ’?’ AND base = ?
                                                           ↑ ‘or 1 = 1--’に置き換え

SELECT * FROM foo WHERE bar = ’‘or 1 = 1--’’ AND base = ?
         or 1 = 1--がリテラル外にはみ出す↑

ここから得られる教訓はプレースホルダの仕組みを安易に自作すると徳丸さんに怒られることです。十分に考慮して作りましょう。

* よくわかるPHPの教科書

『よくわかるPHPの教科書』も古い書籍ですが一時期ベストセラーになっていた本です。セキュリティには一定の配慮がされています。この本ではSELECT文ではmysql_real_escape_stringでエスケープ処理を行いsprintfの%dを使っていますが,DELETE文はmysql_real_escape_stringでエスケープ処理を行っているだけです。

ここでSQLインジェクションを実演してDELETE文で意図しない書き込みの削除ができるところを示しました。エスケープは難しいところがあるため,ケースバイケースできちんとやることが大事だと述べていました。

また,SQLインジェクションがあれば個人情報はたいてい漏れてしまいます。エラーメッセージを使った個人情報漏洩のデモを示しました。ここでは,マイナーなextractvalue関数を使いDELETE文からメールアドレを取得していました。

他にも,ブラインドSQLインジェクションというもので1回の呼び出しで1bitの情報が得られそれを積み重ねることで情報が取得できるとのこと。こちらもデモを示しました。

* 気がつけばプロ並みPHP

『気がつけばプロ並みPHP』では,SQL呼び出しはPDOのプレースホルダを使っていてSQLインジェクションについては特に重大な脆弱性はないと話しました。ただ,htmlspecialcharsを使ったものをSQLに使っていたり文字セットを使ってはいけないと言われているSET NAMESを使っていたりしているそうです。

* その他の本はどうなの?

最近はPDOのプレースホルダを使うのが主流になっているため,SQLインジェクションはないみたいですが,細かい問題はあるようです。ここ5年くらいはPHP入門書にSQLインジェクションの脆弱性がないことが当たり前になったと語りました。

また,XSSに関する脆弱性についてはたいてい説明されているが,次のような抜けもあると指摘しました。

  • SQLインジェクション対策はPDOのプレースホルダで静的と動的があり静的を使うほうが安全。
  • 文字エンコーディングの指定はPDOの5.3.6以降で接続文字列に文字エンコーディングが指定できるようになったので,これを使う。SET NAMES outfitはやめる。
  • PDOの癖としてPDO::PRAM_INTを指定しても文字列として扱われるため,int型にキャストしたほうがいい。

O/RマッパやSQLジェネレータのSQLインジェクション

次に,O/RマッパやSQLジェネレータのSQLインジェクションについて取り上げました。PDOのプレースホルダを使うとだいたいSQLインジェクションはおきませんが,O/RマッパやSQLジェネレータではどうなのかについて話がありました。

* Rails SQL Injection Examples

Rails SQL Injection Examplesというサイトで脆弱性について言及されています。Railsの話なので言語はRubyになりますが,PHPでも参考になります。

例題の実演をして,UNION SELECTで別のテーブルの情報が抜き取れるところを示しました。この例題の脆弱性はプレイホルダで対策できるのでプレースホルダを使いましょうということでした。

また,Railsのorderメソッドは次のような式が書けます。

SELECT * FROM 成績 ORDER BY (算数+国語)

複雑なものも書けるそうで,これを応用するとSQLインジェクションができると説明しました。

画像

エラーメッセージから情報を取得できますが,本番環境ではエラーメッセージは出ないという意見のある方のために,ブラインドSQLインジェクションを紹介し,実演で1回1bitの情報からIDとパスワードが漏洩するところを示しました。このように,SQLインジェクションはどこにどんな形であっても致命的になります。

orderメソッドでUNION SELECTから情報漏洩はできるのか。

  • ORDER BYのあとでUNIONは使えない
  • 複文ではmissing attributeとエラーが出る

といった条件を考慮して列名に別名をつけると,IDとパスワードとメールアドレスが表示されることを示しました。この対策はアプリケーション側でバリテーションすることです。

* Zend FrameworkのSQLインジェクション

Zend FrameworkのSQLインジェクションの説明では,Zend_Dbで次のようにクエリを書けると紹介。

$select = $db->select()
                      ->from(products)
                      ->order(’name’); //列 nameでソート

orderメソッドで式を書くとそこで式も指定できます。これはまずいのでは?と思い,どのように識別子と式を区別しているのかと調べたところ,次のように処理されていました。

if (pre_match(‘/\(.*\)/‘, $val)) {   // (と)があれば
     #val = new Zend_Db_Expr($val);    // 式とみなす
}

その時の徳丸さんは,(; ´Д`)という気持ちだったとのこと。

CVE-2014-4914 (Zend Framework 1.12.6以前)は,次のようなになっていました。

  • orderの引数文字列に(と)がありさえすれば式とみなされエスケープ対象となる。
  • 1;攻撃文字列 ?() とかでもOK。
  • 公表されたPoCは次のとおり。
order(‘MDS(1); drop table products --')
↓生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY MD5(1);  drop table products --

Zend Framework 1.12.7での修正されましたが,どのように直ったかと調べたところ,

//1.12.7
if (pre_match(‘/^[\w]*\(.*\)$/‘, $val)) {
     #val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて(があり,末尾に)があれば式とみなす

という形になっていました。その時の徳丸さんは,またも(; ´Д`)という気持ちになったとのこと。

Zend Framework 1.12.7に対する攻撃は,従来のPoCでは次のようになり,

order(‘MDS(1); drop table products --')
↓生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY 'MDS(1);  drop table products ?' ASC
// order by以降が’で囲まれて識別子となる

新しいPoCでは次のようになります。

order(‘MDS(1); drop table products ?)')
↓生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY MDS(1);  drop table products ?) ASC
// 式とみなされる条件を満たし,「そのまま」SQL文に

そして,Zend Framework 1.12.8での修正で,式の判定が次のように修正されました。

//1.12.8
if (pre_match(‘/^[\w]*\([^\]*\)$/‘, $val)) {
     #val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて(があり,途中は)以外が続き,
// 末尾に)があれば式とみなす

なんとなく攻撃は難しくなっているけど,徳丸さんの気持ちは変わらず(; ´Д`)な感じだそうです。仕様が良くないとし,下手に自動で切り替えずメソッド名を変えたほうが良いと話します。対策は「最新の Zend Frameworkを使用すること,かつorderメソッドの引数をバリテーションすること」だと述べていました(Zend Framework 2にはこの問題はありません)。

* JSON SQLインジェクション

SQL::makerというクエリビルダーがあります。プレースホルダを作ってバインド値も作ってくれて値をリストで渡すとIN演算子に勝手にしてくれたり便利なものですが,ハッシュ値に>=を渡してあげると演算子が変更できてしまいます。このハッシュ値にKEYなど意図していないもの渡すと,演算子のところにKEYと入ってしまいSQLとして意図していないものができあがってしまいます。

もし演算子の部分がいじれてしまうと,SQLインジェクションができてしまいます。Perlの最近の流行りでJSONを受け取るAPIの場合に問題があるそうです。

SQL::makerをPHP版に移植した人がいたので,このあたりを調べてみたそうです。

変数の値がyamadaの場合は次のようになります。

SQL文: SELECT * FROM `users` WHERE (`name` = ?)

また,変数の値がarray(KEY’ => ‘yamada')の場合は次のようになります。

SQL文: SELECT * FROM `users` WHERE (`name` KEY ?)

PHPはGET/POSTのパラメータに連想配列が渡せます。$user_name = $GET[user_name];としている場合は,

http://example.jp/query.php?user_name[key]=value

となり,$user_nameにはarray(key’ => ‘value)が入ってしまい,keyの場所に攻撃文字列を入れると簡単にSQLインジェクション攻撃ができてしまうと説明しました。

PerlよりPHPのほうが簡単に攻撃ができてしまうので注意が必要です。対策としてはSQL::maker側はstaticモードを追加して,値としてハッシュや配列を渡せなくすること, アプリケーション側の対応は渡すパラメータにバリテーションを設けることだとしました。

* Drupageddon (CVE-2014-3704)

Drupageddon (CVE-2014-3704) は,JSON SQLインジェクションと類似の問題になります。

Drupalは3大CMSの一つで,WordPress,Joomlaの次にシェアがありますが,Drupageddon(CVE-201403704)はDrupal ver 7.31以前に存在するSQLインジェクションです。Drupal ver 6には存在せず,7系統にあるDrupal coreのSQLジェネレータの脆弱性です。アルマゲドンをもじってドゥルパゲドンと命名されたようです。

この脆弱性は10月15日に発表された直後に攻撃が開始され10月15日午後11時 (日本時間16日午前8時)までにアップデートまたはパッチの適用していない限り,破られたと想定して対処しなければならないとのこと。技術的にもめずらしいものなのだそうです。

通常の要求は次のような形です。

name=admin&pass=xxxxxx

これは,SQL文で次のようになります。

SELECT * FROM users WHERE name = ‘admin’ AND status = 1

このnameを配列で指定しています。

name[]=user1&name[]=user2&pass=xxxxxx
SELECT * FROM users WHERE name = ‘user1’, ‘user2' AND status = 1

この時点で問題があります。JSON SQLインジェクションにもあったように,IN句生成できると便利な呼びだしかたですが,キー名をつけてみる(id1, id2)と,

name[id1]=user1&name[id2]=user2

となり,プレースホルダに名前がついてしまいます。

SELECT * FROM users WHERE name = :name_id1, :name_id2 AND status = 1

そして,キー名前に空白をつけてみると,

name[1 xxxx]==user1&name[2]=user2

となります。プレースホルダに空白が含まれてしまい,xxxxのところは次のSQL文の一部になってしまいます。

SELECT * FROM users WHERE name = :name_1 xxxx, :name_2 AND status = 1

すでにSQLインジェクションは起こっていますが,この状態だとSQL文のエラーになります。エラーにならないようにバインド変数を設定すると,次のような形になります。

name[2 xxxx]=&name[2]=user2
SELECT * FROM users WHERE name = :name_2 xxxx, :name_2 AND status = 1

プレースホルダは:name_2しかないので正しくSQL文として実行できてしまいます。ここまでくると,SQLインジェクションの攻撃ができてしまいます。

このことを脆弱性のあるバージョンで実演しました。実演したのは次のような10秒待つSQLインジェクションです。

name[2 ;SELECT sleep(10) ? ]=&name[2]=user2

さらに,管理者権限を奪うところを示しました。

* 問題点まとめ

一連の問題点をまとめると,次のように言えます。

  • Zend Frameworkは,orderメソッドの仕様が明確でなかった。
  • SQL::Makerは,キーを外部から入力される想定をしていなかった。
  • Drupalでは,配列は想定していたが,連想配列は想定していなかった(7.36で配列を弾くようにバリテーションをするようになった)。

まとめ

最後に徳丸さんは,次のように発表の内容をまとめました。

  • SQLインジェクション対策もれの責任を開発会社に当判決が出た。
  • PHP入門書のSQLインジェクションは解消されつつありますが,まだまだ他の脆弱性がある。
  • SQLジェネレータの実装に起因するSQLジェネレータ脆弱性は仕様の考慮漏れやバリテーション不足。
  • SQLジェネレータ利用者側の注意ライブラリのマニュアルをよく読みバリテーションは普通にやっておこう。

著者プロフィール

大橋勇希(おおはしゆうき)

東京都渋谷区在住。Webアプリケーションエンジニアで,主な使用言語はPHP。最近は,TDD(テスト駆動開発)やDDD(ドメイン駆動設計)に取り組みエッセンスを吸収している。

Twitter:@secret_hamuhamu URLhttp://hamuhamu.hatenablog.jp/


柏原幸治(かしわばらこうじ)

東京都豊島区在住。仕事はブラウザーゲームのバックエンドをPHPで開発している。他の言語もいろいろいじるが,一番長く使ってるPHPが一番使いやすい。

Twitter:@noah0x00
Facebook:https://www.facebook.com/koji.kashiwabara


菅原のびすけ(すがわらのびすけ)

株式会社LIG エンジニア。1989年生まれ。岩手県立大学在籍時にITベンチャー企業の役員を務める。同大学院を卒業後,株式会社LIGにWebエンジニアとして入社し,Web制作に携わる。最近は特にIoT領域,インタラクティブな企画実装などに従事している。

マッシュアップアワードを始めとしたハッカソン等で入賞歴あり。家賃0円クリエイターズシェアハウス第1期生。ジーズアカデミー第1期メンター。LIGinc,HTML5Experts.jp,さくらのナレッジ,gihyo.jpなどでも執筆・寄稿をしている。Milkcocoaエバンジェリスト,特技はわんこそば,趣味は雪合戦。

Twitter:@n0bisuke

LIGincプロフィール:http://liginc.co.jp/member/member_detail?user=nobisuke


中村慎吾(なかむらしんご)

仕事でPHPを使うようになって10数年。楽しい事には何でもやってみるスタイル。趣味が講じて(拗らせて?)会社も作りました。Web開発となるとPHPは手放せません。

Twitter:@n416
URLhttp://kisaragi-system.co.jp


仁多見遼(にたみりょう)

岩手県立大学大学院 ソフトウェア情報学研究科 2年。主にAndroidアプリ開発をしているが,2016年4月から東京でWebエンジニアとして働くことになり,PHPやJavaScriptを書き始めた。いろんなイベントに参加したり,自身でハッカソンを主催するなどイベントが大好きな人。

Twitter:@bird_nitryn
URLhttp://rnita.me

バックナンバー

PHPカンファレンス2015 スペシャルレポート

コメント

コメントの記入