目的
Androidにおけるセキュアコーディングをする為の一つの指針として、JSSEC(一般社団法人日本スマートフォンセキュリティ協会)が作成している「Androidアプリのセキュア設計・セキュアコーディングガイド」に準拠したコーディングをする場合、実装時の参考にしたい部分をざっくりまとめる
公式 - https://www.jssec.org/report/securecoding.html
概要
公式のPDFではActivity,BroadCastReceiver, Service, SQLiteOpenHelper等セキュアコーディングに重要なクラスの使い方をSample付きでまとめてある。
その他アプリの暗号化やデータの暗号化等も記載しているのでそれぞれ部分ごとに抜き出して参考にするのもあり
各項目にルールブックというものがあり、やるべきこと要約が載っているのでそこも参照するべき
内容
実装で気をつけられる点挙げていく
網羅している部分は一部なので、詳細や他の点に関しては公式を参照
共通
ログについて
4.8.「LogCat にログ出力する」あたり
ログ情報は同じ端末内の他のアプリからも読み取り可能であるため、センシティブな情報を LogCat にログ出力してしまうアプリには情報漏洩の脆弱性があるとされる。
Proguard使おう→特にログ対策。自動で消してくれる上にapkの容量も最適化する
android.util.Log以外使わないこと
//よくない例
String debug_info = String.format("%s:%s", "センシティブな情報 1", "センシティブな情報 2");
if (BuildConfig.DEBUG) android.util.Log.d(TAG, debug_info);
↑こういうコードはよくない。
センシティブな情報を変数に入れてからログ出力、とかやるとProguardはログ呼び出し部分以外は削除してくれないのでデコンパイルすると文字列は解析できる
//改善後
if (BuildConfig.DEBUG) {
String debug_info = String.format("%s:%s", "センシティブな情報 1", "センシティブな情報 2");
android.util.Log.d(TAG, debug_info);
}
if (BuildConfig.DEBUG) {}で囲むと丸ごと削除してくれる
4.8.3.2. ログレベルとログ出力メソッドの選択基準
ログレベルで出力を残したりできるので、センシティブな情報はLog.i以上で出さない。
printStackTraceを使わない(システムの情報が出力されちゃうから)
データのチェック
3.2.「入力データの安全性確認」
→あんまり具体的に書いてない。ここは自分でやる
5.1.3.4. スクリーンキャプチャの無効化
WindowManagerにaddFlagでFLAG_SECUREを設定する
証明書のハッシュ値
5.2.1.3. アプリの証明書のハッシュ値を確認する方法
keytool、jssecのチェッカー使って調べられる
apkの改ざん検知
5.2.3.3. APK の改ざんを検出する
APKの証明書と予めソースコードに埋め込んだ開発者の証明書を比較(この処理の無効化もそこまで難しくないので、そこまで効果あるか微妙だけど)
その他
5.5.2.3. : プライバシーポリシーを含むアプリ
→最初に包括同意をさせる
→できれば情報使用時に個別で同意させる(記載する内容は総務省 SPI 参照)
Activity,BroadCastReceiver, Service等共通
アプリ内のみなら必ずexported=falseをつけるべき
他アプリと連携する場合はホワイトリスト用意、証明書の検証等でマルウェア対策
証明書検証の為同じ会社アプリなら同じ証明書を使う
仮にexported=falseを指定していないと意図せず外部のアプリから呼び出される恐れがある。manifestに列挙するクラスは必ず検討すること
Sqlite
4.5. SQLite を使う
- データ型
Sqliteのデータ型は"hint"なので、指定した値が必ず入るというものではない
→データの制約はSqliteでやるべきでない
→データチェックは必ずinsert前にやること
- クエリ組み立て
クエリ書くならプレースホルダー使う
- DBのデコンパイル対策等
SQLCipher使って暗号化
Service
共通 :他アプリと連携する場合はホワイトリスト用意、証明書の検証等でマルウェア対策
証明書検証の為同じ会社アプリなら同じ証明書を使う
4.4.3.2. Service の実装方法について
startService を利用する場合と bindService を利用する場合でライフサイクルが異なる。
公開しないService(アプリ内のみ)ならbindでやるべき
BroadCastReceiver
共通 :他アプリと連携する場合はホワイトリスト用意、証明書の検証等でマルウェア対策
証明書検証の為同じ会社アプリなら同じ証明書を使う
Normal Broadcast は送信時に受信可能な状態にある Broadcast Receiver
に配送されて消滅する。
StickyBroadcastは送信時に受信可能な状態にあるBroadcastReceiverに配送された後に消滅することはなくシステムに残り続け、後にregisterReceiver()を呼び出したアプリがStickyBroadcastを受信することができる
Sticky BroadCastでない場合届かない場合もあるのを注意
同じ開発者鍵なら同じUIDにして読み出すことができる→アプリ間連携など、限定的にBroadCastReceiverを公開したい場合に使える
ContentProvider
共通 :他アプリと連携する場合はホワイトリスト用意、証明書の検証等でマルウェア対策
証明書検証の為同じ会社アプリなら同じ証明書を使う
OSのバグ
2.2以前は他アプリからアクセスできないようにすること(非公開設定)ができない→ContentProviderを使うべきでない。DB等でデータ持つこと
Activity
共通 :他アプリと連携する場合はホワイトリスト用意、証明書の検証等でマルウェア対策
証明書検証の為同じ会社アプリなら同じ証明書を使う
暗黙的Intent等の扱いを注意
タスクの割り当てを変えることによってIntentを読み取れる可能性があるので下記を指定しない
4.1.1.1. 非公開 Activity を作る・利用する
taskAffinityを指定しない
launchModeを指定しない
FLAG_ACTIVITY_NEW_TASKを指定しない
WebView
JavaScriptとファイルアクセスをなるべくenableにしない
できればローカルのHTMLアクセスのみenableにする
できれば自社管理コンテンツのHTMLアクセスのみenableにする
4.9.2.4. SSL 通信のエラーを適切にハンドリングする
SSLのハンドリングはしっかりする。可能な限り証明書無視のコードは入れないこと
Notification
4.10. Notification を使用する
Manifestにpermissionをつけていれば、どのアプリからでも全てのNotificationの情報を取得可能
センシティブな情報はそこに含めない
NotificationのVisibillityでPrivateにするべき。Publicだとロック中でも表示できるように設定される(デフォルトはPrivateだけど)
パスワード入力
5.1. パスワード入力画面を作る
マスク表示(android:inputType 属性に"textPassword")
平文表示するオプションも用意していい(今覗かれていませんか?みたいな注意喚起文章つける)
5.1.2.4. 前回入力したパスワードを表示する場合、ダミーパスワードを表示する
前回表示したパスワードを入れる様な仕様の場合、そこから桁数が推測できてしまう。
桁数推測を防ぐため固定文字のダミーパスワードを初期表示しておく
5.1.3.1. ログイン処理について
例えば
IDだけを間違えた場合 → IDが間違っています
パスワードだけを間違えた場合 → パスワードが間違っています
これだと片方合っている、ということが攻撃者に分かるのでよくない。
ログイン ID または パスワード が間違っています。
のようにすること
5.6.2.4.
パスワードは必ずSaltを使用すること
暗号化
5.6.2.2.
鍵長基準の表
5.6.3.3.
Android 4.2.x~4.3.xの”Crypto" Provider の SecureRandom 実装バグ
→対策(http://android-developers.blogspot.jp/2013/08/some-securerandom-thoughts.html)
以上。
そのうちiOS版もまとめたいけど、内容がAndroidより広範であいまいなので、やらないかも