Wie kann ich den MySQL-Benutzernamen und das Passwort vor dem Dekompilieren schützen?

87

Java- .classDateien können ziemlich einfach dekompiliert werden. Wie kann ich meine Datenbank schützen, wenn ich die Anmeldedaten im Code verwenden muss?

Jakob Cosoroaba
quelle
Ich hoffe, es macht Ihnen nichts aus, dass ich Ihre Frage erneut beantwortet habe. Ich habe "Benutzername" und "Passwort" entfernt und "Reverse Engineering" und "Dekompilieren" hinzugefügt. Ich denke, sie sind aussagekräftiger als die Originale. Übrigens eine ausgezeichnete Frage zu den Grundlagen!
William Brendel
6
Beachten Sie, dass die Tatsache, dass Sie Java verwenden, hier nicht wirklich relevant ist. Fest codierte Passwörter in jeder Sprache sind auf die gleiche Weise problematisch ("strings thebinary" zeigt auch String-Konstanten in C-Programmen an).
Joachim Sauer
@saua: Stimmt, aber vielleicht veröffentlicht jemand einen Beispielcode zum Entkoppeln von Benutzernamen und Passwörtern in Java. Ich könnte das sogar selbst tun, wenn ich Zeit habe.
William Brendel
1
Ich habe festgestellt, dass viele Antworten glauben, dass Sie versuchen, den Benutzernamen / die Passwörter vor einem nicht autorisierten Benutzer zu verbergen, während der Benutzer, der die App ausführt, in Ordnung ist. Ich glaube, Sie möchten das Passwort vor JEDEM verbergen . Bitte klären Sie dies in der Frage.
Pek
1
Angenommen, die Anmeldeinformationen werden verwendet, um eine Verbindung zu einem Server herzustellen, auf dem sich nur die Anwendung anmelden kann.
Pek

Antworten:

122

Geben Sie niemals Passwörter fest in Ihren Code ein. Dies wurde kürzlich in den Top 25 der gefährlichsten Programmierfehler erwähnt :

Das Hardcodieren eines geheimen Kontos und eines Passworts in Ihre Software ist äußerst praktisch - für erfahrene Reverse Engineers. Wenn das Passwort für Ihre gesamte Software gleich ist, wird jeder Kunde verwundbar, wenn dieses Passwort unweigerlich bekannt wird. Und weil es fest codiert ist, ist es ein großer Schmerz, den man beheben muss.

Sie sollten Konfigurationsinformationen, einschließlich Kennwörter, in einer separaten Datei speichern, die die Anwendung beim Start liest. Dies ist der einzige wirkliche Weg, um zu verhindern, dass das Passwort infolge der Dekompilierung verloren geht (kompilieren Sie es zunächst nie in die Binärdatei).

Weitere Informationen zu diesem häufigen Fehler finden Sie im Artikel CWE-259 . Der Artikel enthält eine gründlichere Definition, Beispiele und viele andere Informationen zum Problem.

In Java ist die Verwendung der Preferences-Klasse eine der einfachsten Möglichkeiten, dies zu tun. Es dient zum Speichern aller Arten von Programmeinstellungen, von denen einige einen Benutzernamen und ein Passwort enthalten können.

import java.util.prefs.Preferences;

public class DemoApplication {
  Preferences preferences = 
      Preferences.userNodeForPackage(DemoApplication.class);

  public void setCredentials(String username, String password) {
    preferences.put("db_username", username);
    preferences.put("db_password", password);
  }

  public String getUsername() {
    return preferences.get("db_username", null);
  }

  public String getPassword() {
    return preferences.get("db_password", null);
  }

  // your code here
}

Im obigen Code können Sie die setCredentialsMethode aufrufen , nachdem Sie in einem Dialogfeld nach dem Benutzernamen und dem Kennwort gefragt haben. Wenn Sie eine Verbindung zur Datenbank herstellen müssen, können Sie einfach die Methoden getUsernameund getPasswordverwenden, um die gespeicherten Werte abzurufen. Die Anmeldeinformationen werden nicht fest in Ihre Binärdateien codiert, sodass die Dekompilierung kein Sicherheitsrisiko darstellt.

Wichtiger Hinweis: Die Voreinstellungsdateien sind nur XML-Dateien im Nur-Text-Format. Stellen Sie sicher, dass Sie geeignete Maßnahmen ergreifen, um zu verhindern, dass nicht autorisierte Benutzer die Rohdateien anzeigen (UNIX-Berechtigungen, Windows-Berechtigungen usw.). Zumindest unter Linux ist dies kein Problem, da beim Aufruf Preferences.userNodeForPackagedie XML-Datei im Home-Verzeichnis des aktuellen Benutzers erstellt wird, die von anderen Benutzern ohnehin nicht gelesen werden kann. In Windows kann die Situation anders sein.

Wichtigere Hinweise: In den Kommentaren dieser und anderer Antworten wurde viel darüber diskutiert, welche Architektur für diese Situation richtig ist. In der ursprünglichen Frage wird der Kontext, in dem die Anwendung verwendet wird, nicht wirklich erwähnt, daher werde ich über die beiden Situationen sprechen, die mir einfallen. Der erste Fall ist der Fall, in dem die Person, die das Programm verwendet, die Datenbankanmeldeinformationen bereits kennt (und dazu berechtigt ist). Der zweite Fall ist der Fall, in dem Sie als Entwickler versuchen, die Datenbankanmeldeinformationen vor der Person, die das Programm verwendet, geheim zu halten.

Erster Fall: Der Benutzer ist berechtigt, die Anmeldeinformationen der Datenbank zu kennen

In diesem Fall funktioniert die oben erwähnte Lösung. Die Java- PreferenceKlasse speichert den Benutzernamen und das Kennwort im Klartext, die Einstellungsdatei kann jedoch nur vom autorisierten Benutzer gelesen werden. Der Benutzer kann einfach die XML-Datei mit den Einstellungen öffnen und die Anmeldeinformationen lesen. Dies ist jedoch kein Sicherheitsrisiko, da der Benutzer die Anmeldeinformationen zunächst kannte.

Zweiter Fall: Versuch, Anmeldeinformationen vor dem Benutzer zu verbergen

Dies ist der kompliziertere Fall: Der Benutzer sollte die Anmeldeinformationen nicht kennen, benötigt jedoch weiterhin Zugriff auf die Datenbank. In diesem Fall hat der Benutzer, der die Anwendung ausführt, direkten Zugriff auf die Datenbank. Dies bedeutet, dass das Programm die Anmeldeinformationen im Voraus kennen muss. Die oben erwähnte Lösung ist für diesen Fall nicht geeignet. Sie können die Anmeldeinformationen der Datenbank in einer Voreinstellungsdatei speichern, der Benutzer kann diese Datei jedoch lesen, da er der Eigentümer ist. Tatsächlich gibt es keine gute Möglichkeit, diesen Fall sicher zu verwenden.

Richtiger Fall: Verwenden einer mehrschichtigen Architektur

Der richtige Weg, dies zu tun, besteht darin, zwischen Ihrem Datenbankserver und Ihrer Clientanwendung eine mittlere Schicht zu haben, die einzelne Benutzer authentifiziert und die Ausführung einer begrenzten Anzahl von Vorgängen ermöglicht. Jeder Benutzer hätte seine eigenen Anmeldeinformationen, jedoch nicht für den Datenbankserver. Die Anmeldeinformationen würden den Zugriff auf die mittlere Schicht (die Geschäftslogikschicht) ermöglichen und wären für jeden Benutzer unterschiedlich.

Jeder Benutzer hätte seinen eigenen Benutzernamen und sein eigenes Passwort, die ohne Sicherheitsrisiko lokal in einer Einstellungsdatei gespeichert werden könnten. Dies wird als dreistufige Architektur bezeichnet (die Ebenen sind Ihr Datenbankserver, Ihr Geschäftslogikserver und Ihre Clientanwendung). Es ist komplexer, aber es ist wirklich der sicherste Weg, so etwas zu tun.

Die grundlegende Reihenfolge der Operationen lautet:

  1. Der Client authentifiziert sich mit der Geschäftslogikschicht unter Verwendung des persönlichen Benutzernamens / Passworts des Benutzers. Der Benutzername und das Kennwort sind dem Benutzer bekannt und stehen in keiner Beziehung zu den Anmeldeinformationen der Datenbank.
  2. Wenn die Authentifizierung erfolgreich ist, fordert der Client die Geschäftslogikschicht an und fordert einige Informationen aus der Datenbank an. Zum Beispiel ein Inventar von Produkten. Beachten Sie, dass die Clientanforderung keine SQL-Abfrage ist. Es ist ein Remote-Prozeduraufruf wie getInventoryList.
  3. Die Geschäftslogikschicht stellt eine Verbindung zur Datenbank her und ruft die angeforderten Informationen ab. Die Geschäftslogikschicht ist für die Erstellung einer sicheren SQL-Abfrage auf der Grundlage der Benutzeranforderung verantwortlich. Alle Parameter der SQL-Abfrage sollten bereinigt werden, um SQL-Injection-Angriffe zu verhindern.
  4. Die Geschäftslogikschicht sendet die Inventarliste zurück an die Clientanwendung.
  5. Der Client zeigt dem Benutzer die Inventarliste an.

Beachten Sie, dass die Clientanwendung während des gesamten Prozesses niemals eine direkte Verbindung zur Datenbank herstellt . Die Geschäftslogikschicht empfängt eine Anforderung von einem authentifizierten Benutzer, verarbeitet die Anforderung des Clients für eine Inventarliste und führt erst dann eine SQL-Abfrage aus.

William Brendel
quelle
4
Wie genau verhindert dies, dass jemand den Benutzernamen / das Passwort erhält? Kannst du es dann nicht einfach aus der Datei lesen?
Joe Phillips
Wie ich in meiner Antwort sagte, hat nur der Benutzer, der das Programm ausführt, Lesezugriff auf diese Einstellungsdatei, wenn Ihre Dateiberechtigungen richtig eingestellt sind. In UNIX-Umgebungen erfolgt dies automatisch. Für Windows sind möglicherweise zusätzliche Schritte erforderlich (ich bin mir nicht sicher, da ich Windows nicht häufig verwende).
William Brendel
Ich denke, die Idee ist, dass der Benutzer, der die App ausführt, nicht derjenige ist, von dem Sie versuchen, sie abzuhalten. Wenn dies der Fall ist, müssten Sie es verschlüsseln.
Michael Haren
Ja, Michael ist richtig. Im Wesentlichen besteht die Idee darin, dass Sie den Benutzernamen / das Kennwort bereits kennen, sodass Sie ihn nicht vor sich selbst verbergen müssen. Es wird jedoch durch Dateiberechtigungen vor anderen Benutzern verborgen.
William Brendel
7
Wenn Sie (Beispiel) eine DB-Bearbeitungsanwendung für einen Benutzer bereitstellen und nicht möchten, dass dieser den Datenbankbenutzernamen und das Kennwort kennt, haben Sie die Lösung falsch erstellt, und die Client-Software sollte mit einem Server kommunizieren (über zB ein Webdienst), der das DB-Zeug erledigt.
JeeBee
15

Fügen Sie das Kennwort in eine Datei ein, die von der Anwendung gelesen wird. Betten Sie NIEMALS Passwörter in eine Quelldatei ein. Zeitraum.

Ruby hat ein wenig bekanntes Modul namens DBI :: DBRC für eine solche Verwendung. Ich habe keinen Zweifel, dass Java ein Äquivalent hat. Wie auch immer, es ist nicht schwer, einen zu schreiben.

Keltia
quelle
7
Dies erleichtert zwar das spätere Ändern der Kennwörter, löst jedoch nicht das grundlegende Sicherheitsproblem.
Brian Knoblauch
Ja tut es. Siehe auch die Antwort von William Brendel.
Keltia
1
Die Methode, auf die Keltia und ich hingewiesen haben, ist die akzeptierte Art, mit diesem Problem umzugehen. Das Entkoppeln Ihrer Anmeldeinformationen von Ihrem kompilierten Code ist eine der grundlegendsten Sicherheitsmethoden für Software. Das Ablegen der Anmeldeinformationen in einer separaten Datei ist ein effektiver Weg, um dies zu erreichen.
William Brendel
Darüber hinaus sollte die Tatsache, dass sich Ihre Konfigurationsinformationen in einer Klartextdatei befinden, durch Betriebssystembeschränkungen aufgehoben werden. Unter UNIX sollte die Klartextdatei beispielsweise dem Benutzer gehören, der das Programm ausführt, und über die Berechtigungen 0600 verfügen, damit nur der Eigentümer sie lesen kann.
William Brendel
4
OK, daher kann die Datei nur von dem Benutzer gelesen werden, der das Programm ausführt. Toll. Das löst nichts. :-) Ich, der Benutzer, vor dem wir das Passwort geheim halten wollen, kann es genauso leicht lesen wie die App ...
Brian Knoblauch
3

Schreiben Sie eine Webanwendung? Verwenden Sie in diesem Fall JNDI, um es extern für die Anwendung zu konfigurieren. Eine Übersicht finden Sie hier :

JNDI bietet eine einheitliche Möglichkeit für eine Anwendung, Remotedienste über das Netzwerk zu finden und darauf zuzugreifen. Der Remote-Dienst kann ein beliebiger Unternehmensdienst sein, einschließlich eines Messaging-Dienstes oder eines anwendungsspezifischen Dienstes, aber natürlich ist eine JDBC-Anwendung hauptsächlich an einem Datenbankdienst interessiert. Sobald ein DataSource-Objekt erstellt und bei einem JNDI-Namensdienst registriert wurde, kann eine Anwendung über die JNDI-API auf dieses DataSource-Objekt zugreifen, mit dem eine Verbindung zu der von ihm dargestellten Datenquelle hergestellt werden kann.

Tim Howland
quelle
vorausgesetzt, der Link ist schlecht
James Oravec
2

Unabhängig davon, was Sie tun, werden die vertraulichen Informationen irgendwo in einer Datei gespeichert. Ihr Ziel ist es, es so schwierig wie möglich zu machen. Wie viel davon Sie erreichen können, hängt von Ihrem Projekt, den Anforderungen und der Dicke des Portemonnaies Ihres Unternehmens ab.

Der beste Weg ist, keine Passwörter irgendwo zu speichern. Dies wird durch die Verwendung von Hash-Funktionen zum Generieren und Speichern von Passwort-Hashes erreicht:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

Hash-Algorithmen sind Einwegfunktionen. Sie verwandeln jede Datenmenge in einen "Fingerabdruck" fester Länge, der nicht rückgängig gemacht werden kann. Sie haben auch die Eigenschaft, dass der resultierende Hash völlig anders ist, wenn sich die Eingabe nur um ein kleines Bit ändert (siehe das obige Beispiel). Dies ist ideal zum Schutz von Kennwörtern, da wir Kennwörter in einer Form speichern möchten, die sie auch dann schützt, wenn die Kennwortdatei selbst gefährdet ist. Gleichzeitig müssen wir jedoch in der Lage sein, die Richtigkeit des Kennworts eines Benutzers zu überprüfen.

Hinweis ohne Bezug: Wenn Sie in früheren Zeiten im Internet auf den Link "Passwort vergessen" geklickt haben, haben Websites Ihnen Ihr Nur-Text-Passwort per E-Mail gesendet. Sie haben diese wahrscheinlich irgendwo in einer Datenbank gespeichert. Wenn Hacker Zugriff auf ihre Datenbank erhielten, erhielten sie Zugriff auf alle Passwörter. Da viele Benutzer auf mehreren Websites dasselbe Kennwort verwenden würden, war dies ein großes Sicherheitsproblem. Glücklicherweise ist dies heutzutage nicht die übliche Praxis.

Nun kommt die Frage: Wie können Passwörter am besten gespeichert werden? Ich würde diese Lösung (Authentifizierungs- und Benutzerverwaltungsdienst Stormpath's) für eine verdammt ideale Lösung halten:

  1. Ihr Benutzer gibt die Anmeldeinformationen ein und diese werden anhand des Kennwort-Hash überprüft
  2. Passwort-Hashes werden generiert und gespeichert, keine Passwörter
  3. Hashes werden mehrmals ausgeführt
  4. Hashes werden mit einem zufällig generierten Salz erzeugt
  5. Hashes werden mit einem privaten Schlüssel verschlüsselt
  6. Der private Schlüssel wird an einem physisch anderen Ort als Hashes gespeichert
  7. Private Schlüssel werden zeitbasiert aktualisiert
  8. Verschlüsselte Hashes werden in Blöcke unterteilt
  9. Diese Blöcke werden an physisch getrennten Orten gespeichert

Offensichtlich sind Sie weder Google noch eine Bank. Dies ist also eine Overkill-Lösung für Sie. Aber dann kommt die Frage: Wie viel Sicherheit benötigt Ihr Projekt, wie viel Zeit und Geld haben Sie?

Für viele Anwendungen ist das Speichern eines fest codierten Kennworts im Code möglicherweise eine ausreichend gute Lösung, obwohl dies nicht empfohlen wird. Durch einfaches Hinzufügen einiger zusätzlicher Sicherheitsschritte aus der obigen Liste können Sie Ihre Anwendung jedoch wesentlich sicherer machen.

Nehmen wir beispielsweise an, Schritt 1 sei keine akzeptable Lösung für Ihr Projekt. Sie möchten nicht, dass Benutzer jedes Mal ein Kennwort eingeben, oder Sie möchten / müssen nicht einmal, dass Benutzer das Kennwort kennen. Trotzdem haben Sie irgendwo vertrauliche Informationen und möchten diese schützen. Sie haben eine einfache Anwendung, es gibt keinen Server zum Speichern Ihrer Dateien oder dies ist zu viel Aufwand für Ihr Projekt. Ihre Anwendung wird in Umgebungen ausgeführt, in denen es nicht möglich ist, Dateien sicher zu speichern. Dies ist einer der schlimmsten Fälle, aber mit einigen zusätzlichen Sicherheitsmaßnahmen können Sie eine viel sicherere Lösung finden. Beispielsweise können Sie die vertraulichen Informationen in einer Datei speichern und die Datei verschlüsseln. Sie können den privaten Schlüssel der Verschlüsselung im Code fest codieren lassen. Sie können den Code verschleiern, sodass es für jemanden etwas schwieriger ist, ihn zu knacken.dieser Link . (Ich möchte Sie noch einmal warnen, dass dies nicht 100% sicher ist. Ein intelligenter Hacker mit den richtigen Kenntnissen und Tools kann dies hacken. Aufgrund Ihrer Anforderungen und Bedürfnisse ist dies jedoch möglicherweise eine ausreichend gute Lösung für Sie.)

Caner
quelle
1

Diese Frage zeigt, wie Kennwörter und andere Daten in einer verschlüsselten Datei gespeichert werden: Java 256-Bit-AES-Kennwortbasierte Verschlüsselung

Aaron Digulla
quelle
1
Irgendwo im Quellcode müssen noch verschlüsselte Passwörter entschlüsselt werden, um eine Verbindung herzustellen. Dieses betrachtete Passwort ist noch vorhanden.
Bear0x3f
0

MD5 ist ein Hash-Algorithmus, kein Verschlüsselungsalgorithmus. Kurz gesagt, Sie können nicht zurückbekommen, was Sie gehasht haben. Sie können nur vergleichen. Es sollte idealerweise zum Speichern der Benutzerauthentifizierungsinformationen verwendet werden und nicht zum Benutzernamen und Kennwort der Datenbank. db Benutzername und pwd sollten verschlüsselt und in einer Konfigurationsdatei gespeichert werden, um das Mindeste zu tun.

renegadeMind
quelle
Ich habe von Leuten gehört, die alle möglichen Kombinationen von Zeichenfolgen generieren und ihre entsprechenden MD5-Hashes speichern. Wenn sie also den MD5-Hash von jemandem finden, finden sie nur den Hash, den sie gespeichert haben, und sie erhalten die entsprechende Zeichenfolge.
Nav