コード品質のテスト
モバイルアプリ開発者はさまざまなプログラミング言語とフレームワークを使用しています。そのため、SQL インジェクション、バッファオーバーフロー、クロスサイトスクリプティング (XSS) などの一般的な脆弱性は、セキュアなプログラミングプラクティスを軽視した場合のアプリに現れることがあります。
同じプログラミングの欠陥が Android と iOS の両方のアプリにある程度の影響を与える可能性があるため、最も一般的な脆弱性クラスの概要をこのガイドの一般セクションで繰り返し説明します。後のセクションでは、OS 固有のインスタンスと悪用緩和機能について説明します。
インジェクション欠陥
インジェクション欠陥 はユーザーの入力がバックエンドのクエリやコマンドに挿入されたときに発生するセキュリティ脆弱性のクラスを表します。メタ文字を注入することにより、攻撃者は誤ってコマンドやクエリの一部として解釈される悪質なコードを実行できます。例えば、SQL クエリを操作することにより、攻撃者は任意のデータベースレコードを取得したり、バックエンドデータベースの内容を操作する可能性があります。
このクラスの脆弱性はサーバー側のウェブサービスで最も一般的です。悪用可能なインスタンスもモバイルアプリ内に存在しますが、発生頻度は少なく、アタックサーフェスも小さくなります。
例えば、アプリはローカルの SQLite データベースをクエリすることがありますが、そのようなデータベースは通常、機密データを格納しません (開発者が基本的なセキュリティプラウティスに従うと仮定します) 。これにより SQL インジェクションは実用的ではない攻撃ベクトルになります。それにもかかわらず、悪用可能なインジェクション脆弱性が発生することがあります。これは適切な入力検証がプログラマにとって必要なベストプラクティスであることを意味しています。
SQL インジェクション
SQL インジェクション 攻撃は入力データに SQL コマンドを統合し、定義済みの SQL コマンドを模倣します。SQL インジェクション攻撃が成功すると、攻撃者はデータベースの読み取りや書き込みが可能になり、サーバーに付与されたアクセス許可に応じて管理コマンドを実行される可能性があります。
Android と iOS のアプリは両方ともローカルデータストレージを制御及び整理する手段として SQLite データベースを使用します。Android アプリはローカルデータベースにユーザー資格情報を格納することにより、ローカルユーザー認証を処理すると仮定します (この例のための意図的な悪いプログラミングプラクティスです) 。ログインすると、アプリはデータベースをクエリし、ユーザーが入力したユーザー名とパスワードでレコードを検索します。
ここで攻撃者が "username" と "password" のフィールドに以下の値を入力したとします。
これにより以下のクエリが生成されます。
条件 '1' = '1'
は常に true と評価されるため、このクエリはデータベース内のすべてのレコードを返し、有効なユーザーアカウントが入力されていなくてもログイン関数は true
を返すようになります。
XML インジェクション
XML インジェクション 攻撃では、攻撃者は XML メタキャラクタを注入して XML コンテンツを構造的に変更します。これは XML ベースのアプリケーションやサービスのロジックを侵害するために使用される可能性があり、攻撃者がコンテンツを処理する XML パーサーの操作を悪用する可能性もあります。
この例では、ローカルファイル /dev/random
が開かれ、無限のバイトストリームが返され、潜在的にサービス拒否を引き起こします。
アプリ開発の現在の傾向では、ほとんどが REST/JSON ベースのサービスにフォーカスしており、XML はあまり一般的ではなくなっています。しかし、まれにユーザーが提供した、または信頼できないコンテンツが XML クエリを構築するために使用される場合、iOS の NSXMLParser などのローカル XML パーサーにより解釈される可能性があります。したがって、前記の入力は常に検証され、メタキャラクタはエスケープされる必要があります。
インジェクション攻撃ベクトル
モバイルアプリのアタックサーフェスは一般的なウェブアプリケーションやネットワークアプリケーションとは大きく異なります。モバイルアプリはネットワーク上でサービスを公開することはあまりなく、アプリのユーザーインタフェース上で実行可能な攻撃ベクトルはまれです。アプリに対するインジェクション攻撃はプロセス間通信 (IPC) インタフェースを介して発生することがほとんどです。悪意のあるアプリがデバイス上で実行されている別のアプリを攻撃します。
潜在的な脆弱性を突きとめるには、以下を行います。
信頼できない入力について可能性のあるエントリポイントを特定し、それらのロケーションからトレースを行い、デスティネーションに潜在的な脆弱性をもつ機能が含まれているかどうかを確認します。
既知の危険なライブラリや API コール (SQL クエリなど) を特定し、チェックされていない入力がそれぞれのクエリとのインタフェースに成功するかどうかを確認します。
手動によるセキュリティレビューでは、両方の技法を組み合わせて使用する必要があります。一般に、信頼できない入力は以下のチャネルを通じてモバイルアプリに入ります。
IPC コール
カスタム URL スキーム
QR コード
Bluetooth, NFC, またはその他の方法で受信した入力ファイル
ペーストボード
ユーザーインタフェース
以下のベストプラクティスが実行されていることを確認します。
信頼できない入力を型チェックしたり、許容値のリストを使用して検証しています。
データベースクエリを実行する際に変数バイディングでのプリペアードステートメント (つまり、パラメータ化されたクエリ) を使用しています。プリペアードステートメントが定義されている場合、ユーザー指定のデータと SQL コードは自動的に分離されています。
XML データを解析する場合、パーサーアプリケーションが XXE 攻撃を防ぐために外部エンティティの解決を拒否するように構成されていることを確認しています。
X.509 形式の証明書データを使用する場合、セキュアなパーサーが使用されていることを確認しています。例えばバージョン 1.6 以下の Bouncy Castle は安全でないリフレクションによりリモートコード実行を許します。
OS 固有のテストガイドでは各モバイル OS の入力ソースや潜在的に脆弱な API に関する詳細について説明します。
クロスサイトスクリプティング欠陥
クロスサイトスクリプティング (XSS) の問題により、攻撃者はクライアント側のスクリプトをユーザーが閲覧したウェブページに注入できます。この種の脆弱性はウェブアプリケーションによく見られます。ユーザーがブラウザに注入されたスクリプトを閲覧すると、攻撃者は同一生成元ポリシーをバイパスすることができ、さまざまな攻撃 (例えば、セッションクッキーの盗難、キー押下の記録、任意のアクションの実行など) を可能にします。
ネイティブアプリ のコンテキストでは、これらの種類のアプリケーションはウェブブラウザに依存していないという単純な理由により、XSS のリスクはあまりありません。但し、iOS の WKWebView
や非推奨の UIWebView
および Android の WebView
などの WebView コンポーネントを使用するアプリではこのような攻撃について潜在的に脆弱です。
静的解析 - セキュリティテストに関する注意点
存在する WebView を注意深く見て、信頼できない入力についてアプリによる処理を調査します。
Java
Kotlin
ユーザー入力により決定される XSS 問題のもう一つの例は public override メソッドです。
Java
Kotlin
ADB
Clipboard Data
3rd party Intent in Java or Kotlin:
WebView を使用してリモートウェブサイトを表示する場合、HTML をエスケープする負担はサーバ側に移ります。XSS の欠陥がウェブサーバーに存在する場合、これを使用して WebView のコンテキストでスクリプトを実行できます。したがって、ウェブアプリケーションソースコードの静的解析を実行することが重要です。
以下のベストプラクティスに準じていることを確認します。
絶対に必要でない限り、信頼できないデータを HTML, JavaScript, 他の解釈されるコンテキストで処理していません。
エスケープ文字には HTML エンティティエンコーディングなどの適切なエンコーディングが適用されています。注:エスケープのルールは HTML が他のコード内にネストされていると複雑になります。例えば、JavaScript ブロック内にある URL を処理するなどです。
レスポンスでのデータの処理方法を検討します。例えば、データが HTML コンテキストで処理される場合に、エスケープする必要がある六つの制御文字です。
&
&
<
<
>
>
"
"
'
'
/
/
動的解析 - セキュリティテストに関する注意点
XSS の問題は手動や自動の入力ファジングを使用すると最も良く検出できます。すなわち、利用可能なすべての入力フィールドに HTML タグや特殊文字を注入して、ウェブアプリケーションが無効な入力を拒否するか、その出力に HTML メタキャラクタをエスケープすることを確認します。
メモリ破損バグ
メモリ破損バグはハッカーにとって一般的な頼みの綱です。このクラスのバグはプログラムが意図しないメモリ位置にアクセスするようなプログラミングエラーが原因です。適切な状況下では、攻撃者はこの動作を利用し、脆弱なプログラムの実行フローをハイジャックして任意のコードを実行できます。この種の脆弱性は様々な方法で発生します。
バッファオーバーフロー: これはアプリが特定の操作のために割り当てられたメモリ範囲を越えて書き込みを行うプログラミングエラーを表します。攻撃者はこの欠陥を使用して関数ポインタなど隣接するメモリにある重要な制御データを上書きできます。バッファオーバーフローは以前は最も一般的な種類のメモリ破損の欠陥でしたが、さまざまな要因により長年にわたり流行してはいません。特に、安全でない C ライブラリ関数を使用するリスクの開発者の意識は現在の一般的なベストプラクティスであり、バッファオーバーフローバグを捕捉することは比較的簡単です。しかし、そのような欠陥をテストする価値はまだあります。
ダングリングポインタ: これらは、メモリ位置への参照を含むオブジェクトが削除または割り当て解除されますが、オブジェクトポインタがリセットされないときに発生します。プログラムがすでに割り当て解除されたオブジェクトの仮想関数をコールする ダングリング ポインタを後で使用する場合、元の vtable ポインタを上書きすることにより実行をハイジャックすることが可能です。あるいは、ダングリングポインタにより参照されるオブジェクト変数や他のメモリ構造を読み書きすることが可能です。
Use After Free: これは解放 (割り当て解除) されたメモリを参照するダングリングポインタの特殊なケースを指します。メモリアドレスがクリアされると、その位置を参照するすべてのポインタは無効になり、メモリマネージャはそのアドレスを使用可能なメモリのプールに戻します。このメモリ位置が後に再割り当てされるとき、元のポインタにアクセスすると、新たに割り当てられたメモリに含まれるデータを読み書きします。これは通常、データ破損や未定義の動作につながりますが、巧妙な攻撃者は適切なメモリ位置を設定して命令ポインタの制御に利用できます。
整数オーバーフロー: 算術演算の結果がプログラマにより定義された整数型の最大値を超える場合、これは最大整数値を「ラップアラウンド」した値となり、必然的に小さい値が格納されます。逆に、算術演算の結果が整数型の最小値より小さい場合、結果が予想より大きくなる 整数アンダーフロー が発生します。特定の整数オーバーフローやアンダーフローのバグが悪用可能かどうかは整数がどのように使用されるかで異なります。例えば、整数型がバッファの長さを表す場合、バッファオーバーフローの脆弱性が生じる可能性があります。
書式文字列の脆弱性: チェックされていないユーザー入力が C 関数の
printf
ファミリの書式文字列パラメータに渡されると、攻撃者は '%c' や '%n' などの書式トークンを注入してメモリにアクセスする可能性があります。書式文字列のバグはその柔軟性のため、悪用するのに便利です。プログラムが文字列書式操作の結果を出力すると、ASLR などの保護機能をバイパスして、攻撃者は任意のメモリに読み書きできます。
メモリ破損を悪用する主な目的は、通常、プログラマが シェルコード と呼ばれるアセンブルされた機械命令を配置した場所にプログラムフローをリダイレクトすることです。iOS では、データ実行防止機能は (名前が示すように) データセグメントとして定義されたメモリからの実行を防ぎます。この保護をバイパスするために、攻撃者はリターン指向プログラミング (ROP) を活用します。このプロセスではテキストセグメント内の小さな、既存のコードチャンク (「ガジェット」) を繋いで実行します。これらのガジェットは攻撃者にとって有用な機能を実行したり、攻撃者が格納した シェルコード の位置の mprotect
と呼ばれるメモリ保護設定を変更する可能性があります。
同様に、iOS アプリは Obj-C や Swift で C/C++ コールをラップできるため、これらの種類の攻撃を受けやすくなります。
例:
以下のコードスニペットはバッファオーバーフロー脆弱性をもたらす状態の簡単な例を示しています。
潜在的なバッファオーバーフローを特定するには、限られたサイズのバッファにユーザー入力をコピーするなど、安全ではない文字列関数 (strcpy
, strcat
, その他の "str" 接頭辞で始まる関数など) や潜在的に脆弱なプログラミング構造の使用を探します。以下は安全でない文字列関数のため危険とみなすべきです。
strcat
strcpy
strncat
strlcat
strncpy
strlcpy
sprintf
snprintf
gets
また、"for" や "while" ループとして実装されたコピー操作のインスタンスを探し、長さのチェックが正しく実行されていることを確認します。
以下のベストプラクティスに従っていることを確認します。
配列インデックス、バッファ長計算、その他セキュリティ上重要な操作に整数変数を使用する場合には、符号なしの整数型が使用されていること、および整数ラッピングの可能性を防ぐために前提条件テストを実行していることを確認しています。
アプリは
strcpy
や他の "str" 接頭辞で始まる安全でない文字列関数や,sprint
,vsprintf
,gets
などを使用していません。アプリに C++ コードを含む場合、ANSI C++ string クラスを使用しています。
memcpy
の場合、ターゲットバッファが少なくともソースと同じであること、および両方のバッファがオーバーラップしていないことを確認します。Objective-C で書かれた iOS アプリは NSString クラスを使用しています。iOS 上の C アプリは、文字列の Core Foundation 表現である CFString を使用する必要があります。
信用できないデータを書式文字列に連結していません。
静的解析セキュリティテストに関する注意点
動的解析セキュリティテストに関する注意点
メモリ破損のバグは入力ファジングで最もよく見つかります。自動化されたブラックボックスソフトウェアテスト技法であり、不正なデータを継続的にアプリに送信して、潜在的な脆弱性の状態を調査します。このプロセスの間、アプリケーションは誤動作やクラッシュを監視されます。クラッシュが発生した場合、(少なくともセキュリティテスト技術者にとっての) 希望はクラッシュを引き起こす条件が悪用可能なセキュリティ上の欠陥を明らかにすることです。
ファズテスト技法やスクリプト (しばしば「ファザー」と呼ばれる) は通常、構造化された入力の複数のインスタンスを完全には正しくない形式で生成します。基本的に、生成された値や引数は少なくとも部分的にターゲットアプリケーションにより受け入れられ、また無効な要素も含み、潜在的に入力処理上の欠陥や予期しないプログラム動作を引き起こします。よいファザーはかなりの量の可能なプログラム実行パス (すなわち、高いカバレッジ出力) をさらけ出します。入力はスクラッチで生成される (「生成ベース」) か、既知の有効な入力データの変異から導出 (「変異ベース」) します。
バイナリ保護メカニズム
位置独立コード (Position Independent Code)
メモリ管理
自動参照カウント (Automatic Reference Counting)
ガベージコレクション (Garbage Collection)
手動メモリ管理 (Manual Memory Management)
スタックスマッシュ保護 (Stack Smashing Protection)
Last updated
Was this helpful?