463 Stimmen

Bewährte Verfahren zum Speichern und Schützen privater API-Schlüssel in Anwendungen

Die meisten Entwickler von Anwendungen integrieren einige Bibliotheken von Drittanbietern in ihre Anwendungen. Sei es, um auf einen Dienst wie Dropbox oder YouTube zuzugreifen, oder um Abstürze zu protokollieren. Die Anzahl der Bibliotheken und Dienste von Drittanbietern ist atemberaubend. Die meisten dieser Bibliotheken und Dienste werden integriert, indem man sich auf irgendeine Weise beim Dienst authentifiziert, meistens geschieht dies über einen API-Schlüssel. Aus Sicherheitsgründen generieren die Dienste in der Regel einen öffentlichen und einen privaten, oft auch als geheim bezeichneten, Schlüssel. Leider muss dieser private Schlüssel zur Authentifizierung verwendet werden, um eine Verbindung zu den Diensten herzustellen, und ist daher wahrscheinlich Teil der Anwendung. Dies stellt natürlich ein großes Sicherheitsproblem dar. Öffentliche und private API-Schlüssel können innerhalb weniger Minuten aus APKs extrahiert werden und lassen sich leicht automatisieren.

Angenommen, ich habe etwas Ähnliches, wie kann ich den geheimen Schlüssel schützen?

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

}

Was ist Ihrer Meinung nach die beste und sicherste Art, den privaten Schlüssel zu speichern? Verschleierung, Verschlüsselung, was denken Sie?

16voto

Der App-Secret-Schlüssel sollte können sie von einigen Leuten umgedreht werden.

für diese Jungs wird es nicht verstecken, sperren Sie die entweder die ProGuard den Code. Es ist ein Refactor und einige bezahlte Obfuscators fügen ein paar bitweise Operatoren ein, um die jk433g34hg3 Zeichenfolge. Sie können 5 -15 min länger das Hacken machen, wenn Sie 3 Tage arbeiten :)

Am besten ist es, es so zu belassen, wie es ist.

Selbst wenn Sie den Schlüssel auf dem Server (Ihrem PC) speichern, kann er geknackt und ausgedruckt werden. Vielleicht dauert dies am längsten? Auf jeden Fall ist es eine Sache von wenigen Minuten oder im besten Fall ein paar Stunden.

Ein normaler Benutzer wird Ihren Code nicht dekompilieren.

16voto

Milad Faridnia Punkte 8644

Eine mögliche Lösung besteht darin, die Daten in Ihrer Anwendung zu kodieren und die Dekodierung zur Laufzeit durchzuführen (wenn Sie die Daten verwenden möchten). Ich empfehle auch die Verwendung von Progaurd, um es schwer zu machen, den dekompilierten Quellcode Ihrer App zu lesen und zu verstehen. Ich habe zum Beispiel einen verschlüsselten Schlüssel in die App eingefügt und dann eine Decode-Methode in meiner App verwendet, um meine geheimen Schlüssel zur Laufzeit zu decodieren:

// "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=="));
}

Der dekompilierte Quellcode einer überwachten Anwendung sieht so aus:

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

Zumindest ist es für mich kompliziert genug. Das ist die Art und Weise, wie ich es mache, wenn ich keine andere Wahl habe, als einen Wert in meiner Anwendung zu speichern. Natürlich wissen wir alle, Es ist nicht der beste Weg, aber es funktioniert für mich.

/**
 * @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";
}

Dekompilierte Version:

 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";
      }
    }
  }

und Sie können so viele Verschlüsselungsklassen mit einer kleinen Suche in Google finden.

13voto

ThePragmatist Punkte 496

Dieses Beispiel hat eine Reihe von Aspekten. Ich möchte ein paar Punkte erwähnen, die meiner Meinung nach an anderer Stelle nicht explizit behandelt wurden.

Schutz des Geheimnisses bei der Übermittlung

Zunächst einmal ist zu beachten, dass der Zugriff auf die Dropbox-API über deren App-Authentifizierung Mechanismus müssen Sie Ihren Schlüssel und Ihr Geheimnis übermitteln. Die Verbindung ist HTTPS, was bedeutet, dass Sie den Datenverkehr nicht abfangen können, ohne das TLS-Zertifikat zu kennen. Damit soll verhindert werden, dass eine Person die Pakete auf ihrem Weg vom Mobilgerät zum Server abfängt und mitliest. Für normale Nutzer ist dies eine wirklich gute Möglichkeit, die Privatsphäre ihres Datenverkehrs zu gewährleisten.

Was sie nicht kann, ist zu verhindern, dass eine böswillige Person die App herunterlädt und den Datenverkehr inspiziert. Es ist wirklich einfach, einen Man-in-the-Middle-Proxy für den gesamten Datenverkehr in und aus einem mobilen Gerät zu verwenden. Aufgrund der Beschaffenheit der Dropbox-API wäre in diesem Fall keine Disassemblierung oder Reverse Engineering des Codes erforderlich, um den Schlüssel und das Geheimnis der App zu extrahieren.

Sie könnten Folgendes tun Anheften das prüft, ob das vom Server erhaltene TLS-Zertifikat das erwartete ist. Dadurch wird der Client zusätzlich überprüft und es wird schwieriger, den Datenverkehr abzufangen. Dadurch wird es schwieriger, den Datenverkehr während des Fluges zu überprüfen, aber die Pinning-Prüfung findet im Client statt, so dass es wahrscheinlich immer noch möglich wäre, den Pinning-Test zu deaktivieren. Allerdings wird es dadurch schwieriger.

Schutz des Geheimnisses in der Ruhephase

Als ersten Schritt sollten Sie etwas wie proguard wird dazu beitragen, dass es weniger offensichtlich ist, wo Geheimnisse gehütet werden. Sie könnten auch das NDK verwenden, um den Schlüssel und das Geheimnis zu speichern und Anfragen direkt zu senden, was die Zahl der Personen, die über die entsprechenden Kenntnisse verfügen, um die Informationen zu extrahieren, erheblich reduzieren würde. Eine weitere Verschleierung kann dadurch erreicht werden, dass die Werte nicht für längere Zeit direkt im Speicher gespeichert werden, sondern, wie in einer anderen Antwort vorgeschlagen, verschlüsselt und erst kurz vor der Verwendung entschlüsselt werden.

Erweiterte Optionen

Wenn Sie jetzt paranoid sind, weil Sie das Geheimnis irgendwo in Ihrer Anwendung platzieren wollen, und Sie Zeit und Geld haben, um in umfassendere Lösungen zu investieren, dann könnten Sie in Erwägung ziehen, die Anmeldedaten auf Ihren Servern zu speichern (vorausgesetzt, Sie haben welche). Dies würde die Latenzzeit bei Aufrufen der API erhöhen, da diese über Ihren Server kommunizieren muss, und könnte die Kosten für den Betrieb Ihres Dienstes aufgrund des erhöhten Datendurchsatzes erhöhen.

Sie müssen dann entscheiden, wie Sie am besten mit Ihren Servern kommunizieren, um sicherzustellen, dass sie geschützt sind. Dies ist wichtig, um zu verhindern, dass die gleichen Probleme mit Ihrer internen API erneut auftreten. Die beste Faustregel, die ich geben kann, ist, wegen der Man-in-the-Middle-Bedrohung keine Geheimnisse direkt zu übertragen. Stattdessen können Sie den Datenverkehr mit Ihrem Geheimnis signieren und die Integrität aller an Ihren Server gerichteten Anfragen überprüfen. Eine Standardmethode hierfür ist die Berechnung eines HMAC der Nachricht, die mit einem Geheimnis verschlüsselt ist. Ich arbeite in einem Unternehmen, das ein Sicherheitsprodukt anbietet, das auch in diesem Bereich tätig ist, weshalb mich diese Art von Dingen interessiert. In der Tat, hier ist ein Blog Artikel von einem meiner Kollegen, in dem das meiste davon behandelt wird.

Wie viel sollte ich tun?

Bei allen Sicherheitsratschlägen dieser Art müssen Sie eine Kosten-Nutzen-Entscheidung darüber treffen, wie schwer Sie es jemandem machen wollen, einzubrechen. Wenn Sie eine Bank sind, die Millionen von Kunden schützt, haben Sie ein ganz anderes Budget zur Verfügung als jemand, der in seiner Freizeit eine App unterstützt. Es ist praktisch unmöglich, jemanden daran zu hindern, Ihre Sicherheitsvorkehrungen zu knacken, aber in der Praxis brauchen nur wenige Menschen all den Schnickschnack, und mit einigen grundlegenden Vorsichtsmaßnahmen kommen Sie schon sehr weit.

9voto

user1611728 Punkte 446

Was auch immer Sie tun, um Ihre geheimen Schlüssel zu sichern, es wird keine echte Lösung sein. Wenn der Entwickler die Anwendung dekompilieren kann, gibt es keine Möglichkeit, den Schlüssel zu schützen. Das Verstecken des Schlüssels ist nur Sicherheit durch Unklarheit, ebenso wie die Verschleierung des Codes. Das Problem bei der Sicherung eines geheimen Schlüssels ist, dass man einen anderen Schlüssel verwenden muss, um ihn zu sichern, und dieser Schlüssel muss ebenfalls gesichert werden. Stellen Sie sich einen Schlüssel vor, der in einer Kiste versteckt ist, die mit einem Schlüssel verschlossen ist. Sie stellen eine Schachtel in einen Raum und schließen den Raum ab. Sie haben noch einen weiteren Schlüssel, den Sie sichern müssen. Und dieser Schlüssel wird immer noch in Ihrer Anwendung fest kodiert sein.

Es gibt also keine Möglichkeit, den Schlüssel zu verbergen, es sei denn, der Benutzer gibt eine PIN oder eine Phrase ein. Dazu müsste man aber ein Schema für die Verwaltung von PINs haben, die außerhalb des Bandes, d. h. über einen anderen Kanal, erfolgt. Für die Sicherung von Schlüsseln für Dienste wie Google-APIs ist das sicherlich nicht praktikabel.

7voto

Bernard Igiri Punkte 1272

Die sicherste Lösung ist, die Schlüssel auf einem Server zu speichern und alle Anfragen, die diesen Schlüssel benötigen, über den Server zu leiten. Auf diese Weise verlässt der Schlüssel niemals Ihren Server, und solange Ihr Server sicher ist, ist es auch Ihr Schlüssel. Diese Lösung ist natürlich mit Leistungseinbußen verbunden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X