2019年7月1日月曜日

PHPカンファレンス福岡2019のSST社ブースにてPHPクイズ出題を担当しました

PHPカンファレンス福岡2019のセキュアスカイテクノロジー(SST)社ブースにて、PHPクイズの出題を担当しました。以下は、そのパネルのイメージです。SST社は弊社EGセキュアソリューションズ株式会社のパートナー企業で、私はSST社のEラーラニングコンテンツ(PHP編)の監修を担当しています。


問題はすべて2択で、回答は下の写真のように、赤または青のシールをボードに貼り付ける形になっています。回答がわりとバラけていて、出題者的にはいい感じですね。


以下、出題と解答、解説を記載します。

問題1

以下のPHPスクリプトで、クッキーPHPSESSIDの削除として機能するのはどちら?
PHPのバージョンは7.3.6とする。

A) (正解)31人が選択=正答率 83.8%
<?php
setcookie('PHPSESSID');

B) 6人が選択
<?php
header('Set-Cookie: PHPSESSID=');

解説

setcookie関数の第2引数の省略時の値は '' (空文字列)ですので、A)は setcookie('PHPSESSID', ''); としたのと同じはずです。そして、第2引数が空文字列の場合は、実際にはクッキーの値としてdeletedが指定され、expires属性が過去日時(PHP-7.3.6の場合は、Thu, 01-Jan-1970 00:00:01 GMT)になります。すなわち、setcookie関数の第2引数を省略すると、クッキーの削除として機能します。
B) header関数の方は、クッキーの値が空文字列になりますが、クッキーそのものは削除されません。

参考記事: PHPのsetcookie関数で空文字列を設定しようとするとクッキーが削除される

ところが、出題の確認時にmodphpallで確認したところ、古いPHPでは上記の挙動にはならず、以下のレスポンスヘッダが送信されることがわかりました。この動作は、setcookie関数の第2引数を省略した場合のみで、空文字列またはnullを指定した場合はマニュアルどおりの動作となります。
Set-Cookie: PHPSESSID=
正確に言えば、PHP 4.1.0~PHP 5.6.13までが上記挙動になります。すべてのPHP 4.0と PHP 5.6.14以降、PHP 7以降では、マニュアルどおりの動作となります。この変更は、恐らく以下のバグレポートに対応したものと思われます。

Bug #67131 setcookie() conditional for empty values not met

プログラミング言語のエッジケースの出題をする場合は特に、実環境での動作確認と、処理系の想定バージョンの明記が重要だなとあらためて感じました。

問題2

以下のPHPスクリプトはどちらが表示される? PHPのバージョンは7.3.6とする。
<?php
   $mail = "a@b@example.jp";
   var_dump(filter_var($mail, FILTER_SANITIZE_EMAIL));

A) 31人が選択
bool(false)

B) (正解)6人が選択=正答率 16.2%
string(14) "a@b@example.jp"

解説

この問題は「ひっかけ」でして、実際に多くの方がひっかかりました。
マニュアルにあるように、filter_var関数に FILTER_SANITIZE_EMAIL を指定した場合は、英字、数字および !#$%&'*+-=?^_`{|}~@.[] 以外のすべての文字を取り除きます。出題の場合は、除去対象の文字がないので、入力値がそのまま出力されます。
メールアドレスの形式を検証するためには、FILTER_SANITIZE_EMAIL ではなく、FILTER_VALIDATE_EMAIL を使用します。

率直に言って、FILTER_SANITIZE_EMAILのユースケースは思いつきません。要らんでしょ、こんなもの。

問題3(PHP考古学)

PHP-5.2.17にてregister_globals=onの環境で、あらかじめ以下のようにセッション変数が設定されている。
$_SESSION['user'] = 'yamada';
クエリ文字列 user=tanaka
を指定して以下のスクリプトを実行した場合の表示はどちら?
<?php
   session_start();
   echo $user;
A)yamada  (正解) 23人が選択=正答率 62.2%
B)tanaka 14人が選択

解説

register_globals=on の環境では、session_start()を実行時にセッション変数の値がグローバル変数として初期設定されます。したがって、$_SESSION['user'] = 'yamada'; が設定されている場合、$user の初期値は 'yamada' になります。
仮に、このセッション変数がセットされていない場合、クエリ文字列 user=tanaka の方が使われ、$user の初期値は 'tanaka' になります。セッション変数とクエリ文字列(やPOST変数など)の両方に同じパラメータ名がある場合、セッション変数の方が優先されます。その理由は、セッション変数のグローバル変数へのセットは、起動時ではなく、session_start()の実行時に行われるからです。すなわち、同名のパラメータがあった場合は、セッション変数が上書きします。
したがって、register_globals=on の使用は脆弱性の原因になりやすいことはよく知られていますが、セッション変数をグローバル変数の初期値として用いると、特に脆弱性が混入しやすくなります。

例えば、以下のログインチェックのプログラムについて
session_start();
if (! isset($_SESSION['user']) {
  die('ログインしてください');
}
$user = $_SESSION['user'];

これを register_globals=on を想定して「直訳」すると以下のようになります。
session_start();
if (! isset($user)) {
   die('ログインしてください');
}
// $user = $_SESSION['user'];  // これは不要となる
register_globals=on の場合、session_start()を実行した時点で、セッション変数 $_SESSION['user'] の値はグローバル変数 $user にセットされるため、「これは不要となる」とコメントした行は不要となります。便利ですね!
しかし、$_SESSION['user'] がない場合、$user=nullが実行されるわけではないので、クエリ文字列等にuser=tanakaがあった場合、$userには 'tanaka' がセットされます。すなわち、パスワードを知らなくても誰にでもなりすましできます。これは重大な問題ですね。
register_globals=on を用いて、上記の脆弱性を修正するには以下のように書くべきですが…
$user = null;  // register_globals=onによる初期設定を無効化する
session_start();
// $_SESSION['user'] がある場合、$userにその値がセットされる
if (! isset($user)) {
  die('ログインしてください');
}
上記は著しく直感に反するプログラムですし、それゆえに初期化漏れが盛大に発生しそうです。なので、register_globalsがPHP 5.4で削除されたのは、必然のことと言えましょう。


[PR]
徳丸が代表を務めるEGセキュアソリューションズ株式会社では、ウェブサイトを堅牢にするための各種セキュリティサービスを提供しています。

2019年5月30日木曜日

2019年1月から5月に公表されたウェブサイトからのクレジットカード情報漏えい事件まとめ

株式会社ヤマダ電機の運営するECサイトから、最大37,832件のクレジットカード情報が漏洩したと昨日発表されました。ヤマダ電機のように日本を代表する家電量販店のサイトからクレジットカード情報が漏洩したことに私自身驚きました。

弊社が運営する「ヤマダウエブコム・ヤマダモール」への不正アクセスによる個人情報流出に関するお詫びとお知らせ

漏洩した情報は以下のように発表されています。
  • クレジットカード番号
  • 有効期限
  • セキュリティコード
はてなブックマークやtwitterのコメントを見ていると、「セキュリティコードを保存していたのか」という意見が見えますが、おそらくセキュリティコードは保存されていなかったと推測します。
本稿では、この件を含め、本年の現時点までのウェブサイトからのクレジットカード情報漏えい事件についてまとめました。

事件の一覧

下表に本年(2019年)の現時点までに公表されたウェブサイトからのクレジットカード情報漏洩事件をまとめました。サイト名、漏洩期間、漏洩件数(最大)、セキュリティコードの漏洩有無、漏洩の手口(後述)を記載しています。

サイト名漏洩期間漏洩件数セキュリティコード漏洩手口
バニーファミリー横浜ネットショップ2018年6月28日~同年10月25日241件漏洩Type5
オンライン通販サイト(ハセ・プロ)2018年10月1日~2019年1月24日1,311件漏洩Type5
歯学書ドットコム2012年11月11日~2018年12月28日5,689件漏洩不明
本味主義2017年5月22日~2018年10月14日2,926件漏洩Type4?
子供服サーカス2018年10月1日~2019年1月18日2,200件漏洩Type4
エコレオンラインショップ2018年5月16日~2018年12月11日247件漏洩Type4
ななつ星 Gallery2013年10月5日(サイト開設日)~
2019年3月11日(サイト閉鎖日)
3,086件漏洩不明
「ショコラ ベルアメール」オンラインショップ2018年8月6日~2019年1月21日1,045件漏洩Type5
エーデルワイン オンラインショップ2015年7月8日~2018年8月5日1,140件漏洩不明
小田垣商店オンラインショップ2018年4月3日~2018年5月16日及び
2018年9月3日~2019年2月28日
2,415件漏洩不明
藤い屋オンラインショップ2018年10月15日~2019年1月28日477件Type5
エンターテインメントホビーショップ ジャングル2017年5月2日~2018年11月6日2,507件漏洩Type2
ヤマダウエブコム・ヤマダモール2019年3月18日~2019年4月26日37,832件漏洩Type4

大半の事件でセキュリティコードが漏洩している

上表からわかるように、藤い屋オンラインショップを除いたすべての事件でセキュリティコードが漏洩しています。昨年のまとめも見ていただくとわかりますが、最近のウェブサイトからのクレジットカード情報漏洩では、セキュリティコードが漏洩する方がむしろ普通です。
昨年10月のブログ記事「クレジットカード情報盗み出しの手口をまとめた」では、クレジットカード情報を窃取する手口をType1~Type5にまとめましたが、この中で、Type4とType5はセキュリティコードを容易に盗むことができます。

Type4の手口とは

ここでType4の手口について説明します。下図のように、カード情報入力画面に細工を施すことがType4の特徴です。
下図はサイト利用者がクレジットカード情報を入力している様子です。入力フォームに仕掛けられたJavaScriptにより、カード情報は攻撃者の管理するサーバーに送信されています。
ヤマダ電機のサイトからの情報漏えいがこのパターンと推測する理由は、リリースの以下の記述からです。
(1)原因
第三者によって「ヤマダウエブコム・ヤマダモール」に不正アクセスされ、ペイメントアプリケーションの改ざんが行われたため
弊社が運営する「ヤマダウエブコム・ヤマダモール」への不正アクセスによる個人情報流出に関するお詫びとお知らせより引用
そして、同じくType4と推測されるサイトとしては下記があります。
Q.子供服サーカス、および子供服ミリバールからクレジットカード情報が流出したのですか?
弊社ではカード情報を保有しておりません。今回は、注文情報入力画面 が 不正アクセスにより 改竄され、カード入力画面で入力されたお客様のカード情報が、流出したと思われます。
Q&A】クレジットカード情報流出に関するご質問と回答(株式会社サーカス)より引用
2018 年5 月16 日に攻撃者が、データベースへ不正な仕掛けをページ内に埋め込んだとみられます。
その仕掛けは、ページ内の入力フォームに入力されたカード会員情報を正規の処理とは別に外部サイトへ転送する機能を持っていた為、2018年5月16日以降当該サイトのカード利用者のカード会員情報が搾取されていたと考えられる、との事でした。
エコレ通販サイトにおける不正アクセスによるお客さま情報の流出懸念に関するお知らせ より引用

Type5の手口も依然活発

一方、昨年から使われた始めたType5も依然として活発です。下図は、バニーファミリー横浜のFAQからの引用です。


【重要】カード情報流出についての、ご質問とご回答  バニーファミリー横浜 公式オンラインショップ より引用

そして、下図が攻撃後の画面遷移で、「偽のカード入力画面」が画面遷移に挿入されています。


【重要】カード情報流出についての、ご質問とご回答  バニーファミリー横浜 公式オンラインショップ より引用

この遷移は、「2018年に公表されたウェブサイトからのクレジットカード情報漏えい事件まとめ」で紹介した伊織の事例と酷似しており、偽画面のドメイン名まで同じです。同一犯人、あるいは同一犯行グループではないでしょうか。

また、以下のように、ハセ・プロ、ショコラ ベルアメール、藤い屋についてもこの手口であることがリリースからわかります。

フィッシングサイトによるクレジットカード情報不正取得についてのお詫びと注意喚起のお知らせ (株式会社ハセ・プロ)より引用

「ショコラ ベルアメール」のオンラインショップへの不正アクセスに関するご質問と回答 より引用
2018年10月15日から2019年1月28日までの期間においてシステムが改ざんされた痕跡があり、 クレジットカード決済を選択されたお客様が偽の決済画面へ誘導され、そこで入力されたクレジットカード情報が流出し、 一部のお客様のクレジットカード情報が不正利用された可能性があることを確認いたしました。
弊社が運営する「藤い屋オンラインショップ」への不正アクセスによる個人情報流出に関するお詫びとお知らせより引用

なぜ入力フォームからクレジットカード情報を盗むのか

ブログ記事「ECサイトからクレジットカード情報を盗み出す新たな手口」にて紹介したように、昨年の6月1日に改正割賦販売法が施行され、クレジットカード情報を扱うECサイト事業者にもカード情報保護が求められるようになりました。そして、この保護についてのガイドラインになるのが、『クレジット取引セキュリティ協議会の「実行計画」』であり、中身はクレジットカード情報番号の非保持化が柱になっています。
具体的には、以下に示す「JavaScript(トークン)型」と「リダイレクト(リンク)型」による決済システムが推進されています。
以下は、JavaScript型の決済の様子です。入力フォームは加盟店(ECサイト)側にありますが、カード情報はECサイトのサーバーを通過せず、JavaScriptにより直接決済代行事業者にカード情報を送信する方式です。

実行計画2019 より引用

JavaScript型の場合、先に説明したType4およびType5でカード情報を盗むことができます。
一方下図はリダイレクト型決済の概念図です。カード情報を入力する際には、決済代行事業者の画面に遷移するため、ECサイト側では一切生のカード情報を扱わず、安全性が高い方法と考えられていました。

実行計画2019 より引用

しかし、前記Type5の攻撃ではリダイレクト型決済でもカード情報を盗むことができます。今年のカード情報窃取事件ではType4とType5が使い分けられていますが、これは決済方式に合わせて適した方法が採用されたものと考えられます。

カード情報非保持化で満足せず基本的な対策を

以上で説明してきたように、経産省およびクレジット取引セキュリティ協議会の進める「カード情報非保持化」では十分にカード情報を保護できる状況ではありません。基本に立ち返って、以下の対策を推奨します(2018年末の記事の再掲)。
  • ウェブアプリケーションやソフトウェアライブラリ、プラットフォームの脆弱性対策(パッチ適用など)
  • 管理者等のパスワードを強固にする(可能ならばインターネットからは管理者ログインできないよう設定する)
加えて、以下の対策を推奨します。
  • Web Application Firewall(WAF)の導入
  • ファイルパーミッションとファイルオーナーの適切な設定
  • 改ざん検知システムの導入

[PR]
徳丸が代表を務めるEGセキュアソリューションズ株式会社では、ECサイトを堅牢にするための各種セキュリティサービスを提供しています。


2019年5月7日火曜日

[書評]噂の学園一美少女な先輩がモブの俺に惚れてるって、これなんのバグですか?

瓜生聖(うりゅうせい)の近著「噂の学園一美少女な先輩がモブの俺に惚れてるって、これなんのバグですか? 」を読んだので紹介したい。本書は、JNSA(特定非営利活動法人 日本ネットワークセキュリティ協会)が主催したサイバーセキュリティ小説コンテストにて大賞を受賞した作品「目つきの悪い女が眼鏡をかけたら美少女だった件」を大幅に加筆したのち、角川スニーカー文庫から出版された。

重要事項説明

  • 著者と評者は知人関係にあり公私共に交流がある
  • 評者が読んだ書籍はご恵贈いただいたものである
  • この記事のリンクにはアフィリエイトが含まれる

はじめに

著者の瓜生聖は、twitterのプロフィールには「ITmediaで記事を書いてる兼業ライター」とあるが、本業はITエンジニアである。つまり、現役のITエンジニア兼テクニカルライターである人物が、サイバーセキュリティ小説を書いたのが本書ということになる。瓜生聖はライターとしても一流なので技術面と文章力は申し分ないはずだが、小説となるとどうだろうか。評者の興味は、まずそこにあった。

あらすじ

本書は以下の三部構成となっている。

第一話 試験問題漏洩事件

本書の準ヒロイン格で、主人公鷹野祐(たかのたすく)の幼なじみの西村理乃(にしむらりの)が、数学の試験問題漏洩事件の犯人だという噂が流れ、事件が提示される。祐は、美少女衣川マト(ころもがわまと)の力を借りて事件の真相を解明し、理乃を救う。祐は小学生時代に探偵を志していたが、とある事件がトラウマになり、探偵の夢をあきらめていたが、この事件をきっかけに、自分が進むべき道がおぼろげながら見えてくる。

第二話 夏の嵐

祐は区民プールで子供たちと遊ぶマトを見かけ、そこからの流れでマトが幼少期を過ごした施設を訪問、マトの過去を知ることになる。その後マトの自宅を訪問しドキドキの一時を過ごす。
台風の日、買い物を言いつけられた祐は自宅に帰ることができなくなり、愛帝学園を訪れる。そこで第二の事件を解決する。過程で、サイバーセキュリティに詳しい青年鴻上(こうがみ)が登場する。

第三話 最強の武器

マトが高校に来なくなってしまった。鴻上がマトをスカウトして米国に呼んだらしい。マトが遠くに行ってしまう。しかも、米国でのマトの仕事は…
マトを救うべく、祐は第三の事件を三人の少女の力を借りて解決する。そして、はたして祐とマトは結ばれるのか。

重層的な構造

本書はラブコメラノベという分類になっているが、その実複雑な構成になっている。評者の見るところ、以下の要素を併せ持っている。
  • ラブコメ
  • サイバーセキュリティ小説
  • 成長譚(さまざまな事件を少女たちの力を借りて解決する過程で主人公は成長する)
  • 自分探し(無目的に生きてきた主人公が自分のやりたいことを見出す)
  • 探偵小説(第一話は探偵小説でもある)
  • 家庭内暴力や児童ポルノ問題
まだ他にもあるかもしれない。
これだけの要素を文庫本300ページに突っ込むとごちゃごちゃしてしまいそうだが、どうか。率直なところ、児童ポルノ問題を扱い探偵小説仕立てにもなっている第一話は、話が少し重くなり、また、筋書きを追うのに苦労する箇所がある。著者の初めての小説ということで張り切ってネタをぶち込みすぎたのかもしれない。その反省からか、第二話と第三話では、材料を少し減らして、著者はすっきりとまとめてみせる。全体としては、重層的な構造にも関わらずラノベらしくすらすら楽しく読むことができる。

エピソード紹介

次に、本書の冒頭からいくつかのエピソードを引用で紹介しよう。

物語は、本書の主人公である愛帝学園の1年生の鷹野祐(たかのたすく)が、ヒロイン衣川マト(ころまがわまと)と邂逅するところから始まる。
 教室に入ろうとした瞬間、突然引き戸が勢いよく開いた。
 突然のことに足が絡まり、思わず尻餅をつく。
「いてて……す、すいません」
 見上げた先にいたのは妖精だつた。
 陽の光をまとい、細く艷やかな銀髪がふわりと舞う。シルバーアッシュの柳眉の下はぱっちりとした大きな翠眼(すいがん)で、真っ白な肌は白磁のようにつるつるだ。
 学年の違う俺でも知っている銀髪の美少女ーー衣川先輩だった。
 遠くから見るのとは全然違う美の暴力をまともに受け、俺はただただ、呆然と見つめるだけだった。目を離すこともできない。もしかしたら呼吸も忘れていたかもしれない。
この後、いったんは「氷のように冷たい目で俺を見下ろしていた」マトは、あるきっかけの後主人公を抱きしめるに至る。その後マトは顔を赤らめて走り去るが、主人公は小さなUSBメモリが落ちていることに気づく。

その後、準ヒロイン格でアイドルを目指している西村理乃(にしむらりの)や、主人公が入り浸っているコンピュータ部(主人公は部員ではない)の部員である嵯峨野亜弥(さがのあや)が次々に登場し、わずか10ページほどの間で主要人物が出揃うことになる。スピーディな展開で手際が良い。

主人公は亜弥にUSBメモリの調査を依頼する。亜弥はアーミーナイフを取り出し、慣れた手つきでガワを開くと、USBメモリのチップがPhison2307であり、ファームウェア改造ツールPsychsonにより改造が可能であると指摘する。
「ちょ、ちょっと待ってくれ。つまりその、ファームウェア? ってのを書き換えるとどうなるんだ?」
「デバイスクラスを偽装できるー」
「俺にもわかるように言ってくれ」
「USBデバイスの挙動をエミュレートできるー」
「俺にもわかるように言ってくれ」
 亜弥は話し足りなそうにしながらも、言葉を選ぶように言った。
「PCを乗っ取れるー」
「まじか」
BadUSBが早くも登場である。このやり取りでもわかるように、主人公はコンピュータには詳しくない。本書は主人公の一人称視点で書かれているので、難しい用語が交わる技術的な説明について、主人公に「わからない、もっと易しく説明してくれ」と言わせることによって、わかりやすく解説したり、詳細を省略して要点のみを説明できることになる。これは、著者の工夫だろう。

この後、なぜか主人公と連れ歩いているマトと、アイドルを目指す理乃が、主人公を巡って張り合う場面がある。そう、なんせラブコメなので主人公はモテモテなのだ。理乃は言う。
「好みは人それぞれだと思いますけどね、衣川先輩。幼なじみでずっと昔から知っていて、お互いに祐、理乃と呼び合う関係のあたしから言わせてもらえれば、今の祐はつまんないやつですよ。普通の人に分かる良さなんて、皆無ですからね」
幼なじみの理乃は、祐は昔と違ってつまらないやつになってしまったと言う。祐は、小学生の頃まではシャーロック・ホームズにあこがれ、将来を探偵になることを目指していたが、ある事件に関わったことがきっかけとなり、探偵を志すことをやめ、できるだけ目立たないように過ごしている。

あらすじで紹介したように、この物語は、愛帝学園に起こるさまざまな事件を主人公が周囲の力を解決することにより成長する、いわばロールプレイングゲーム(成長譚)の形態をとっている。主人公は、過去のトラウマから探偵になる夢を放棄してしまったが、友人のトラブルを解決すべく再び探偵として行動する。その過程で、マトからOSINT(オシント)という方法論を教えてもらい、これが自分の進むべき道だと気づく。

問題を抱えているのは主人公だけではない。マトと理乃は、家族に関するトラブルを抱えていたが、それぞれの方法で過去のトラブルを乗り越えようとしている。それが物語に厚みを加えている。

次に、本書の二大要素であるサイバーセキュリティ小説として、およびラブコメラノベとしての側面を紹介する。

サイバーセキュリティ小説として

本書の「事件」に登場するセキリティ要素を紹介しよう。

第一話: BadUSB、MaaS(Malware-as-a-Service)
第二話: ウェブサイト侵入(クレジットカード情報非保持サイト?)
第三話: 暗号通貨

上記のように、イマドキのセキュリティ要素がふんだんに盛り込まれている。しかも、現役のITエンジニアが書いただけあって、セキュリティ部分のクォリティは非常に高い。先に引用したBadUSBもそうだが、ここでは、評者が思わずニヤリとした箇所を引用しよう。以下は、第二話中に出てくる、侵入されたウェブサイトを巡る会話である。
「なるほど…じゃあコレはニセモノってことなんですか?」
 僕は入力途中になっていた通販サイトのページを示す。アドレスバーには緑色で企業名まで表示されている。これがニセモノならどうやって見分ければいいんだろうか。
「ああ、フィッシングという言い方は正確じゃないか。改ざん、と言った方が正しいのかな。通常だと通信が保護されていなかったり、URLが微妙に違ったりすることが多いけど、これは本物のサーバーに何かあったときの予備サーバだから、それ自体は本物よ。ただ、ファイルが書き換えられているだけ」
これはクレジットカード情報窃取の最新の手口である。評者が以前ブログ記事「クレジットカード情報盗み出しの手口をまとめた」に書いたタイプ4かタイプ5に相当する。おそらく「フィッシング」という言葉があることから、タイプ5、すなわち最新の手口であろう。
このように、著者は細かいクラッキングの手口にまで目配りして、最新の手法が紹介されている。まるで、評者がふだん「これをフィッシングと言うな」と言っているのを意識しているかのような会話だw
細かいようだが、セキュリティはディテールが重要であるし、あまりに荒唐無稽な手口だと(荒唐無稽さを突っ込んで楽しむなら別だが)読者は白けてしまう。その点、本書は、セキュリティに精通した読者でも、存分に楽しむことができる。
また、本書のヒロイン衣川マトは、実在の高名なバグハンターがモデルになっている。評者は予めそれを知っていたが、それを知らない読者でも、途中で実在のモデルを暗示する重要なエピソードが出てくるので、セキュリティに関心のある読者なら「ははーん」と分かるだろう。ここは引用したいところではあるが、ネタバレになるので控えておく。

ラブコメとして

ラブコメラノベなのだから、主人公が「冴えないやつ」であるにも関わらずがモテモテなのは、いくら非現実的でも大目に見てあげるのが大人のたしなみというものだ…そんなふうに考えていた時期が私にもありました。
実際冒頭のくだりでは、「おいおい、それはないだろう」と思いながら読んでいたのであるが、読み進めていくうちに、主人公のモテモテぶりが不自然ではなくなってくる。当初は、「主人公がモテモテなのは在原業平や光源氏以来の我が国の伝統だからな」と思っていたが、そうではなかった。鷹野祐は冴えないように見えて実は魅力的な少年なのだ。そのことを、周囲も、当人も、そして読者も気づいていないが、三人の少女は気づいていた。それは、先に引用した「普通の人に分かる良さなんて、皆無ですからね」という理乃の言葉からも伺える。読者は、物語の進展とともに主人公の魅力がわかってくるわけで、それで「モテモテぶりが自然に思えてくる」のだろう。
評者は、主人公の艶福ぶりをうらやんだり、時には「おいおい、昔から『据え膳食わぬは男の恥』と言ってな…なぜ据え膳を食わぬ」と主人公をけしかたり、さらには、「うーん、この調子でいくと祐はマトとくっつくのではなく、理乃とくっついてしまう、なんてどんでん返しもなくないか?」などと要らぬ心配をしたりした。つまり、評者はラブコメとしての本書も存分に堪能した。

おわりに

瓜生聖のサイバーセキュリティ小説「噂の学園一美少女な先輩がモブの俺に惚れてるって、これなんのバグですか? 」を紹介した。本書は、ラブコメ仕立てのサイバーセキュリティ小説であるのみならず、重層的な構造を持つ意欲的な内容であり、それでいて、ラノベとしてすらすらと読める一流のエンタテインメントである。
著者は「あとがき」の末尾で「本書を読んだラノベ好きの方が『サイバーセキュリティって面白そう』と思っていだたければ、サイバーセキュリティ関係者が『ラノベもなかなか面白いな』と思っていただければ著者冥利につきます」と記している。本書は評者が初めて読んだラノベだが、本当に面白かった。可能ならば、本書の続編で、祐とマトが恋人らしくいちゃついているシーンも読んでみたい。


2019年4月29日月曜日

鈴木常彦先生の「共用レンタルサーバにおけるメールの窃盗」の話を聴講した

4月23日(火)に開催された 「#ssmjp 2019/04 ~DNSの話を聞く会~」に「Outputなら任せてください枠」で参加しましたので、講演内容からとくにやばい(?)内容と思われる@tss_ontap(鈴木常彦=浸透言うな先生)の「黒塗りの DNS (萎縮編)」から、「共用レンタルサーバにおけるメールの窃盗」について紹介します。スライドは公開されています

サマリ

レンタルサーバーからメールを送信する場合、悪意の第三者に、特定のドメインに対するメールを横取りされるリスクがある

攻撃手法


  • 攻撃者は、レンタルサーバーを契約(お試しなどでも可能)して、攻撃対象のドメイン名(ここではchukyo-u.ac.jp…中京大学のドメイン名を用いる)を登録する
  • その際に、当該ドメイン名の権利を有している必要はない(権利があれば正当にメールを受信できるので攻撃の必要がない)
  • これだけ

なぜメールが横取りされるか?

一般にメール送信する場合、サーバー内に送信先ドメインがある場合、そちらを優先して配送する。レンタルサーバー内に攻撃対象のドメイン名(ここではchukyo-u.ac.jp)がメール設定されていると、メール配送の際にDNSを参照することなく、無条件に(ローカル内で)当該のメールボックスにメールを配送してしまう。このchukyo-u.ac.jpのメールボックスは、実際には正規のドメイン名登録者ではなく、悪意の第三者が登録したものなので、メールは悪意の第三者が横取りできることになる。

FAQ

Q1: レンタルサーバー契約時にドメイン名の権利は確認されないのか?
A1: 多くの場合権利確認はないそうですし、まぁ難しそうですね

Q2: レンサバ側でDNS権威サーバーやwhoisとか参照しないのか?
A2: 前述の通り、ローカル配送の際にはDNSやwhoisは参照しません

Q3: ドメイン名の権利者ができる対策はあるか?
A3: すべてのレンタルサーバーに契約すれば防げますが現実的ではありせん

Q4: メール送信者(レンタルサーバー利用者)ができる対策はあるか?
A4: メール以外の方法で「メール送ったよ、届いていないときは連絡して!」とメッセージするとか…というのは冗談で、メール送信者側にとれる対策はなさそうです

Q5: DNS関係ないじゃん
A5: はい。DNSとは無関係の問題ですが、近隣のテーマだとは思います

2019年4月17日水曜日

WordPressのプラグインVisual CSS Style Editorに権限昇格の脆弱性

最近WordPressのプラグインのアップデート状況を監視しているのですが、Visual CSS Style Editor(別名Yellow pencil visual theme customizer)が公開停止になり、しばらくたって公開が再開されていました。これは、掲題のように権限昇格の脆弱性があったので公開停止になり、修正版が出たことにより公開再開したものです。WordPressのプラグインではよく見る光景です。

注: 公開停止になったまま再開しないプラグインも珍しくありません。最近では、typesgoogle-maps-buildersimple-share-buttons-adderyuzo-related-post等の人気プラグインが公開停止になりましたが、本稿執筆時点で再開していません。

脆弱性の概要

脆弱性のあるバージョン: 7.1.9以前
脆弱性の種類: 権限昇格
脆弱性による直接の影響: 外部からWordPressの設定をログインなしに変更できる
典型的な攻撃手法: 外部から勝手に管理者ユーザーを登録することで管理者になれる
対策: 最新版(本稿執筆時点では7.2.1)にアップデートする

上記は、以前紹介したWP GDPR Complianceの脆弱性CVE-2018-19207と攻撃手法や影響は似ています。PoC(概念実証コード)は公開されていますが、影響が大きいので紹介はしません。私が逮捕されても困りますしね…

原因

複数の問題が組み合わさって攻撃が可能になっていますが、とくに、yp_remote_get_first()関数内で、特定のGETパラメータを指定すると一時的に管理者になる(厳密にはid=1のユーザになる)機能が実装されていることが原因のようです。この機能の意図はよくわかりませんが、表面上は開発者が仕込んだバックドアのように見えます。以下は7.1.9から7.2.0への差分です。当該箇所は、修正版では単に削除されています。



対策

Visual CSS Style Editorプラグインの7.2.0で改修されています。本稿執筆時点の最新版は7.2.1です。最新版の導入を推奨します。
当脆弱性の攻撃のうち、管理者ユーザーを勝手に作成される経路については、WordPressのログイン機能 wp-login.php のURLを変更し、隠すことで緩和策になります。

宣伝

EGセキュアソリューションズ株式会社では、WordPress利用サイトのセキュリティ強化施策を支援しています。サービス案内はこちら。詳細はお問い合わせください

2019年3月4日月曜日

EC2上でDNS RebindingによるSSRF攻撃可能性を検証した

AWS EC2環境でのDNS Rebindingについて検証したので紹介します。
まずは、「前回までのおさらい」です。先日以下の記事でSSRF攻撃およびSSRF脆弱性について紹介しました。
この記事の中で、以下のように紹介しました。
ホスト名からIPアドレスを求める際にも以下の問題が発生します。
  • DNSサーバーが複数のIPアドレスを返す場合の処理の漏れ
  • IPアドレスの表記の多様性(参考記事)
  • IPアドレスチェックとHTTPリクエストのタイミングの差を悪用した攻撃(TOCTOU脆弱性)
  • リクエスト先のWebサーバーが、攻撃対象サーバーにリダイレクトする
上記のTOCTOU(Time of check to time of use)問題は、DNSの名前解決の文脈ではDNS Rebindingとも呼ばれます。
DNS Rebinding攻撃についてはインフラ側で対策が取られている場合もあります。そこで、EC2上のウェブサイトにて、DNS RebindingによるSSRF攻撃が可能か検証してみました。
攻撃対象のスクリプトを以下に示します。これは先日の記事のスクリプトを改修したもので、クエリ文字列urlで指定したURLのコンテンツを読み出し、表示するものです。IPアドレスのチェックを追加し、表示系を簡略化しています。
<?php
  header('Content-Type: text/plain');
  $url = $_GET['url'];
  $urlinfo = parse_url($url);
  $host = $urlinfo['host'];     // URLからホスト名を取り出し
  $ip = gethostbyname($host);   // 接続先IPアドレスを取得
  echo "Target IP : $ip\n";     // 接続先のIPアドレスを表示
  if ($ip == "169.254.169.254") {
    die("Invalid host $host");  // IPアドレスが169.254.169.254ならエラー
  }
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $_GET['url']);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $body = curl_exec($ch);
  echo $body;
  $prime_ip = curl_getinfo($ch, CURLINFO_PRIMARY_IP);
  echo "\nActual IP : $prime_ip\n";  // 実際のIPアドレスを表示 
このスクリプトは、DNS Rebinding以外にもいろいろ駄目ですが、とりあえず接続先のIPアドレスが169.254.169.254でないことは確認しています。これを以下のIPアドレスで実行すると

http://ホスト名/ssrf.php?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/test-role

以下のようにエラーになります。
Target IP : 169.254.169.254
Invalid host 169.254.169.254

DNS Rebinding

ようやくDNS Rebindingの説明です。攻撃対象が下図のWeb Applicationだとします。図の右側のDNS Contents Server(DNS権威サーバー)は、接続先のドメイン名を管理しているDNSサーバーで、実は攻撃者が管理している想定です(example.jpは例示用のドメイン名です)。


この状態で、先のサンプルスクリプトでurl=http://evil.example.jp/... を指定した場合、通常ケースでは以下のように名前解決されます。

上図のように、gethostbynameとcurl_execの双方で名前解決が発生しますが、DNSキャッシュサーバーのキャッシュが残存していれば、2回目の名前解決の際には、DNSコンテンツサーバーには問い合わせずにDNSキャッシュサーバーがキャッシュの値を返します。
そこで、DNSコンテンツサーバー(権威サーバー)側がTTL=0としてAレコードを返し、Aレコードの要求毎に異なるIPアドレスを返す攻撃がDNS Rebinding攻撃です。その様子を下図に示します。

DNSコンテンツサーバーがTTL=0でAレコードを返すので、DNSキャッシュサーバーは問い合わせがある度にDNSコンテンツサーバーにAレコードを問い合わせます。そして、最初の問い合わせでは無害なIPアドレス(203.0.113.5)を返し、二度目の問い合わせでは169.254.169.254(EC2インスタンスの設定を返すIPアドレス)を返しています。このようにして、IPアドレスのチェックをすり抜ける攻撃手法がDNS Rebinding攻撃です。
上図を見る限りいかにも成功しそうですが、実際にはキャッシュサーバーの仕様などに依存します。DNS Rebinding対策その他を目的として、キャッシュサーバー側でTTLを超えてキャッシュを保持するDNS Pinningを実施している可能性があるからです。

試してみる

簡易的なDNSコンテンツサーバーを書いて、上記攻撃を試してみました。このDNSサーバーは、上図の通り、1回目の応答ではAレコードとして 203.0.113.5 を返し、2回目以降は 169.254.169.254 を返します。その結果、以下のようにSSRF攻撃に成功しました。
Target IP : 203.0.113.5
{
  "Code" : "Success",
  "LastUpdated" : "2019-03-03T08:00:51Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAxxxxxxxxxxxxxxxx",
  "SecretAccessKey" : "zCc1sxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "Token" : "FQoGZXIvYXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
  "Expiration" : "2019-03-03T14:30:31Z"
}
Actual IP : 169.254.169.254 
gethostbynameの時点では、アクセス対象のIPアドレスは 203.0.113.5 (Taget IPとして表示)でしたので、事前のIPアドレスチェックはかいくぐっていることがわかります。
Actual IPは、curlが実際にアクセスしたIPアドレスであり、169.254.169.254となっています。

対策

DNS Rebinding攻撃を前提としない条件でも、接続先のURLについて以下のチェックが必要です。
  • プロトコル(スキーム)が http か https のいずれかである
  • 接続先ホストに紐づくIPアドレスが禁止されたものでない
そして、IPアドレスのチェックがそもそも容易でないことはSSRF徹底入門にも書きました。加えて、DNS Rebinding攻撃の対策もやっかいです。gethostbyname関数の呼び出しに代えてdns_get_record関数等によりDNSクエリを実行して、TTLを確認することは可能ですが、正常系でもTTLが 0 になることはあり得るので、リトライなどの複雑な処理が必要になります。処理が複雑になると、それ自体が脆弱性の原因になるので、ちょっとやりたくない実装です。

先のスクリプトでは、PHPのcurl実装で使用できるCURLINFO_PRIMARY_IPを利用して、「実際に接続したホストのIPアドレス」を表示しています。攻撃によるリスクが情報漏えいのみである場合は、このIPアドレスを検証して、許可されていない場合はエラーにすることで漏洩を回避できます。ただし、これで完全な対策になるかは、徳丸自身は検証していません。

いっそのこと…ということで、外部コンテンツの取得をPROXY経由にするという対策があります。PROXYなら、接続先のホワイトリストやブラックリストによる制限は容易だからです。これだと、DNS Rebinding以外の対策も同時に行えます。

PROXYサーバーを用意するほどでもない…というケースでは、Amazonが推奨しているiptablesによる対策が考えられます。先の記事でも引用しましたが、再掲します。
sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP
Amazon ECS コンテナインスタンスの IAM ロール - Amazon Elastic Container Service より引用
詳しくは、上記ドキュメントを参照ください。

補足

DNS Rebindingの一般的な対策(アクセスされる側)は以下のいずれかを実装することです(両方でもよい)。
  • Hostヘッダの検証(参考
  • 適切な認証とアクセス制御
AWSの 169.254.169.254 は両者のどちらもやっていないことはイケテナイと個人的には思います。仮に Host ヘッダがIPアドレス以外のケースをエラーにしておけば、DNS Rebinding攻撃でここから情報を盗むことはできません。今更変更は難しいかもしれませんが、ここはチェックしていただきたかったですね。

まとめ

DNS RebindingによるSSRF対策回避について説明しました。要約すると、「やってみたらできました」ということです。SSRF脆弱性の対策は難しいので、任意URLを受け取る仕様そのものを見直すか、本稿で紹介したPROXYあるいはiptablesによる対策を推奨します。

2019年2月25日月曜日

bcryptの72文字制限をSHA-512ハッシュで回避する方式の注意点

宅ふぁいる便から平文パスワードが漏洩した件を受けて、あらためてパスワードの安全な保存方法が関心を集めています。現在のパスワード保存のベストプラクティスは、パスワード保存に特化したハッシュ関数(ソルトやストレッチングも用いる)であるbcryptやArgon2などを用いることです。PHPの場合は、PHP5.5以降で使用できるpassword_hash関数が非常に便利ですし、他の言語やアプリケーションフレームワークでも、それぞれ用意されているパスワード保護の機能を使うことはパスワード保護の第一選択肢となります。

なかでもbcryptは、PHPのpassword_hash関数のデフォルトアルゴリズムである他、他の言語でも安全なハッシュ保存機能として広く利用されていますが、パスワードが最大72文字で切り詰められるという実装上の特性があり、その点が気になる人もいるようです(この制限はDoS脆弱性回避が目的です)。
72文字による切り詰めを回避するためのアイデアとして、bcryptに与えるパスワードを前処理としてSHA-512ハッシュを求めてbcryptの入力とする方法を見かける場合があります。疑似コードで書くと以下となります。

$hash = bcrypt(sha512($password));  // パスワードをSHA-512ハッシュを求めた後bcryptで処理する
今までは個人ブログ(これなど)で見かける程度でしたが、弊社のお客様から、Dropboxのパスワード保存方式がこれだと教えていただきました。
以下はDropboxで採用している方式の模式図です(上記ブログ記事より引用)。


この図から分かるように、SHA-512→bcrypt→AES256(暗号化)と3段階の処理が入っていることになります。過剰なまでにパスワード保護が入っている背景には、Dropboxは以前パスワード情報を漏洩している「前科」があるからかもしれません。
Dropboxが採用している方式ということで、自分(自社)でもこの方式を採用しようと思う人がいるかもしれませんが、このSHA-512とbcryptを二重に適用するという方法は重大な落とし穴があるため、筆者としては、素直にbcryptのみを使うことを推奨します。以下、その落とし穴について説明します。

SHA-512→bcrypt方式の詳細検討

SHA-512ハッシュは、その名前の通り512ビットすなわち64バイトのハッシュ値が生成されますが、多くの場合16進数文字列の形で用いられます。その場合は128バイト長となり、bcryptにより72文字に切り詰めされます。これだとかなりの情報が失われますし、そもそも切り詰めが嫌で始めたことなのに、半分近くの情報が失われるのでは何をやっているのかという気分になるでしょう。同様に、base64エンコードしても88バイト長なので、切り詰めが発生することには変わりません。
このような事情から、先に紹介した個人ブログ記事では、SHA-512のバイナリ形式をbcryptにパスワードとして与える実装になっています。バイナリ形式だと64バイトですから、「切り詰め」の対象にはならないはずです。

すなわちPHPの場合、以下のような実装です。
// ハッシュ値の生成
$sha512 = hash('sha512', $password, true);    // true はバイナリの指定
$hash = password_hash($sha512, PASSWORD_DEFAULT);
// パスワードの照合
$sha512 = hash('sha512', $password, true);    // true はバイナリの指定
$result = password_verify($sha512, $hash);

PHPのbcrypt実装はバイナリセーフでない

ここで問題が生じます。PHPのbcrypt実装はオペレーティングシステムのcrypt(3)ライブラリに依存しており、NULLターミネート形式の文字列でパスワードを受け取ります。すなわち、bcryptは72文字の切り詰め問題に加えて、「バイナリセーフでない」という仕様上の制約があります。このため、SHA-512ハッシュ(バイナリ)中にNULLバイトがあると、そこから先を「切り詰め」してしまうという問題があります。最悪ケースとしては、SHA-512ハッシュの先頭がNULLバイトになる場合で、crypt関数にはゼロ文字のパスワードが指定されることになります。
この性質を持つパスワードの例として下記(8文字、99文字)があります。
Aaaaaa3@
A very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong password
以下のように、SHA-512ハッシュの先頭はNULLバイトになります。
$ echo -n Aaaaaa3@ | sha512sum
0011780c00726845802482273be4b2e9329a5d403276b5088fc3e49ca866262632108b32dd2950b680a32eb3808ec9e5710af59c6f6f60f6bbcc9e17098f8685  -

$ echo -n A very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong password | sha512sum
00895faa444575854626475e154dcb8407670af59d9be977a0aa855a49702e45b9c2aad59befe77241bf48f870b08c06bcf80726a6887f41689dc1f0ed977506  -
これらは、SHA-512ハッシュの先頭バイトが共にNULLになるので、これらのハッシュ値によるログインでは、相互にパスワードを入れ替えてもログインできるはずです。試してみましょう。
// 長いパスワードでハッシュ値を求める
$password = 'A very loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong password';
$sha512 = hash('sha512', $password, true);
$hash = password_hash($sha512, PASSWORD_DEFAULT);

// 短いパスワード Aaaaaa3@ で照合する
$password = 'Aaaaaa3@';
$sha512 = hash('sha512', $password, true);
$result = password_verify($sha512, $hash);
var_dump($result);
このスクリプトは true を表示します。すなわち、ログイン成功となります。
ここでは2例のみ示しましたが、SHA-512ハッシュの先頭バイトがNULLとなる確率は1/256ですから、上記方式で実装したログインプログラムでは、任意ユーザーが1/256の確率でパスワード「Aaaaaa3@」で不正ログインできることになります。これはとんでもない大穴ですよね。

ではどうすればよいか

NULLバイトを防ぐ目的であれば、バイナリハッシュ値を255進数に変換し、1~255のバイト値にマップする(base255とでも言いますか)ことで防げます。しかし、そのような複雑な処理を追加することは、バグの原因になり、ひいては脆弱性の要因になります。
なので、bcryptを使う場合は、72文字制限やバイナリセーフでない問題は仕様として受け入れ、そのままで使うことをお勧めします。
先に紹介したDropboxのブログでは、「For ease of elucidation, in the figure and below we omit any mention of binary encoding (base64) (私訳: 説明を簡単にするために、下図および以下の説明では、バイナリエンコーディング(base64)については言及しません)」とあり、base64エンコーディングを採用しているようでもありますが、詳細は不明です。

どうしても、これら制限が受容できない場合は、Argon2などのアルゴリズムを使うことで回避できます。PHPの場合は、以下で実現可能です(PHP 7.2以降)。
$hash = password_hash($sha512, PASSWORD_ARGON2I);   // PHP 7.2以降
$hash = password_hash($sha512, PASSWORD_ARGON2ID);  // PHP 7.3以降
PHPのArgon2実装はcrypt(3)に依存しておらず、72文字制限はなくバイナリセーフであるようです。PHP 7.3以降で使用できるオプションPASSWORD_ARGON2IDは、サイドチャネル攻撃とGPUによるクラッキングの両方の耐性を備えたアルゴリズムです。

まとめ

bcryptとSHA-512ハッシュを組み合わせて使う方式の注意点を説明しました。
私は、拙著「安全なWebアプリケーションの作り方 第2版」で以下のように説明しました。
PHPには、これらの施策をまとめて使いやすくしたpassword_hashという安全で便利な関数があります(PHP5.5.0以降)。極力password_hashを使うことを推奨します。
また、PHPに限らず、パスワード保存機能は独自実装せずに、安全なライブラリやフレームワークの機能を用いることを推奨します。
「独自実装せずに、安全なライブラリやフレームワークの機能を用いる」とは、ライブラリ等を使う際に余計なことをしないということでもあり、本稿で紹介した例は「余計なこと」をやってしまった副作用の例であると考えます。

一般論としても、プログラムは複雑になればなるほどバグが混入しやすくなり、ひいては脆弱性の原因にもなります。プログラムを簡明に保つことは、安全なプログラムを開発する上でも重要だと考えます。

フォロワー

ブログ アーカイブ