- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存
同じ「Top 10」でも数えたものが違います。今回はモバイルの一覧が何を数えたかを読んでから、その1位の中身を手元のバイナリで確かめます。
まず結論
OWASP Mobile Top 10 2024 の M1 は、アプリのコードや設定ファイルに埋め込んだ資格情報(hardcoded credentials)を脆弱性の第一の兆候に挙げます。配布したバイナリは受け取った全員の手元にあり、埋めた鍵は汎用の文字列抽出ツール1つで平文のまま出てきます。
strip も minify も鍵を消さず、XOR で隠しても復号手順ごと配っています。鍵を埋めない限り、読める人の範囲は変わりません。
鍵を文字列としてコードに書き、`gcc -O2` でビルドしてから `strip` でシンボルを削った実行ファイルがあります。`strings` で文字列を抜き出すと、鍵はどう見えるでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
この一覧は何を数えたか、そして1位は何を言うか
番号を読む前に、何を数えた一覧かを確かめます。Mobile Top 10 の方法論は、自分を a data-based approach and unbiased sources によるものと説明し、データ源を incident reports, vulnerability databases, and security assessments と書きます。つまり事故報告・脆弱性データベース・診断結果です。具体例の列挙には Surveys of security professionals or application developers も入っています。分析に使いうる指標としては、発生頻度・影響の深刻さ・悪用の難しさなどを挙げます。ただし、集めた件数や項目ごとの値は公開されていません(確認範囲: index.md・tab_updates.md・2023-risks/methodology.md)。各項目に付いている EASY・COMMON・SEVERE は段階のラベルで、数値ではありません。
公開は二段構えです。方法論の Progress Report の表は Initial Release の日付を Aug 2, 2023 とし、2023-risks/methodology.md の同じ表は Final Release の日付を Jan 22, 2024 とします。トップページに Final release 2024 の表記が入ったのは、git の履歴で 2024-01-20 です。項目の本文は今も 2023-risks/ の下にあります。
その1位、M1: Improper Credential Usage は、脆弱性の兆候の第一にこれを挙げます。
Hardcoded Credentials - If the mobile app contains hardcoded credentials within the app's source code or any configuration files, this is a clear indicator of vulnerability.
理由は技術というより配布の構造にあります。配ったバイナリは、受け取った全員の手元にある同じ物です。 鍵を埋めれば、その鍵を読める人は「アプリを入手した全員」になります。秘密ごとに読める人を一覧にする見方は、第24期で扱います。
- ・直す向き: 共有の鍵はサーバに置き、端末には取り消せる値だけを渡す(M1 の How Do I Prevent の方向。本回では実装していない)
埋めた鍵を、strings で抜き出す
環境は macOS 26.5.2(arm64)上の Docker 28.4.0、イメージ gcc:13(gcc 13.5.0・GNU strings と objdump 2.40)です。鍵は実在しないダミー文字列です。同じ手順を2回走らせ、出力は完全に一致しました。
#include <stdio.h>/* DUMMY credential embedded at build time (NOT a real secret) */static const char *API_KEY = "DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE";int main(int argc, char **argv) {/* realistic: the app puts the key in a request header at runtime */printf("Authorization: Bearer %s\n", API_KEY);(void)argc; (void)argv;return 0;}
gcc -O2 でビルドし、strip -o embedded_stripped embedded でシンボルを削った版も作ります。それぞれから strings で文字列を抜き出し、grep で探します。
$ ./embeddedAuthorization: Bearer DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE$ strings embedded | grep -n DUMMY11:DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE(grep exit=0)$ strings embedded_stripped | grep -n DUMMY11:DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE(grep exit=0)$ stat -c "%s %n" embedded embedded_stripped70480 embedded67560 embedded_stripped
11: は strings 出力の行番号です。strip の前も後も、同じ11行目に同じ鍵が出ました。 ファイルは 2920 バイトしか減っていません。削られたのはシンボルで、プログラムが実行時に使う文字列は残ります。使う以上、消せません。
strip の前後でファイルは 70480 バイトから 67560 バイトになり、鍵は同じ行に残りました。ここから言えることはどれですか。
隠す加工は、読める人の範囲を変えない
この一覧の限定も確かめておきます。トップページは、初回の一覧に入らなかったものとして Hardcoded Secrets を挙げています。
Vulnerabilities that didn't make the place on the initial release list, but in the future, we may consider them.
ただし M1 の本文は hardcoded credentials を正面から扱っています。**「独立の項目としては入らなかった」**までが言えることで、「Top 10 で扱われていない」とは書けません。
では、鍵を読みにくく加工すればよいのでしょうか。M7: Insufficient Binary Protection は、難読化などの追加の防御についてこう書きます。
If there are additional protective measures, like obfuscation, encoding of secrets in native code (for Android) or similar, successful attacks become harder to achieve but never impossible.
同じ M7 は no fully reliable mechanisms to prevent binary attacks とも書きます。難しくはできても、不可能にはできないという位置づけです。アプリ自身が鍵を使う以上、使える形に戻す手順も同じ配布物に入っています。
逆向きの落とし穴もあります。鍵を使わないコードを -O2 でビルドすると、コンパイラが未使用の文字列を捨て、grep が空振りします(gcc:13 で確認)。この回の検証でも、最初の XOR 版は復号結果を使わない作りだったため、符号化した並びごと消えていました。「grep に出ない」は「鍵を埋めていない」の証明になりません。 確かめる対象は配布物ではなく、M1 が言うとおりソースと設定ファイルです。
実行時に読む版と、XOR で隠した版
同じ環境で、鍵を埋めない2つの版を比べます。1つ目は getenv("API_KEY") で実行時に環境から読む版、2つ目は同じ鍵を XOR 0x5A で符号化して埋め、実行時に復号して embedded.c と同じくヘッダに載せる版です。復号した結果を実際に使う作りにしてあります。
$ strings envbased | grep -n DUMMY(grep exit=1)$ strings envbased | grep -n API_KEY13:API_KEY$ strings obfuscated | grep -n DUMMY(grep exit=1)$ objdump -s -j .rodata obfuscatedobfuscated: file format elf64-littleaarch64Contents of section .rodata:400720 01000200 00000000 41757468 6f72697a ........Authoriz400730 6174696f 6e3a2042 65617265 72202573 ation: Bearer %s400740 0a000000 00000000 00000000 00000000 ................400750 1e0f1717 03111f03 7736332c 3f05633c ........w63,?.c<400760 62386d39 6c3e6f3f 6e3b6968 6b6a051e b8m9l>o?n;ihkj..400770 15051415 0e050f09 1f00 ..........$ ./obfuscatedAuthorization: Bearer DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE
実行時に読む版のバイナリには、変数名 API_KEY しか入っていません。ただし、これは配布物に鍵が無いというだけです。配った端末で動かすなら、その値を渡される先はやはり受け取った人の手元です。 実行時に読む形が効くのは、実行する場所が自分のサーバのときです。
XOR 版は平文の grep を逃れました。しかし objdump で読み取り専用データ(.rodata)を出すと、0x400750 からの 42 バイトがソースの符号化した並び(鍵 41 文字と終端の 0)とそのまま一致します。実行すれば平文の鍵が出ます。隠した並びも、戻す手順も、同じ実行ファイルに入っています。
鍵を XOR 0x5A で符号化した版は、strings と grep では平文が見つからず、実行すると平文の鍵を載せたヘッダを出力しました。M7 の原文に照らした読み方として適切なのはどれですか。
ネイティブのバイナリ
C で書いて gcc -O2 でビルドし、strip した実行ファイル。strings と grep で、strip の前後とも同じ行に鍵が出ました。
$ strings embedded_stripped | grep -n DUMMY11:DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE(grep exit=0)
Web のフロントエンドのバンドル
同じ鍵を JS に書き、esbuild 0.28.2 の --bundle --minify で束ねた 148 バイトのバンドル(node:24-alpine、node v24.21.0)。変数名は縮みましたが、鍵は平文のまま残りました。
$ wc -c < bundle.js148$ cat bundle.jsvar e="DUMMYKEY-live_9f8b7c6d5e4a3210_DO_NOT_USE";function a(){return fetch("/api",{headers:{Authorization:"Bearer "+e}})}export{a as callBackend};
モバイルアプリとブラウザ向けの JS。実行する場所も言語も違う2つに共通するのは何でしょうか。
その配布物のコピーを誰が持っているかを考えてみてください。
この章のまとめ
- Mobile Top 10 2024 は事故報告・脆弱性データベース・診断結果を集めたと書くが、件数や項目ごとの値は公開していない。公開は初版 2023-08-02、方法論の表で Final Release は 2024-01-22。
- M1 は hardcoded credentials を脆弱性の兆候の第一に挙げる。Hardcoded Secrets は独立の項目としては入らなかった。
- 配布物は受け取った全員の手元にある。strip 後も同じ行に鍵が出て、minify したバンドルにも平文で残った。
- XOR で隠しても、符号化した並びと復号手順が同じ実行ファイルに残る。M7 は
harder to achieve but never impossible。難読化は補助。 - grep の空振りは証明にならない。未使用の文字列は
-O2で消える。確かめる対象はソースと設定。 - 直す向きは、共有の鍵はサーバに置き、端末には取り消せるトークンだけを渡すこと。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
Mobile Top 10 2024 での Hardcoded Secrets の扱いとして、原文に合うのはどれですか。
用語集(3語)
- hardcoded credentials
- コードや設定ファイルに書き込まれた資格情報。M1 は脆弱性の兆候の第一に挙げ、予防策の見出しを Avoid Using Hardcoded Credentials とする。
- strip
- 実行ファイルからシンボル情報を削るコマンド。プログラムが使う文字列データは残る。
- 難読化 (obfuscation)
- 配布物を読みにくくする加工。M7 は、攻撃を難しくするが不可能にはしない補助として扱う。
出典(一次ソース)
- OWASP Mobile Top 10 2024 — M1: Improper Credential Usage(www-project-mobile-top-10 コミット f2dc2d6、取得 2026-09-28) ↗
- OWASP Mobile Top 10 2024 — M7: Insufficient Binary Protection(コミット f2dc2d6、取得 2026-09-28) ↗
- OWASP Mobile Top 10 — プロジェクトのトップ(一覧・didn't make the place のリスト。コミット f2dc2d6、取得 2026-09-28) ↗
- OWASP Mobile Top 10 — Methodology(Updates タブ。データ源・Progress Report。コミット f2dc2d6、取得 2026-09-28) ↗
- OWASP Mobile Top 10 2024 — 2023-risks/methodology.md(Progress Report に Final Release の行。コミット f2dc2d6、取得 2026-09-28) ↗