web-dev-qa-db-ja.com

アプリケーションでプライベートAPIキーを保存および保護するためのベストプラクティス

ほとんどのアプリ開発者は、いくつかのサードパーティ製ライブラリを自分のアプリに統合します。 DropboxやYouTubeなどのサービスにアクセスしたり、ログ記録にクラッシュしたりする場合。サードパーティのライブラリとサービスの数は驚異的です。これらのライブラリとサービスのほとんどは、なんらかの方法でサービスを認証することによって統合されています。ほとんどの場合、これはAPIキーを通じて行われます。セキュリティ上の理由から、サービスは通常パブリックキーとプライベートキーを生成します。これらはしばしば秘密キーとも呼ばれます。残念ながら、サービスに接続するには、この秘密鍵を認証に使用する必要があるため、おそらくアプリケーションの一部になります。言うまでもなく、これは非常に大きなセキュリティ問題に直面しています。公開および非公開のAPIキーはほんの数分でAPKから抽出でき、簡単に自動化できます。

これに似たものがあると仮定して、秘密鍵を保護するにはどうすればよいですか。

public class DropboxService  {

    private final static String APP_KEY = "jk433g34hg3";
    private final static String APP_SECRET = "987dwdqwdqw90";
    private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;

    // SOME MORE CODE HERE

}

あなたの意見では、秘密鍵を保管するための最善かつ最も安全な方法は何ですか?難読化、暗号化、あなたはどう思いますか?

313
Basic Coder
  1. そのままでは、コンパイル済みアプリケーションにはキー・ストリングが含まれていますが、定数名APP_KEYおよびAPP_SECRETも含まれています。このような自己文書化コードからキーを抽出するのは簡単です。たとえば、標準のAndroidツールdxの場合です。

  2. ProGuardを適用することができます。キー文字列はそのまま残りますが、定数名は削除されます。また、可能な限り、クラスやメソッドの名前を短く意味のない名前に変更します。どの文字列がどの目的に役立つかを判断するには、キーの抽出にさらに時間がかかります。

    ProGuardの設定は、あなたが恐れているほど難しくはありません。はじめに、project.propertiesに記載されているように、ProGuardを有効にするだけです。他社製ライブラリに問題がある場合は、proguard-project.txtでいくつかの警告を抑制したり難読化したりしないようにする必要があるかもしれません。例えば、

    -dontwarn com.dropbox.**
    -keep class com.dropbox.** { *; }
    

    これは強引なアプローチです。処理されたアプリケーションが動作したら、そのような設定を改良することができます。

  3. 例えばBase64エンコーディングやできればもっと複雑なコードを使用して、コード内で文字列を手動で難読化することができます。ネイティブコードでさえも。ハッカーはそれからあなたの符号化を静的にリバースエンジニアリングするか、適切な場所で復号化を動的に傍受しなければならないでしょう。

  4. ProGuardの特別な兄弟 DexGuard のように、市販の難読化ツールを適用できます。それはさらにあなたのために文字列とクラスを暗号化/難読化することができます。キーを抽出すると、さらに時間と専門知識がかかります。

  5. アプリケーションの一部を自分のサーバー上で実行できる可能性があります。あなたがそこに鍵を保管することができれば、それらは安全です。

結局のところ、それはあなたがしなければならない経済的なトレードオフです。鍵がどれほど重要であるか、どれだけの時間またはソフトウェアを買う余裕があるか、キーをハッキングするまでにどれだけの時間を費やすか、どの程度の規模で成功するハッカーがキーを配布するかなど。キーのような小さな情報は、アプリケーション全体より保護するのが困難です。本質的には、クライアントサイドで破ることができるものは何もありませんが、あなたは確かにバーを上げることができます。

(私はProGuardとDexGuardの開発者です)

313
Eric Lafortune

いくつかのアイデアは、私の考えでは最初の1つだけがある程度の保証を与えるものです。

  1. あなたの秘密をインターネット上のサーバーに保存し、必要なときにそれらをつかんで使うだけです。ユーザーがdropboxを使用しようとしているのであれば、あなたがあなたのサイトにリクエストを出してあなたの秘密鍵を取得するのを妨げるものは何もありません。

  2. あなたのライブラリーをより大きくそして逆コンパイルすることをより困難にするためにjniコードにあなたの秘密を入れて、いくつかの可変コードを加えなさい。キーストリングをいくつかの部分に分割してさまざまな場所に保管することもできます。

  3. 難読化ツールを使用し、コードをハッシュ化して秘密にし、後で使用する必要があるときにはハッシュを解除します。

  4. あなたの秘密鍵をあなたの画像の最後のピクセルとしてアセットに入れてください。それから必要なときにあなたのコードでそれを読んでください。コードを難読化すると、それを読み取るコードを隠すのに役立ちます。

Apk codeを読むのがいかに簡単かをすぐに見たい場合は、APKAnalyserを入手してください。

http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/

68
marcinj

API /秘密鍵を保護するための3つの簡単なステップに従ってください

Gradleを使用してAPIキーまたは秘密キーを保護することができます。

1。 gradle.properties(プロジェクトプロパティ):キーを使って変数を作成します。

GoolgeAPIKey = "Your API/Secret Key"

2。 build.gradle(Module:app):build.gradleに変数を設定してアクティビティまたはフラグメントでアクセスします。以下のコードをbuildTypes {}に追加してください。

buildTypes.each {
    it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}

3。アプリのBuildConfigによるActivity/Fragmentでアクセスします。

BuildConfig.GoogleSecAPIKEY

更新:

上記の解決策はGitを使ってコミットするオープンソースプロジェクトに役立ちます。 (David Rawsonとriyaz-ALiにコメントをありがとう)。

MatthewとPablo Cegarraのコメントによれば、上記の方法は安全ではなく、Decompilerは誰かが私たちの秘密鍵でBuildConfigを見ることを許可するでしょう。

解決策:

APIキーを保護するためにNDKを使用できます。キーをネイティブのC/C++クラスに格納し、それらをJavaクラスでアクセスすることができます。

NDKを使用してAPIキーを保護するには、 this blogに従ってください。

トークンを安全にAndroidに保存する方法のフォローアップ

19
SANAT

もう1つの方法は、そもそもデバイスに秘密を持たないことです。 モバイルAPIセキュリティ技術 (特にパート3)を参照してください。

昔ながらの間接化の伝統を使用して、APIエンドポイントとアプリ認証サービスの間で秘密を共有します。

あなたのクライアントがAPI呼び出しを行おうとするとき、それは(強いリモート認証技術を使って)それを認証するようにアプリ認証サービスに要求し、そしてそれは時間制限を受けます(通常JWT)シークレットによって署名されたトークン。

トークンは各API呼び出しとともに送信され、エンドポイントは要求に応じる前にその署名を検証できます。

実際の秘密はデバイスには存在しません。実際、アプリが有効であるかどうかはまったくわかりません。リクエスト認証が実行され、結果のトークンが渡されます。間接的な方法の利点として、秘密を変更したい場合は、ユーザーがインストール済みのアプリケーションを更新しなくても変更できます。

あなたの秘密を保護したいのであれば、そもそもそれをあなたのアプリの中に持っていないのが良い方法です。

15
Skip Hovsmith

考えられる解決策の1つは、アプリ内のデータをエンコードし、実行時にそのデータを使用する場合はデコードを使用することです。また、progaurdを使用して、アプリケーションの逆コンパイルされたソースコードを読み、理解しにくくすることをお勧めします。たとえば、私はアプリにエンコードされたキーを入れてから、実行時に自分の秘密キーをデコードするためにアプリのdecodeメソッドを使用しました。

// "the real string is: "mypassword" "; 
//encoded 2 times with an algorithm or you can encode with other algorithms too
public String getClientSecret() {
    return Utils.decode(Utils
            .decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}

推奨されるアプリの逆コンパイルされたソースコードは次のとおりです。

 public String c()
 {
    return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
  }

少なくとも私にとっては十分に複雑です。これは、自分のアプリケーションに値を格納する以外に選択肢がない場合に行う方法です。もちろん、それは最善の方法ではありませんが、それは私には有効です。

/**
 * @param input
 * @return decoded string
 */
public static String decode(String input) {
    // Receiving side
    String text = "";
    try {
        byte[] data = Decoder.decode(input);
        text = new String(data, "UTF-8");
        return text;
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return "Error";
}

デコンパイル版

 public static String a(String paramString)
  {
    try
    {
      str = new String(a.a(paramString), "UTF-8");
      return str;
    }
    catch (UnsupportedEncodingException localUnsupportedEncodingException)
    {
      while (true)
      {
        localUnsupportedEncodingException.printStackTrace();
        String str = "Error";
      }
    }
  }

そして、Googleで少し検索するだけで、非常に多くの暗号化クラスを見つけることができます。

13
Milad Faridnia

App-Secretキーは非公開にしておく必要があります - ただし、アプリをリリースするときには、一部の人が元に戻すことができます。

非表示にしない人にとっては、ProGuardのどちらかのコードをロックしてください。これはリファクタリングであり、一部の有料の難読化ツールは、jk433g34hg3 Stringを取り戻すためにいくつかのビット演算子を挿入しています。 3日間仕事をすれば5〜15分長くハッキングすることができます:)

最善の方法はそれを現状のままにしておくことです、私見。

たとえあなたがサーバー側(あなたのPC)に保管したとしても、鍵はハッキングされそして印刷されることができます。多分これは最長かかりますか?とにかく、それは最善の場合数分または数時間の問題です。

通常のユーザーはあなたのコードを逆コンパイルしません。

10
user529543

この例には、さまざまな側面があります。他の場所で明示的に説明されているとは思わない点について、いくつか言及します。

送信中の秘密を保護する

最初に注意することは、 アプリ認証 メカニズムを使用してdropbox APIにアクセスするには、鍵と秘密を送信する必要があるということです。接続はHTTPSです。つまり、TLS証明書を知らないとトラフィックを傍受することはできません。これは、人がモバイルデバイスからサーバーへの移動中にパケットを傍受して読み取るのを防ぐためです。通常のユーザーにとっては、トラフィックのプライバシーを保護するための本当に良い方法です。

それが得意ではないのは、悪意のある人がアプリをダウンロードしてトラフィックを検査するのを防ぐことです。モバイルデバイスに出入りするすべてのトラフィックに中間者プロキシを使用するのは本当に簡単です。この場合、Dropbox APIの性質上、アプリのキーとシークレットを抽出するためにコードを分解したりリバースエンジニアリングする必要はありません。

サーバーから受け取ったTLS証明書があなたが期待しているものであることを確認する ピン留め を行うことができます。これはクライアントにチェックを追加し、トラフィックを傍受することをより困難にします。これは飛行中のトラフィックを検査することを難しくしますが、ピン留めチェックはクライアントで行われます、従ってピン留めテストをディセーブルにすることはそれでも可能性があるでしょう。それは難しいですが。

安静時の秘密を守る

最初のステップとして、 proguard のようなものを使用すると、秘密がどこに保持されているかがわかりにくくなります。 NDKを使用してキーとシークレットを保存し、要求を直接送信することもできます。これにより、情報を抽出するための適切なスキルを持つ人々の数が大幅に削減されます。値をメモリに直接保存しないことで、さらに難読化することができます。別の回答で示唆されているように、使用する直前に値を暗号化して復号化できます。

より高度なオプション

あなたが今、あなたのアプリのどこかに秘密を置くことに妄想していて、あなたがより包括的なソリューションに投資する時間とお金があるなら、あなたはあなたがあなたのサーバーに資格を保存することを考えるかもしれませんサーバーを介して通信する必要があるため、APIへの呼び出しの待ち時間が長くなり、データのスループットが向上するため、サービスを実行するためのコストが増加する可能性があります。

その後、サーバーを確実に保護するために、サーバーとの通信方法を決定する必要があります。これは、同じ問題が内部APIで再び発生するのを防ぐために重要です。私が与えることができる最もよい経験則は、中間者の脅威のために直接秘密を送信しないことです。代わりに、あなたはあなたの秘密を使ってトラフィックに署名し、あなたのサーバーに来るリクエストの完全性を検証することができます。これを行うための1つの標準的な方法は、秘密をキーとするメッセージのHMACを計算することです。私はこの分野でも動作するセキュリティ製品を持っている会社で働いています、それがこの種のものが私の興味を引く理由です。実のところ、これは私の同僚の一人による ブログ の記事で、これの大部分を解説しています。

いくらすればいいですか?

このようなセキュリティ上のアドバイスがあれば、誰かが侵入するのをどの程度困難にするかについて、費用対効果の判断を下す必要があります。余暇。誰かがあなたのセキュリティを破るのを防ぐことは事実上不可能ですが、実際にはすべての鐘と笛を必要とする人はほとんどいません。

9
ThePragmatist

@Manohar Reddyソリューション、firebaseデータベース、またはfirebase RemoteConfig(デフォルト値はNull)に追加することができます。

  1. 鍵を暗号化する
  2. Firebaseデータベースに保存する
  3. アプリの起動時または必要に応じて入手する
  4. 鍵を解読して使用する

この解決策の違いは何ですか?

  • firebaseのクレジットはありません
  • firebaseのアクセスは保護されているので、署名された証明書を持つアプリだけがAPI呼び出しを行う特権を持ちます。
  • 中間者の傍受を防ぐための暗号化/解読しかしすでにfirebaseにhttpsを呼び出している
8
Ayman Al-Absi

最も安全な解決策は、あなたの鍵をサーバに保持し、その鍵を必要とするすべてのリクエストをあなたのサーバを通してルーティングすることです。そうすれば、鍵があなたのサーバを離れることはありません、あなたのサーバが安全である限り、あなたの鍵もそうです。もちろん、このソリューションにはパフォーマンス上のコストがあります。

7
Bernard Igiri

古い投稿ですが、それでも十分です。 NDKとC++を使用しているので、.soライブラリに隠すのは素晴らしいことだと思います。 .soファイルは16進エディタで見ることができますが、幸運にもそれを逆コンパイルします:P

4
Ahmed Awad

秘密鍵を保護するために何をしても、本当の解決策にはなりません。開発者がアプリケーションを逆コンパイルできる場合、キーを保護する方法はありません。キーを隠すことは、あいまいさによるセキュリティだけであり、コードの難読化も同様です。秘密鍵を保護することの問題は、秘密鍵を保護するためには別の鍵を使用しなければならず、その鍵も保護する必要があるということです。鍵でロックされている箱の中に隠された鍵を考えてみてください。箱を部屋の中に置いて部屋をロックします。あなたは安全のために別の鍵を持っています。そしてそのキーはあなたのアプリケーションの中にまだハードコードされているでしょう。

そのため、ユーザーがPINまたはフレーズを入力しない限り、キーを隠すことはできません。しかしそのためには、帯域外で発生しているPINを管理するためのスキームが必要になります。つまり、別のチャネルを介して行われます。 Google APIのようなサービスのためのキーを保護するために確かに実用的ではありません。

4
user1611728

秘密をfirebase databaseに入れて、アプリの起動時に取得します。Webサービスを呼び出すよりもはるかに優れています。

4
Manohar Reddy

これらを非公開にする唯一の本当の方法はそれらをあなたのサーバー上に保ち、それがサーバーにあるものなら何でもアプリに送信させ、そしてサーバーはDropboxと相互作用することです。そうすれば、あなたは秘密鍵をいかなる形式でも配布することができません。

3
Nick

苦い経験に基づき、IDA-Proの専門家と相談した後の最善の解決策は、コードの重要な部分をDLL/SharedObjectに移動し、それをサーバーから取り出して実行時にロードすることです。

機密データは次のようなことをするのがとても簡単なのでエンコードする必要があります。

$ strings libsecretlib.so | grep My
  My_S3cr3t_P@$$W0rD
2
RonTLV