携帯業界の認証事情
巡回サイトの一つである高木浩光@自宅の日記で以下のようなエントリーがあった。
高木浩光@自宅の日記 - 携帯電話向けWebアプリの脆弱性事情はどうなっているのか
ここではいつもの高木氏の口調で、「携帯向けWEBアプリ開発では未だにGETパラメータでセッションIDを渡しており、それはこれまでも何度もいかんことだと言っている。」というような内容が語られている。
確かにWEB+DBの記事に対して高木氏が注釈で言っているように「IPアドレスによる制限に関して書いていない」という点に関してはWEB+DB側の落ち度だと思う。実際これを行わない限り端末IDやユーザID*1による認証が意味をなさなくなってしまうからだ。*2
但し、キャリア毎にIPアドレス制限をする限りにおいては端末IDやユーザIDは偽装不可能*3なので、むしろ他人でも入力可能なパスワード認証よりも強力な認証かもしれません。逆にいえばその認証方法があるからこそ携帯業界ではクッキー無しでのセッション管理が可能になっていると言えます。
なので高木氏の以下の批判は的外れと言えるでしょう。
そもそも、「ID/パスワードを毎回入力するのでは携帯の場合では特に面倒です」などといって、端末IDをパスワード代わりにしてはいけない。端末IDは他のサイトにも同じものが送信されるのだから、パスワード代わりになどならない。
高木浩光@自宅の日記 - 携帯電話向けWebアプリの脆弱性事情はどうなっているのか
こんな基本的なことが携帯業界では未だ常識になっていないようで困ったことだ。
同じ値の端末IDが他のサイトにも送られたからと言っても、そのIDを他人が騙ることはできないのだから問題ないはずです。パスワード認証におけるパスワードの代わりにはなりませんが、パスワード認証に代わる認証になります。
ここで一応端末IDとユーザIDが偽装できない理由を書いておきます。
まず端末IDですが、
- DoCoMoではformタグにutn属性を付けることでそのsubmitのリクエストにおいてUser-Agentに端末IDが付加されます*4。
- Softbankでは常にUser-Agentに端末IDが付いてきます。
- AUでは端末IDに相当するものはありません。
- そして偽装できない理由ですが、どのキャリアでも User-Agent により送信されるものであり、携帯端末のブラウザが User-Agent の偽装を許していない為です。
- 携帯キャリア毎のIP制限を行っている限りにおいては携帯端末でアクセスしていることが保障されるはずなので端末IDは十分信頼がおけると言えます。
次にユーザIDですが、これはキャリア毎に送信方法が異なっているので個別に説明します。
- まず、AUの場合は x-up-subno というHTTPヘッダにより送信されます。これは User-Agent と同様に携帯ブラウザが偽装を許していないことを理由に偽装が出来ません。
- 次に、Softbankの場合は x-jphone-uid というHTTPヘッダにより送信されます。これも同じく携帯ブラウザが偽装を許していないことを理由に偽装が出来ません。
- 最後にDoCoMoの場合ですが、HTTPヘッダでは無く uid という名前のCGIパラメータで送信されます。高木氏はこれを見てUIDもGETで渡すのなら意味がないではないか、と危惧していました。しかしこれは以下の2つの点で間違っています。
- まずそもそも uid=端末ID というGETパラメータが使われることはありません。通常は uid=NULLGWDOCOMO というGETパラメータを利用することになっており、DoCoMoのゲートウェイプロキシがuidの値を本物のユーザIDに書き換えてコンテンツサイトに送っているだけなのです。なのでブラウザもuid=NULLGWDOCOMOという文字列のままのURLとして認識しているので仮にRefererが漏れてもNULLGWDOCOMOという文字列が漏れるだけです。
- 次に、仮にユーザIDが漏れたとしても*5それを偽装することは出来ないということです。偽装が出来ないことはDoCoMo特有の(変な)仕様により保障されています。
uidの偽装不能な理由は口で説明するのも良いですが以下のような簡単な実証スクリプトにアクセスした結果を見た方が分かりやすいと思います。
<html><head><title>uid test</title></head><body> uid=<?php echo htmlspecialchars($_GET['uid']) ?><br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>">1.</a>no param<br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>?uid=NULLGWDOCOMO">2.</a>uid=NULLGWDOCOMO<br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>?uid=1234567890ab">3.</a>uid=1234567890ab<br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>?uid=1234567890a">4.</a>uid=1234567890a<br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>?uid=1234567890abc">5.</a>uid=1234567890abc<br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>?uid=foobar">6.</a>uid=foobar<br> <a href="<?php echo $_SERVER['SCRIPT_URL'] ?>?uid=">7.</a>uid=<br> </body></html>
この結果は以下のようになります。
NO | uidに指定した値 | 実際に送信されてくる値 |
---|---|---|
1 | パラメータ無し | 無し |
2 | NULLGWDOCOMO | ユーザID |
3 | 1234567890ab | ユーザID |
4 | 1234567890a | 1234567890a |
5 | 1234567890abc | 1234567890abc |
6 | foobar | foobar |
7 | 空文字 | 空文字 |
つまり uid に12桁の文字列を指定した場合はそれがどんな値だろうと強制的に正しいユーザIDに書き換えられてしまうということです。
なので他人のuidを指定した場合は、それが正しいuidであれば12桁の筈なので強制的に自分のuidに書き換えられてしまうので他人のuidを騙ることはできないし、12桁でないuidの値は認証には使われるものではないので意味がありません。
携帯に関して言いたいことを纏めると以下のような感じかな。
- 携帯の端末ID及びユーザIDは漏れても直接の問題はない。*6
- キャリア毎のIP制限をしてあれば携帯の端末ID及びユーザIDは信用できる(偽装できない)*7。
- 携帯サイトの場合は以下の条件を満たせばセッションIDをGETパラメータに含めても良い。
- キャリア毎のIP制限をした上で端末IDもしくはユーザIDを使って認証する。
- 携帯用のセッションIDとPC向けのセッションIDは共有してはいけない*8。
- ゲートウェイのIPアドレス一覧情報など
一般に公開されていないように思われる…。せめてIPアドレス一覧は公開してほしい。公開されてるようだ。 - 情報が少ないことにより正しい実装に関する議論が育ちにくい。
- DoCoMoはやる気ないようだが、いい加減クッキー対応端末を出してほしい。
- 携帯料金を安くしてほしい。
- 実家が未だに電波通じないのを何とかしてほしい…。
追記)タイトル変更しました*9。
追記)ユーザIDや端末IDの詐称可能性に関して別エントリーで追記あり。
*1:ここでは、端末IDが携帯のハードウェアである端末が持つIDで、ユーザIDはキャリアとの契約に対応するIDとして話します。(キャリア毎に呼び方が違うんだよな…)
*2:端末IDやユーザIDは簡単に漏洩するし(むしろ公開情報に近い)、一般にGETパラメータやUser-Agentは偽装可能だから。
*3:端末IDはスマートフォン等でゴニョゴニョすると偽装できる可能性があるようだ…。ユーザIDを使えということか。
*4:その際、端末IDを送信する旨のダイアログが表示されます。
*5:というかそもそもユーザIDはどのサイトに対しても同じ値が送られます。
*7:偽装出来たとしたらそれは携帯端末かキャリアのゲートウェイの問題(バグ、脆弱性)であり、サイト側の責任ではない、…と思う。
*8:携帯のRefererでセッションIDが漏れた場合、それをPCブラウザで利用することでセッションハイジャックを許してしまうから。
*9:元々のタイトルは「珍しく間違った批判をしている高木先生」だったが単に情報まとめ的な意味が強い記事になってきたので。