がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

UUIDv4の(多分)一番雑な作り方

色々考えたり調べたり悩んだりしていたのですが。
uuidgenっていうコマンドラインユーティリティあるんですねぇ知りませんでした(苦笑


http://linuxjm.sourceforge.jp/html/e2fsprogs/man1/uuidgen.1.html

uuidgen - UUID 値を生成するコマンドラインユーティリティ


って事で。
PHPの場合ですと

$uuid = `uuidgen -r`;

これで、割合と簡単に作成できるってもんです。


………ど〜しようMagicWeaponのほうで作ろうかと思ったんだけど…これがあるとすると、別に作らなくても問題ないんだよなぁ(苦笑


(主として、コマンドの綴りを)忘れる前に、めも。

管理画面のインジェクションは無問題?

いや、たまたまそーゆー論調をちらりと拝見したので。


結論から書くと「管理画面の脆弱性だから、インジェクションがあっても特に問題が無い」は短視野的にはYesだけど現実的にはNo。
前提として「そのインジェクションによるデータのぶっ込みは、別の画面で、管理画面の機能として普通に提供されている」と仮定。
「提供されていない」ぶっ込みなら、短視野的にもNGなのは当たり前なので。


んと…結局の所管理画面だから。別の箇所で大体「なんでもできる」ので、インジェクションがあろうがなかろうが一緒。
つまり「他の場所で、インジェクションしたときと同じ操作が正規ルートでもできるんだから、別に騒ぎ立てるほどのこっちゃない」ってのは、この文脈においてYes。


ただこれって、物凄く短視野。


管理画面で、とは言っても「脆弱性がある」っていうことは。
その脆弱性の根っこが「管理画面とfrontで共通で使っている」可能性もあるわけだし、プログラムは切り分けてあるとしても、同じスキルの人たちが作っている以上「同じような脆弱性を、front側でも、別途作り込んでいる」可能性だって十分に想起される。


つまり「思考は習慣になり、習慣は行動になる」。


勿論「その脆弱性は意図的にわざと作り込んでます」って言い訳が出てくるかもしれないんだけど。
そも「本当に」わざと脆弱性を作る意味と意図が理解不能だし、そのわざと作った脆弱性を、その次に「ついうっかり」frontで使えば、アウトだし。
で、おそらくは「わざと」ではなくて「ついうっかり」脆弱性を作り込んでいる可能性の方が、当然ながら圧倒的に高いので。


故に。
「管理画面の脆弱性だから、インジェクションがあっても特に問題が無い」は短視野的にはYesだけど現実的にはNo。


この辺は「三流でとどまる」のか「一流を目指す」のか、っていう気概とかの類いの問題、なのかもなぁ、っと思いつつ、memo。

「冗長な言葉で」Webアプリケーションセキュリティの一部を書いてみる

ん…年末の、escapeとかvalidateとかsanitizeとかprepared-statementとかの一連のお話をみて、ちょうど良い機会なので「自分なりに」かみ砕いたものを書いてみようかなぁ、っと。
単純に「自分の中で整理したい」のに糅てて加えて「間違いがあったらきっと突っ込んでもらえるに違いない」という淡いというよりは甘い期待、込みでw
推敲はしていますが、初稿は「夜中の寝不足」で書いているので。ノリその他、微妙におかしいのは気にしない。


基本、所謂injection系とかを想定しています。…XSSってinjection系に入れてもよいのかしらん? って思うのですが、その辺を「いかんモンぶっ込み系」って括ると似たようなモンなんじゃないかなぁ、っと。


大まか、プログラムなんで「入力 → 処理 → 出力」という流れでの説明になります。
ただ、出力先が、コマンドラインだったりmailだったり(mailはコマンドラインなのかTCP/IP通信なのか、ってのは置いといて)、HTMLだったりRDBだったりしますので、その辺も込み込みで。
また、セッションハイジャックとかその辺については言及をしていませんのでご注意のほどを。
だから「Webアプリケーションセキュリティの一部」です。


ちなみに、流れ的には、Webアプリケーション「じゃないプログラム」のセキュリティにも「同じような事が言える部分は多い」ので、そんなつもりでれっつらごー。


まずは入力を受け取ります。


受け取った入力ですが。
多くの場合において「業務的にあずましくない値」は、速やかに突っぱねる事が推奨されています。
具体的には、例えば日付を入力するところで「12月32日」とか入ってくるのは、それが「入力者の強い思いと願いと願望と懇願」であったとしても、受け入れるには少々難のある値になります。
或いは、購入アイテム数をマイナス値にする行為は「一昨日来やがれすっとこどっこい」と突き返す強い心が必要になります*1


というわけで、まず入力値を確認して「業務的に明らかにあずましくない値」が含まれていないかどうかチェックをしましょう。
含まれている場合「お手数ですが、入力値または目またはあんたの脳みそをご確認の上、再度ご依頼くださいませ」と、丁重に突き返す必要があります。


大まか、この「業務的にあずましくない値が含まれているかどうかのチェック」を、validate、なんていう用語で呼称するケースが多いようです。


若干余談。
validateの一つの手法として「予め想定されている値一覧、の中に入っているモノしか認めない」という、大変に狭量なやり口が存在します。
昔は「デフォルト不許可」なんていう言い方で、特にネットワーク系の指定(ルータの指定なんかが顕著ですな)で出てきたのですが、ソフト系だと「ホワイトリスト(許可リスト)」なんて言い方をする事が多いですね。
やり口としては大変に狭量ですが、それだけに「がっちりガードしやすい」特徴があるので、お勧めです。
この真逆にあるのが「デフォルト許可」或いは「ブラックリスト(不許可リスト)」。リストに漏れがあるとてきめんでひどい事になるので、こちらはあまりお勧めしません。


また、このvalidateですが。
値によっては「突っぱねてよいかどうかが難しい」ところも、色々とございます。
年齢に「数字じゃない文字ぶっこんできたら」NG出せますが*2、例えばそれが5とか8とか、或いは100とか110とか122とか入力された場合に「…ん…多分嘘くさいけど…」とは思いつつ、入力者が「絶対にジャンヌ・カルマンさんではない」と否定しきれる根拠も薄いわけで。そこを無碍に突っぱねてしまって「本当によいのか?」というあたりには色々と疑問が出てくるところです。
郵便番号も割合に面倒ですね。「フォーマット的に正しい」と「存在する」との間には、日本海溝くらいには深い溝が存在します。
あとはemailが面倒ですかね。「RFC的にあり得ない」docomoの正式なアドレスとか滅びればいいのに。ついでに「フォーマット的にvalidである」と「到達する」にも溝がありますが、到達するemailアドレスが「ご本人の所有管理下にあるかどうか」ってのもまた、考慮すべきポイントになります*3


閑話休題


まぁ「どこまでを許容するか」或いは「どこからを許容しないか」ってのは、色々と(システム起因以外で)難しい問題もあるので。
この辺は割合「ビジネスに直結するケース」なので、お客様と存分に話し合うのがよろしいかと思われます。
んでまぁ「ビジネス的に明らかに許容できねぇだろヲイ」てな値は、鉄の意志を持って突っぱねてください。


さて。
このタイミングで時々「後々、攻撃に使われやすい文字(列)」を対象に「(攻撃にならないような文字(列)に)置換したり削除したり」といった事を、推奨したりしなかったり、という状況があるようです。
人によって用語の用い方が異なるのですが、「フィルタリング」「サニタイズ」なんて用語を見かける事が多いですかね。
気持ちがわからんではないのですが、個人的には、このタイミングで「「後々、攻撃に使われやすい文字(列)」を対象に「置換したり削除したり」」は、業務要件に変な縛りを入れる事が増えるのを散見しているのもあるので、あんまりお勧めしません。
そも「後々、攻撃に使われやすい文字(列)」の定義が曖昧ですし。出力シーンによって変わるから。


処理はまぁ適宜。


んでもって、出力。
XSSにしても*-Injectionにしても、発生しているのは「データだけを記述すべきところに、データではない別の意味ある言葉が混じる」のが「あずましくないよ」ってのが、一番の根っこ。
この部分を踏まえた上で、対策の話に進みませう。


例えばHTMLで。INPUTエレメントのvalueアトリビュートに、「デフォルトの値」だけを入れたいのに、ちょっときかせちゃいけない気を利かせて「"><script> alert('test');</script><"」なんてな値を入れると。
<input value="値">ってなのが展開されて<input value=""><script> alert('test');</script><"">となってなんか「値だけを入れたかったのに外にはみ出して勝手にHTMLの続きの指示書いちゃってるよをい」てな事になり、あんまりあずましくない。
あぁちなみに<>は本当は半角ですが、色々と面倒なんで全角で書いてます。


例えばSQLで。検索したいので「SELECT * FROM hoge_tbl WHERE id = '検索する値';」なんて感じにしたいところで、検索したい値に「'; UPDATE items SET price=1;--」なんて入れてみると。
お手軽に「SELECT * FROM hoge_tbl WHERE id = ''; UPDATE items SET price=1;--';」となって、例えばitemsが商品のお値段でここがECサイトだったりすると、割と心温まる大売り出しとなるわけでございます*4


前者のケースにおいて"と<>のあたりが「放置すると悪さする子」であり、後者においては'や;が悪い子ちゃんです(実際にはもっと色々あるので注意)。


ちなみに「ちゃんとTCP/IPでしゃべる」SMTP通信で。
mailの本文のうち、偶然「1文字目に.(ドット)、2文字目に改行」がはいると、それ以降のmail本文が「切り捨てられる」という楽しい事象も存在しえます。
…過去に本当に実務でそれに一度遭遇したときは、気づくのに少々時間がかかったものです。


ここで重要であり必要な根っこは「値は"値"として扱われること」。
言い換えると「値が、お外にはみ出さないようにすること」が尤も需要にして肝要です。


さて。
古来から割とよく使われている「お外にはみ出さない」為の方法として「ある特定の文字(列)を、別の文字(列)に置換する」方法がとられます。
これをエスケープ処理とか言いますが…ちょいとこの辺をかみ砕いてみましょう。


まず「エスケープシーケンス」ってもんがあります。
これは「通常では書き表しにくいものを表現するため」の記法です。一応念のために書くと「書けない」訳ではないですバイナリエディタ使えばなんだって書けるしねぇ。
ただまぁ普通そこまでやる人はあんまりいないので、普通にテキストエディタとかで書きやすいようにするために、\nとか\rとか\0とか、っていうのを使って記述するわけですね。
この辺を「エスケープシーケンス」って言います。
「JISのエスケープシーケンス」とかを調べるのも面白いかもしれないけど、ちょいと余談だねぇ。


ちなみに「JISのエスケープシーケンス」と同じような手法を、昔の、J-PHONEとかVodafoneとかの絵文字が、やっていた記憶がある。
んで、なんか特定の条件で「エスケープシーケンスだけが」はずされちゃって、奇妙な文字化けをする…みたいな事象が過去にあったんだよねぇ、たしか。


閑話休題


んで。
上述で書いた「JISのエスケープシーケンス」なんかもそうなのですが、多くの「出力先」には、そこの世界特有の「こいつはただの文字ぢゃねぇ、特別なヤツなんだ!」っていう、依怙贔屓されている文字ってのがあります。
HTMLだと<>が割と典型ですし、SQLだと'や;が典型です。URIですと=や&や?は特別な子ですし、コマンドラインでも'や&や;は特権階級です。CSV(カンマ区切りの表記)だと、カンマと改行がハイカーストな存在ですね。


ただ、ここで問題があります。
HTMLで、<>をただの文字として出力したいこともありましょう。
SQL文としてぶち込むデータの中に'や;を使いたいシーンもございましょう。
URIに付けたいパラメタで&や?を含めたいと駄々をこねられる事もありましょう。
コマンドライン経由で送りたいmailで、何かの理由で&が付けられてしまう事もありましょう。
CSVのデータの中に、文字としてのカンマを使いたいとか願う子羊もおりましょう。


こーゆー状況で尤も手っ取り早いのは「使えません仕様です」で切り捨てる事です。「システム上の制限です」とか言い張るのは、非常に楽で、それ故に今でも「使われる時は使われる」手法ですね。
…実際、CSVで「データにカンマを含める事は出来ません。それは仕様です」と言われて、色々と難儀した記憶がよみがえってきたりします。
ただまぁそうやってあらゆる面倒を「仕様です」で切り捨てると、いつか「あなたは不必要です。それが仕様です」とか言われてしまいそうなので、もうちょっとまじめに真剣に真摯に、その辺は考えていきたいところです。


というわけで「どうにか、特権階級な子を、一般庶民に引きずり下ろす手法」というのが考慮されてくるわけです。
HTMLにおいて、<は<、>は>と書くと、表示的には<だったり>だったりする「庶民のための、表示用文字」を得る事ができます。
SQLにおいて、データとしての'は、''という風に「二つ重ねて」あげると大人しくなります。MySQLだと\'って書き方もありですね。
URLにおいて、=や&や?は、%3Dや%26や%3Fって書くと無問題です。
CSVでは「ダブルクォートの中のカンマ」は、一般庶民である、という風習が根付いております。


というわけで、上述のように「特権階級から引きずり下ろす悲報じゃなくて秘法」があるわけで、これを一般的に「エスケープ処理」なんていう言い方をする事が多いです。
んでもって、この秘法が「値が、お外にはみ出さないようにすること」の実際の手法として様々な盤面でありとあらゆる万能手法として…使われるかと思いきや、必ずしもそうにあらず。


んと。
上述の「特権階級な子を、一般庶民に引きずり下ろす手法」ですが、どれも中途半端です。
ちなみにコマンドラインのほうを言及していないのは「コマンドラインが、shなのかbashなのかzshなのかcshなのかkshなのかtcshなのか」でも変わる上に色々面倒にすぎるので、言及さえ避けておりますこの根性なし < 俺。


もう一つ。各秘法は「その局面においてのみ有効」です。
つまり「対HTML用のエスケープの秘法」はSQLに対しては威力が薄く、「URL特化型エスケープの秘法」は、コマンドラインについては無力にも等しい存在です。
んでもって「あらゆる局面で有効な、万能型のエスケープの秘法」は、存在しません。
ちゃんと出力先を見極めて、あまたの秘法から「ただ一つの、有効な術」を選び出さなければならないのです。


さらに。
各秘法について「用意された、完全なる関数とかクラスとか」があればよいのですが、その「用意された子」ですら「実装上のミス」の可能性があるので。
「値が、お外にはみ出さないようにすること」の実際の手法は、必ずしも「エスケープ処理1択」にはならないしなれないんですね。


現在。
HTMLについては、PHPだとhtmlspecialchars関数によるエスケープがよく用いられています。
コマンドラインは「可能な限り避けろ」という、心温まるお話をよく耳にします。どうしても不可避な場合、escapeshellarg関数を用いるか、或いは可能なら「環境変数と標準入力つかってデータを渡す」といいよねぇ、的なお話がちらほら。
CSVは割と簡単にエスケープ可能なので、自作でみなさんやってらっしゃる気がする。…関数あったら書き直しますんで教えてくださいませ*5


んでもってSQLですが。各APIともにエスケープ関数(メソッド)は用意されているのですが。
昨今では「原理的に安全」な、静的プリペアドステートメントを用いる、というのがおナウなヤングの基本です。
(静的)プリペアドステートメントは、若干動作に「限定的」な所があるのですが。具体的には、テーブル名やカラム名などを「プレースホルダ化する」事が出来ないのですが。
通常、ンなことはしなくても基本的な業務アプリやゲームアプリは一通り作成可能なので、あんまり考慮しなくてもよいと思われます。
あなたがもし、フレームワーク作るとか、phpMyAdminを超える、似たようなサービスを作るとか、そーゆーレベルに達してから「プリペアドだけじゃ要件満たせないんだよなぁ」と悩んで、エスケープ処理用の関数なりメソッドなりに手を出してください。


とまぁ、こんな形で入力された値から「明らかにあずましくないものを、どちらかというとビジネス的制約基準で突っぱね」つつ「出力先の都合にあわせて、適切に丁寧に、檻に閉じ込めたまま値として扱う」ってのが、このあたりの基本になります。


…なんか偉い事脱線しまくってる気もしますが。
なんかあったら、お気軽に突っ込んでくださりませ。

*1:…それをやらないばかりに、課金系のゲーム内通貨を、簡単なパラメタ操作で「増加できる」とかいう愛くるしいバグを、本当に作りやがったカス会社が居たのよマジで orz

*2:普段は16進数表記とかで年齢を呼称する事もありますが、プログラムを組む時は10進法に限定したいなぁ、と考えるのが恐らく一般的です

*3:某○○○○とかいうMLサービス、とっとと滅びればいいのに

*4:SQLの複数文は最近ガードされている的な話もありますが、されていないケースもあるようなので

*5:処理的には「ダブルクォートを二重にした」うえで「全体をダブるクォートで囲む」でよいので、そんなに困らんとです

プリペアドステートメントとエスケープの線引き おいちゃん変

微妙にあちこち賑わっている気がしたので、なんか必要になる前に、おいちゃんの見解を。


まず前提として「適切にもちいられていない」ケースは排除。
具体的には「**をやる、というルールを作っても、守られない/中途半端なら意味がない」ってのはどこにいってもどこまで行っても当たり前のお話なので。
そーゆー基本は「出来ている」前提で。


おいちゃんのスタンスは以下の通り。
・基本はプリペアドステートメントを静的に
SQLの識別子を動的にしたい場合は「ホワイトリスト」を用意して実装(プラスしてエスケープ、はアリだと思う)


ちなみに「通常の業務やゲーム範囲程度までで、phpMyAdmin的なものは作らない」前提。
一応念のため。


んで。
やはり基本は「静的プリペアドステートメント」のほうが、単純に安心。
エスケープ処理は、まぁ九分九厘ないとは思うのですがとはいえ「実装でミスったらアウト」なので、やはり静的プリペアドステートメントのほうが有難い。


性能について、複数の角度から言及を聞いた事があるんだけど…


まず「パケット数が増える」については、増えるんだろうにしても、それは「セキュリティとトレードオフに出来る関係なのだろうか?」っていう疑問が山盛り。
逆に言うと「真剣に考えた上で、トレードオフせざるを得ないほどのアクセス数(と収益のなさ)」があるんなら、考慮してもいいと思う。


「パケット数」の亜流で「プリペアドにすると速度が遅くなる」って言ってる人を聞いた事があるんだけど、それ、逆。
そもそもプリペアドステートメントは「速度を上げるために考案された」っていう基本背景をちゃんと踏まえてないと危ない。
…まぁ、その頃の基本背景、特にWeb系だと、恩恵にあずかりにくいのは事実なんだけどさ、それにしても。


次に「SQLの実行計画的に遅くなる」についても、概ね上述と一緒。
ちなみにこのケースは「可能性としてはあるんだろうなぁ」という予想の一方で「実際には見た事がない」ので、実際の状況を目にするまでは懐疑的。


そういや余談。
「プリペアドステートメントは軽量化のためであり、しかもPHPでは軽量化の意味もあまりなく、セキュリティ的には無関係です(`・ω・´)キリッ」とかいう寝言をほざいていた、ガラケーなシステム会社擬さんがいらっしゃいましたねぇお元気してらっしゃいますですかしらん?
Blogネタ的には、 http://d.hatena.ne.jp/gallu/20130606/p1 この辺のエントリのネタとかやらかしてくれて、大変に有難い側面のある会社様でいらっしゃいましたが。


閑話休題


まぁ上述諸々から、静的プリペアドステートメントが「人為的ミスの介在の余地がなくなる」ので、割と心安らかだなぁ、とか思うわけです。
「性能とのトレードオフ」については、否定はしないけど「もうちょっと別の場所で、テーブル設計とかマシンパワーとかキャッシュとかロジックとか仕様とか」でカバー出来るんじゃねぇかなぁ? って事象ばかり見ているので、可能性としては視野に入れつつも、懐疑的。


お次。
出てくるのが「動的な識別子」と、あと結構耳にするのが「IN」。


まず動的な識別子なのですが。「パラメタの値をエスケープする」よりは「初手からホワイトリスト用意しておく」ほうが、心安らかだと思うです。
なお「ホワイトリストを用意したうえでエスケープ通しておく」ってのは、おいちゃん的にはアリ。ホワイトリストに「ついうっかり」な何かが「ゼッタイに混入しない」とも限らないよねぇ、とは思うしねw


ん…この辺から、実装イメージを実際に。
一番ありがちなのは「どれか一つのカラムを対象に検索」的な何か。

<form action="./find.php">
<input type="radio" name="target_col" value="hoge">hoge
<input type="radio" name="target_col" value="foo">foo
<input type="radio" name="target_col" value="bar">bar <br>
<input name="value"><br>
<br>
<input type="submit">
</form>

すげぇ雑だけど。カラムhoge, foo, barがあって「どれか一つを選んで」検索する感じ。
多分、一部で言われているのは、こんなやり方。

  //
  $target_col = $_GET['target_col'];
  $val = $_GET['value'];
  //
  $e_target_col = DBが提供しているエスケープ関数($target_col);
  $e_val = DBが提供しているエスケープ関数($val);

  //
  $sql = "SELECT * FROM table WHERE `{$e_target_col}` = '{$e_val}';";

ん…valueはもしかするとプリペアドかもしれない。その辺は適宜脳内補完してくださいませ。


おいちゃんが推奨するのは、雑には、こんな感じ。

  // ホワイトリスト的な何か
  $target_col_list = array (
    'hoge' => 'hoge',
    'foo' => 'foo',
    'bar' => 'bar',
  );

  // XXX 微妙に雑なのは気にしない
  $target_col = @$target_col_list[$_GET['target_col']] . '';
  $val = $_GET['value'];

  //
  if ('' === $target_col) {
    // エラー処理的な何か
    return ;
  }

  // プリペアドステートメントの作成、だと思いねぇ
  $sql = "SELECT * FROM table WHERE `{$target_col}` = :val ;";


でまぁ、こんな風に丁寧なのは、アリだと思う。

  // ホワイトリスト的な何か
  $target_col_list = array (
    'hoge' => 'hoge',
    'foo' => 'foo',
    'bar' => 'bar',
  );

  // XXX 微妙に雑なのは気にしない
  $target_col = @$target_col_list[$_GET['target_col']] . '';
  $val = $_GET['value'];

  //
  if ('' === $target_col) {
    // エラー処理的な何か
    return ;
  }

  //
  $e_target_col = DBが提供しているエスケープ関数($target_col);

  // プリペアドステートメントの作成、だと思いねぇ
  $sql = "SELECT * FROM table WHERE `{$e_target_col}` = :val ;";


ホワイトリストコードん中にべた書きすんな」とかについては「サンプルコードだからそうしたけど必要に応じて適宜外にはじき出してね」って感じ。
まぁ、こんな風に「ホワイトリスト」持っていたほうが、万が一の時に「動かねぇ」のほうで気づくので、テストで洗い出せるか、でなきゃお客様の怒声で気づけるので、便利。


ちなみにINの時は、単純にforなりforeachなりで採番した :val_番号 を埋め込んで、あとでどかどかっと値をbindすりゃいいと思う。
…いやもしかしてもっと妙手妙案があるかもしれないけど、あったら教えてくださいませ(笑


んで。
「数的に少ない、レア業務」まで想定するともうちょっと色々あるような気がするんだけど。
割とあちこちに転がってるくらいの業務ゲームだと、こんな指針で今のところ困らなかったので、こんなもんじゃないかなぁ、とか思うです。


…ふと最後まで読んで気づいた「エスケープについて書いてねぇじゃん」w
ん…正直、あんまり使わないので。
おいちゃん的にはこんな感じ。
・識別子を動的にする時は、慎重の上にも慎重を重ねる意味合いで、ホワイトリストに「糅てて加えて」使うのは、あり
・「速度前提に」使うのは、よっぽどの考察によりけりだけど、基本、なし
エスケープ処理が「どんな事をやっているか」を知るのは大切だけど、基本、自分での実装はせず、提供されている関数なりAPIなりクラスメソッドなりを使う


こんな所かなぁ…うんやっぱり、使わないですむんならあんまり積極的に使う事を考えていない。
いや単純に「プリペアド一本で片付く事」が多いので、あんまりお道具にバリエーションがあっても面倒なだけだなぁ、ってのも強くて(苦笑


以上、多分「いつかどこかで、授業で使うか引用するか」な時用の覚え書きでした(笑

MACアドレスでの認証があずましくない理由

UDIDの話は先日書いたわけなのですが( http://d.hatena.ne.jp/gallu/20130415/p1 )。
そうすると次は「んぢゃMACアドレス」という話が耳に入りまして。


ってなわけで第二弾「MACアドレスでの認証やめれ」をお送りいたします(拍手@サクラの群れ)。
先に結論を書くと「UUIDv4使おう」になりますので、そのように認識をしつつ以下をご覧くださいまっしょ。
UDIDなんで一端「iPhoneの話」をしますが、Androidでも大して変わりゃしませんので、そのようにご認識いただきたく。


さて。
簡単に、MACアドレスを使うのを止めるべき理由は
・プライバシ(名寄せ)にまつわる問題
・クラックしやすいぢゃん問題
の2つになります。
ちと経緯的に「下の状況」が発生しているので、そちらをメインに話を進めさせていただきます。
プライバシ周りは、例えば「固有IDのシンプル・シナリオ( http://www.hyuki.com/techinfo/uniqid.html )」などを適宜ご覧ください。


MACアドレスによる認証ですが、一撃で屠ると
・長さが足りなくて簡単に墜とせる
で片付きます。で、ここが片付いちゃうと「他人になりすませる」ので、「認証が必要」なはずのPageで、それはもぉ心置きなく思う存分「ひゃっほい」出来るという事になります。


かみ砕いて。
上述が成立するには「他人の、かつ"サービスに登録されている、有効なMACアドレス"」をゲトる必要があります。


入手手段その1:無線LANネットワークの傍受等
MACアドレスは、適当に無線LANの一つも設置すれば「簡単に入手」できます。
「この人のアカウントを乗っ取りたい」ならともかく、「とりあえず有効なMACアドレスが欲しい」くらいなら、手段はぶっちゃけなんぼでも。
大分と古い記事ですが、こちらが参考になるかもしれません。
無線LANMACアドレス制限の無意味さがあまり理解されていない( https://takagi-hiromitsu.jp/diary/20071103.html )」


入手手段その2:総当たり(ブルートフォース)
MACアドレスアドレス空間は48ビット(281兆個)。
ただしベンダーIDは「このメーカ(iPhoneの場合、apple)である」というところで固定できるので、事実上24ビット(1677万)。ちなみにアップル社は現在、ベンダーIDを100個ほど持っているらしいってことは100程度しかバリエーションがない。
一端「ベンダーIDを1つ」と仮定して、1677万回で「全てのアドレス」へのチャレンジが出来る。秒あたり50アクセスすると、わずか93時間。
もちろん「可能性のあるあらゆるMACアドレス」を頑張れば93時間の100倍かかるわけなのだけど、実際にそこまで「恣意的にランダムにしている」とは考えにくく、おそらく「割とすぐに分かる規則性」が見て取れるので、つまり「そこそこ時間かければ」認証用MACアドレス、ゲットだぜ!! になる。


結論として、しゃれにならないくらい「弱い」。
ちなみに「hash化したら」というのは常に耳にする愚考で。ンなもんは「レインボーテーブル」作れば終わる。以上。
入り口で入ってくるデータ(MACアドレス)のアドレス空間が狭いんだから、ナニやったって無理。


ちなみにUUIDv4は128ビット。一部「バージョン番号とかで固定部分があるから実質、ランダムなのは122ビット」って話も書いてあったのですが、エビデンスは取れず。
まぁ短い方がよいので、一端「122ビット」で考察をしてみましょう。
アドレスの個数(範囲)は5.3169e+36。…えっと…日本の表記で「5.3澗」。
澗ってなによ?って話なのですが。
万、億、兆、京、垓、じょ(漢字でない…)、穣、溝、澗、という並びの、澗。
数字で書くと、こんな桁。…多分あってると思うけど、数えてミスがあったら突っ込んでちょw
5,316,900,000,000,000,000,000,000,000,000,000,000
…気が遠くなりそうな桁だ(苦笑


MACアドレスで「百時間未満」程度で出来た力技アタックですが。…桁数多すぎて計算できませんが、確か記憶が間違いなければ、億年単位くらいかかったような。
まぁ普通の用途なら「100年かかる」くらいの強度があれば大丈夫だと思いますので、強度的には「十二分」かと思われます。


上述のような理由があるので。
「間違っても」MACアドレスを認証に使おう、とか思っちゃいけません。
UUID、iPhoneならそんなに苦労なく使えるはずなので、ちゃんと素直に、UUIDのv4を、適切な疑似暗号ロジック使って、生成しましょう。

UDIDの話の軽いまとめ

ちょいと今日話が出ていたので、多分今後も「出るだろうなぁ」との予想込みで、Blogに。
(余談。タグに「プライバシ」とか追加しようかしらん? どう思います?)


ん…直接的には、あぽー様がすでに「おら受けトンねぇ」宣言をなさってるでごわす。
ちなみにそれって2011/08/19のお話( http://techcrunch.com/2011/08/19/apple-ios-5-phasing-out-udid/ )。「最近」とか騒いではいけません。

AppleApp StoreRetina非対応/UDID採用アプリの登録とアップデートが不能に 5月1日から
( http://www.itmedia.co.jp/news/articles/1303/22/news041.html )
Apple、5月1日からUDIDにアクセスするアプリ・非iPhone 5最適化アプリはリジェクトと発表。
( http://www.appbank.net/2013/03/22/iphone-news/568323.php )
■[iOSプログラミング]UDIDが完全に利用禁止に
( http://d.hatena.ne.jp/nakamura001/20130325/1364231754 )
アップル、UDID利用アプリを5月から拒否へ--Retinaディスプレイや「iPhone 5」への対応も必須に
( http://japan.cnet.com/news/service/35029839/ )


で、その背景ですが。
いくつかあるかと思うのですが、おいちゃんが認識している限りで
・広告とかビッグデータとか行動ぶっこ抜きとか系のプライバシ周り
・認証に使う馬鹿が後を絶たない
の2つがあると思ってるでやんす。


元々は、フューチャーホン(ガラケー)の契約者固有IDの頃から、少なくとも一部では言われるったけ言われまくった話ではあるのですが。
ん…以下のURIを適宜ごらんやがれください。

契約者固有IDとは
( http://itpro.nikkeibp.co.jp/article/Keyword/20081007/316269/ )( http://itpro.nikkeibp.co.jp/article/Keyword/20081007/316269/?SS=imgview&FD=-499245482&ST=keyword )
無責任なキャリア様に群がるIDクレクレ乞食 ―― 退化してゆく日本のWeb開発者
( http://takagi-hiromitsu.jp/diary/20080727.html )


根っこにあるのは「変更のきかない固定ID」がやべぇって話です。
こちらがその辺では秀逸なURIの一つ。

固有IDのシンプル・シナリオ
( http://www.hyuki.com/techinfo/uniqid.html )


フューチャーフォンはそれでも「限定されたIP空間内であるが故に安全である」とかいう嘘がまかり通っていたので、とりあえず「見えなければ安全」というクラスタ相手には、大砲の前の半紙10枚分ほどの防御力は見込めたのですが。
iPhoneにおいて、もはやその辺は半紙1枚ほどの防御力にも満たない状況となってます。


あぁ物凄く一応念のため。
極めてごく稀に「UDIDは偽装が出来ないから云々」とかいう話を聞きますが。360度大体どの角度から見ても「嘘」です。


まずUDIDのリストはぶっちゃけ「結構なところの広告会社がお手持ち」な可能性が想起されます。特に「行動ターゲットマーケティング」という言葉を用いているところには要注意。


行動ターゲティング広告
http://advertising.yahoo.co.jp/ad/privacy/

掲載場所
スマートフォン版の行動ターゲティング広告は、スマートフォンYahoo! JAPANiOSAndroid対応ページ)のページ、またはYahoo! JAPANが提供するスマートフォン向け公式アプリケーション内のWebViewを対象に掲載されます(一部対象外のページ・アプリケーション内のWebViewもあります)。


※1 アプリケーション内WebViewとは、アプリケーションからウェブページを閲覧できる簡易的なブラウザー機能です。
※2 個体を識別する情報とは、iOSにおいてはUDID(Unique Device Identifier)、AndroidにおいてはAndroidIDを指します。アプリケーションを識別する情報とは、iOSAndroidともにアプリケーションごとに生成されたUUID(Universally Unique Identifier)を指します。

(「UDID使って広告流してます」ってはっきりと書いてある一例)


iOSにおけるリワード広告でのUDIDを使わない計測方法の提案
http://blogs.itmedia.co.jp/jinmsk/2012/02/iosudid-eb91.html

個体識別番号はリワード広告におけるユーザユニークIDを特定するための一つの方法であり、このIDを使わなくともリワード広告は行えると何回か書きましたが、今回はその一例の仕組みを書きたいと思います。

(使わなく"ても"というあたりから背景をかぎ取ってください)


UDIDに依存する人々とたしなめる人々
http://togetter.com/li/177744
(困っている人の論調を見てみましょう)


iOS の UDID 廃止問題は誰にとっての問題なのか
http://www.deftrash.com/blog/archives/2011/09/ios_udid.html

利用ポイントを抜粋させていただくと、
アプリケーションのサーバとのセッション保持
行動トラッキングによるターゲティング広告
リワード広告
目的は異なるにせよ、デベロッパーにとって容易にユーザを特定するための手段としての UDID というのは、便利な代物です。UDID の万能なところは、ターゲティング広告やリワード広告で使われているように、アプリを横断しても同一ユーザを特定できるところです。その良し悪しは別として、iOS 関連のエコシステムの拡張において、UDIDの果たした役割は小さくなかったと思います。

(「誰にとって」便利なのか、って話ではありますが)


なぜiOSでUDIDが必要とされていたのか、メモ
http://d.hatena.ne.jp/shinichitomita/20110822/1314013326


米グーグル、iOS向け広告プラットフォームからUDIDを除外
http://ggsoku.com/2013/04/google-admob-remove-udid/


UDIDと行動ターゲッティング広告
http://d.hatena.ne.jp/next49/20110822/p1


などなど。
んで、お手持ちにそういったリストがあると、次に出てくるのは漏洩。
直接的には「デジタル出版を手掛ける米BlueToad(本社フロリダ州)」の事件が有名ですが。
http://www.itmedia.co.jp/enterprise/articles/1209/11/news031.html
http://internet.watch.impress.co.jp/docs/news/20120911_558780.html
http://jp.techcrunch.com/2012/09/12/20120911inside-the-udid-hack-how-a-lone-programmer-cracked-the-case/


まぁじゃぁ「上述以外のすべてのadやってる会社さまが、技術力がパーペキでイージスの盾レベルの強度の防御能力を有する」って考えるのは、少々ナイーブに過ぎる見方か、と。


さて。んじゃUDIDが適当に流出しますと。まぁぶっちゃけ「ネットで流出した」その手のデータは、もはや為す術もなく散らかっていきます。
http://d.hatena.ne.jp/gallu/20120505


んで。UDIDは「ふつ〜に偽装ができます」。

識別子(UDID)の偽装
( http://blog.livedoor.jp/sirazumi_rio/archives/50785204.html )
JailBreakで出来る色々な偽装
( http://zuzu-service.net/misc/iphone/20110429/235.html )


つまり。
UDIDを使う場合、それがどんな目的であれ「とてもしゃれになっていない」状況になります。
ちなみに「じゃぁ代わりにMACアドレス」とか考えちゃうような頭には脳みそが入ってないと思いますので、漂白剤とかで頭蓋骨洗ってからぬかみそかおがくずを詰め込んできてください。


UDIDの代わりは、UUIDを、もうちょい正しく書くと「UUIDのバージョン4」を用いるのが現状もっとも適切であると考えられます。
以下を参考にしてみてください。

UUID を作る - CFUUIDCreate と globallyUniqueString
( http://xcatsan.blogspot.jp/2009/11/uuid-cfuuidcreate-globallyuniquestring.html )
■[iPhoneプログラミング]UUIDを生成する
( http://d.hatena.ne.jp/nakamura001/20100706/1278376345 )


ついで。

OpenUDIDはUDIDの代替にはならない
( http://ameblo.jp/ryoji4321/entry-11204718235.html )
今更聞けないUDIDとUUIDまとめ for iPhoneアプリ開発
( http://matome.naver.jp/odai/2134045671202872301 )


別口で「広告識別子(Advertising Identifier)」ってのが出てきました。
認証目的に使うのは御法度ですが、広告系ならまぁ考えてみてもいいのかも(まだ問題山盛りですが、UDIDよりはわずかばかりマシ)。

iOS6.1の新機能「Advertising Identifierをリセット」を見てみる
( http://d.hatena.ne.jp/mikanmarusan/20130211/1360581304 )
iOS6.1の新機能「Advertising Identifierをリセット」
( http://otoko50.cocolog-nifty.com/blog/2013/02/ios61advertisin.html )
iOS6のAdvertising Identifierについて
( http://togetter.com/li/377573 )


…ん、なんかリセット系の記事ばかりですねぇ。
一応、実装も…と思ったが、見事に見つからねぇ orz
まぁいいや誰かが突っ込んでくれたら追記しようw


以上、めっさ雑にですが「UDID使うな」なお話、でした。

CSRF実装の一案

注意
結構「状況限定」なので、本当に「一案」程度。


前提
セッションIDが定期的に変わる。だから「セッションIDをtokenに」しにくい。


一案
「消すのが面倒くさければmemcachedにすればいいぢゃない」作戦


CSRFで引っ掛けたい画面を「画面A → 画面B」の遷移である、と仮定します。


画面Aでの処理
・ディードなIDを作成( http://d.hatena.ne.jp/gallu/20120607/p1 のコメント参照。トークンそのものは http://d.hatena.ne.jp/gallu/20120402/p2 を参照)
・ディードなIDをkeyに、ユーザIDをvalueに、寿命を10分(この辺適当)にして、memcachedにぶち込む
・ディードなIDをhiddenに埋め込んでおく

画面Bでの処理
・hiddenからディードなIDを取得
memcachedに確認
・ユーザIDを確認
・all OKなら
 ・memcachedの該当情報を削除
 ・OKな処理をぶちかます


こんな感じ?
とりあえずざっくりだけど、ヘンにゴミもたまらないし、楽に実装できるんじゃなかろうか、って思うんだけど、どだろ?
あんまり高圧だともうちょい考えたほうがよいかもなのだけど…結構な圧まではいけそうな気もする。


今度、チャンスとタイミングを見計らって、検証でもしてみますか。