ハードコードされたキーとシークレットの発見と検証
ハードコードされたシークレットは見つけやすく、機密データや特権アクセスへの入り口になり得ます。そのため、バグバウンティハンターや攻撃者にとって格好の標的です。
ハードコードされたシークレットキーは、バグバウンティハンターや攻撃者にとって手頃な標的です。見つけやすく、機密データや特権アクセスへの大きな入り口になり得るからです。
ハードコードされたシークレットは、過去にいくつもの注目を集めた侵害を引き起こしてきました。代表的なものは次のとおりです。
-
MyCar:MyCarは車両テレマティクスシステムです。メーカーは、AndroidおよびiOSのモバイルアプリ内に認証情報をハードコードしたまま残していました。その結果、数万台の車がハッカーに対して脆弱な状態となり、車の位置の特定、車両の識別、ロックの解除、エンジンの始動、アラームの作動が可能になっていました。
-
Uber:Uberの従業員が平文の認証情報を含むソースコードを公開し、それがGithubに投稿されました。攻撃者はGitHub上で埋め込まれた認証情報を見つけ、それを使ってUberのAmazon AWSインスタンスへの特権アクセスを取得しました。攻撃者はその後100k$の身代金を要求し、Uberはそれを支払いました。このUberの侵害により、5700万人の顧客と約600,000人のドライバーの情報が露出しました。この件が公になると、Uberは148M$の和解金を支払い、IPOを延期せざるを得なくなりました。
-
Uniguest:Uniguestは、ホテルのロビー(やその他の場所)に設置されるキオスク端末(PC、iMac、タブレット)を提供しています。ユーザーはこれを使って、Webの閲覧や搭乗券の印刷といった簡単な作業を行えます。アプリケーション内にAPIの認証情報がハードコードされており、それを使ってUniguestのクラウドデータベースの全データがダンプされました。そのデータには、管理者の認証情報、ルーターやBIOSのパスワード、プロダクトキーなど、さまざまな機密情報が含まれていました。
ハードコードされたシークレットは、バグバウンティハンターによってもよく報告されています。さまざまなバグバウンティプログラムのいずれかを確認すれば、AndroidManifest.xml、Info.plist、あるいは何らかのリソースファイル内のハードコードされたキーを指摘する、公開済みのレポートが数多く見つかるでしょう。
キーの権限や用途によっては、こうした脆弱性に最大1k$の報奨金が支払われることもあります。重大度は、権限昇格、機密情報へのアクセス、サービスの過剰請求や不正利用、サービス拒否攻撃の実行まで多岐にわたります。
シークレットを発見し検証するには
シークレットは、静的にも動的にも見つけることができます。一般的な静的アプローチは、既知のパターンを検索する方法です。たとえば、次の正規表現AIza[0-9A-Za-z\\-_]{35}に一致する文字列を検索します。
$ app8 egrep -r 'AIza[0-9A-Za-z\\-_]{35}' .
Binary file ./resources.arsc correspondent
Binary file ./app.apk correspondent
Binary file ./classes.dex correspondent
./assets/google-services-desktop.json: "current_key": "AIzaSy....................."
使用できる正規表現の一覧は、こちらのリポジトリl4yton/RegHexにあります。
すべてのAPIキーやシークレットが問題になったり危険だったりするわけではなく、パターンによる検出結果がすべて正しいわけでもありません。そのため、キーを確認したうえで、権限、ロール、スコープ、制限(これについては後述します)を列挙し、検証する必要があります。streaakは、幅広いキーを確認するためのcurlコマンドをまとめたリポジトリstreaak/keyhacksを公開しています。
以下は、FirebaseのキーとFacebookのシークレットの例です。
$ curl -s -X POST --header "Authorization: key=AIzaS........." --header "Content-Type:application/json" 'https://fcm.googleapis.com/fcm/send' -d '{"registration_ids":["1"]}'
<HTML>
<HEAD>
<TITLE>INVALID_KEY_TYPE</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>INVALID_KEY_TYPE</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
$ curl https://graph.facebook.com/oauth/access_token\?client_id\=51XXXX\&client_secret\=0cbd4XXXXX\&redirect_uri\=\&grant_type\=client_credentials
{"access_token":"5181XXXXXXXXXXXXXX","token_type":"bearer"}%
キーが見つかったら、そのキーにどのような権限があり、どのような操作を実行できるかを判断するうえで、APIドキュメントが最良の味方になります。たとえばFacebookアプリケーションの場合、https://graph.facebook.com/v8.0/{applicationId}/permissionsエンドポイントを使って権限を一覧表示できます。
$ curl "https://graph.facebook.com/v8.0/51XXXXXXXX/permissions?access_token=51XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
{"data":[{"permission":"email","status":"live"},{"permission":"pages_show_list","status":"live"},{"permission":"pages_messaging","status":"live"},{"permission":"groups_show_list","status":"live"},{"permission":"pages_read_engagement","status":"live"},{"permission":"public_profile","status":"live"}]}%
CLIで使いやすいもう一つのツールが、Yelpのdetect-secretです:Yelp/detect-secrets。このツールが検出するキーの種類はより少ないものの、一部のキーについては検証も実装されています。
Ostorlabは、キーやシークレットの発見と確認のプロセスを自動化しており、本記事の執筆時点で53種類のシークレットに対応しています。シークレットエージェントは、パターンに一致するキー、特定のAPIによって動的に使用されるキー、通信経路上で傍受されたキーを収集します。続いてエージェントは、既知のすべてのサービスを順に試して、それらのキーが有効かどうかを確認します。
以下は、有効なキーとそれに対応するサービスを確認した例を示すスクリーンショットです**。

直近にスキャンした1万件のアプリだけでも、決済やソースコードなど、非常に機密性の高いデータや重要なサービスへのアクセスを可能にする有効なシークレットを200件以上報告しています。以下は、サービスごとに見つかったキーの数の統計です。
| サービス名 | 件数 |
|---|---|
| Firebase | 35/1000 |
| Google Cloud Platform | 31/1000 |
| 22/1000 | |
| 21/1000 | |
| 18/1000 | |
| AWS | 18/1000 |
| GitHub | 14/1000 |
| PayPal | 5/1000 |
| slack | 1/1000 |
修正するには
- 埋め込んでも問題ない場合、対応は不要:ほとんどのサービスは、APIやシークレットの使い方に関するベストプラクティスを提供しています。APIの中には、アプリケーションに埋め込んでも問題ないものがあります。Firebaseがその一例です。
Unlike how API keys are typically used, API keys for Firebase services are not used to control access to backend resources;
that can only be done with Firebase Security Rules.
Usually, you need to fastidiously guard API keys (for example, by using a vault service or setting the keys as environment variables);
however, API keys for Firebase services are ok to include in code or checked-in config files.
- 代替手段が推奨されている場合は切り替えるべき:サービスによっては、認証情報を埋め込むよりも安全な代替手段を提供しています(AmazonやGoogleなど。以下はAWSの推奨事項の例です)。
You have a mobile app. Do not embed access keys with the app, even in encrypted storage.
Instead, use Amazon Cognito to manage user identities in your app. This service lets you authenticate users using Login with Amazon, Facebook, Google, or any OpenID Connect (OIDC)–compatible identity provider.
You can then use the Amazon Cognito credentials provider to manage credentials that your app uses to make requests to AWS. For more information, see Using the Amazon Cognito Credentials Provider on the AWS Mobile Blog.
- 埋め込みが危険な場合は、処理をサーバーに委ねる:サービスによっては、キーを埋め込むことはハッキングを招くようなものです。たとえばStripeのドキュメントを参照してください。このような場合は、サーバーがサービスとのやり取りを行うようにする必要があります。
Your secret API key can be used to make any API call on behalf of your account, such as creating charges or performing refunds.
Treat your secret API key as you would any other password. Grant access only to those who need it.
Ensure it is kept out of any version control system you may be using.
Control access to your key using a password manager or secrets management service.
In live mode, new secret keys are only visible the first time you access them.
After that, the Dashboard redacts the API key. When the key is revealed, you can leave a note on the Dashboard describing the location on your own systems where you’ve copied it.
If you lose your secret key, you can’t recover it from the Dashboard and must roll the key or create another one.
- リモートでのキー取得とキーピンニングによる堅牢化:ユーザーごとの認証サービスを提供しておらず、アプリケーションに埋め込む必要があるサービスについては、キーをサーバーから取得することをお勧めします。また、キーの権限を制限すること(最小権限の原則)もベストプラクティスです。サービスによっては、キーをアプリケーションやドメイン名にピン留め(PIN)できるものもあります。この保護は完全ではないため、露出を抑えるためにキーをローテーションすることをお勧めします。
