Umgang mit Passwörtern, die für die Authentifizierung im Quellcode verwendet werden

79

Angenommen, ich versuche, von einer RESTful-API zu ziehen, die Basisauthentifizierung / Basiszertifikate verwendet. Was wäre der beste Weg, um diesen Benutzernamen und dieses Kennwort in meinem Programm zu speichern? Im Moment sitzt es nur im Klartext da.

UsernamePasswordCredentials creds = new UsernamePasswordCredentials("myName@myserver","myPassword1234");

Gibt es eine sicherheitsorientiertere Möglichkeit?

Vielen Dank

A_Elric
quelle
4
Die Antwort hängt von folgenden Dingen ab: Möchten Sie die App verteilen? Basiert der Benutzer / das Kennwort auf dem Benutzer der Anwendung oder handelt es sich um eine Art API-Schlüssel? Möchten Sie den Benutzer / das Kennwort vor dem lokalen Benutzer (einer Art DRM) schützen?
Parasietje
Es ist eigentlich ein Programm, das auf einem Backend läuft, aber es geht wirklich mehr um Stil. Ich sollte den Benutzernamen / das Passwort nicht für ein Konto haben, das Informationen auf klassifizierter Ebene im Klartext enthält.
A_Elric
2
Werfen Sie einen Blick auf diesen Thread stackoverflow.com/questions/12198228/… und Sie erhalten eine allgemeine Vorstellung.
Hai

Antworten:

112

Wichtige Notiz:

Wenn Sie das Authentifizierungssystem als Ganzes entwerfen, sollten Sie keine Kennwörter speichern, auch wenn diese verschlüsselt sind. Sie speichern einen Hash und prüfen, ob die bei der Anmeldung angegebenen Kennwörter mit demselben Hash übereinstimmen. Auf diese Weise wird durch eine Sicherheitsverletzung in Ihrer Datenbank vermieden, dass die Kennwörter Ihrer Benutzer offengelegt werden.

Nach alledem, mit einer inneren zu äußeren Denkweise, sind hier einige Schritte, um Ihren Prozess zu schützen:


Im ersten Schritt sollten Sie Ihre Passwortbehandlung von Stringauf ändern character array.

Der Grund dafür ist, dass a Stringein immutableObjekt ist und seine Daten daher nicht sofort bereinigt werden, selbst wenn das Objekt auf gesetzt ist null. Die Daten werden stattdessen für die Speicherbereinigung festgelegt. Dies führt zu Sicherheitsproblemen, da böswillige Programme möglicherweise vor der Bereinigung Zugriff auf diese String(Kennwort-) Daten erhalten.

Dies ist der Hauptgrund, warum die JPasswordField-getText() Methode von Swing veraltet ist und warum getPassword()Zeichenarrays verwendet werden .


Der zweite Schritt besteht darin, Ihre Anmeldeinformationen zu verschlüsseln und sie während des Authentifizierungsprozesses nur vorübergehend zu entschlüsseln. Oder um sie serverseitig zu hashen, speichern Sie diesen Hash und "vergessen" Sie das ursprüngliche Passwort.

Dies stellt ähnlich wie im ersten Schritt sicher, dass Ihre Schwachstellenzeit so kurz wie möglich ist.

Es wird empfohlen, dass Ihre Anmeldeinformationen nicht fest codiert sind und dass Sie sie stattdessen zentral, konfigurierbar und leicht zu warten speichern, z. B. als Konfigurations- oder Eigenschaftendatei oder als Datenbank.

Sie sollten Ihre Anmeldeinformationen verschlüsseln, bevor Sie die Datei speichern. Außerdem können Sie eine zweite Verschlüsselung auf die Datei selbst anwenden (2-Schicht-Verschlüsselung für die Anmeldeinformationen und 1-Schicht für andere Dateiinhalte).

Beachten Sie, dass jeder der beiden oben genannten Verschlüsselungsprozesse selbst mehrschichtig sein kann. Jede Verschlüsselung kann eine individuelle Anwendung des Triple Data Encryption Standard (AKA TDES und 3DES) als konzeptionelles Beispiel sein.


Nachdem Ihre lokale Umgebung ordnungsgemäß geschützt wurde (aber denken Sie daran, dass sie niemals "sicher" ist!), Wendet der dritte Schritt einen grundlegenden Schutz auf Ihren Übertragungsprozess an, indem Sie TLS (Transport Layer Security) oder SSL (Secure Sockets Layer) verwenden .


Der vierte Schritt besteht darin, andere Schutzmethoden anzuwenden.

Wenden Sie beispielsweise Verschleierungstechniken auf Ihre "zu verwendende" Kompilierung an, um (wenn auch in Kürze) zu vermeiden, dass Ihre Sicherheitsmaßnahmen offengelegt werden, falls Ihr Programm von Frau Eve, Herrn Mallory oder einer anderen Person (dem Bad-) erhalten wird. Jungs) und dekompiliert.


UPDATE 1:

Auf Anfrage von @ Damien.Bell finden Sie hier ein Beispiel, das den ersten und zweiten Schritt abdeckt:

    //These will be used as the source of the configuration file's stored attributes.
    private static final Map<String, String> COMMON_ATTRIBUTES = new HashMap<String, String>();
    private static final Map<String, char[]> SECURE_ATTRIBUTES = new HashMap<String, char[]>();
    //Ciphering (encryption and decryption) password/key.
    private static final char[] PASSWORD = "Unauthorized_Personel_Is_Unauthorized".toCharArray();
    //Cipher salt.
    private static final byte[] SALT = {
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
    //Desktop dir:
    private static final File DESKTOP = new File(System.getProperty("user.home") + "/Desktop");
    //File names:
    private static final String NO_ENCRYPTION = "no_layers.txt";
    private static final String SINGLE_LAYER = "single_layer.txt";
    private static final String DOUBLE_LAYER = "double_layer.txt";

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws GeneralSecurityException, FileNotFoundException, IOException {
        //Set common attributes.
        COMMON_ATTRIBUTES.put("Gender", "Male");
        COMMON_ATTRIBUTES.put("Age", "21");
        COMMON_ATTRIBUTES.put("Name", "Hypot Hetical");
        COMMON_ATTRIBUTES.put("Nickname", "HH");

        /*
         * Set secure attributes.
         * NOTE: Ignore the use of Strings here, it's being used for convenience only.
         * In real implementations, JPasswordField.getPassword() would send the arrays directly.
         */
        SECURE_ATTRIBUTES.put("Username", "Hypothetical".toCharArray());
        SECURE_ATTRIBUTES.put("Password", "LetMePass_Word".toCharArray());

        /*
         * For demosntration purposes, I make the three encryption layer-levels I mention.
         * To leave no doubt the code works, I use real file IO.
         */
        //File without encryption.
        create_EncryptedFile(NO_ENCRYPTION, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 0);
        //File with encryption to secure attributes only.
        create_EncryptedFile(SINGLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 1);
        //File completely encrypted, including re-encryption of secure attributes.
        create_EncryptedFile(DOUBLE_LAYER, COMMON_ATTRIBUTES, SECURE_ATTRIBUTES, 2);

        /*
         * Show contents of all three encryption levels, from file.
         */
        System.out.println("NO ENCRYPTION: \n" + readFile_NoDecryption(NO_ENCRYPTION) + "\n\n\n");
        System.out.println("SINGLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(SINGLE_LAYER) + "\n\n\n");
        System.out.println("DOUBLE LAYER ENCRYPTION: \n" + readFile_NoDecryption(DOUBLE_LAYER) + "\n\n\n");

        /*
         * Decryption is demonstrated with the Double-Layer encryption file.
         */
        //Descrypt first layer. (file content) (REMEMBER: Layers are in reverse order from writing).
        String decryptedContent = readFile_ApplyDecryption(DOUBLE_LAYER);
        System.out.println("READ: [first layer decrypted]\n" + decryptedContent + "\n\n\n");
        //Decrypt second layer (secure data).
        for (String line : decryptedContent.split("\n")) {
            String[] pair = line.split(": ", 2);
            if (pair[0].equalsIgnoreCase("Username") || pair[0].equalsIgnoreCase("Password")) {
                System.out.println("Decrypted: " + pair[0] + ": " + decrypt(pair[1]));
            }
        }
    }

    private static String encrypt(byte[] property) throws GeneralSecurityException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));

        //Encrypt and save to temporary storage.
        String encrypted = Base64.encodeBytes(pbeCipher.doFinal(property));

        //Cleanup data-sources - Leave no traces behind.
        for (int i = 0; i < property.length; i++) {
            property[i] = 0;
        }
        property = null;
        System.gc();

        //Return encryption result.
        return encrypted;
    }

    private static String encrypt(char[] property) throws GeneralSecurityException {
        //Prepare and encrypt.
        byte[] bytes = new byte[property.length];
        for (int i = 0; i < property.length; i++) {
            bytes[i] = (byte) property[i];
        }
        String encrypted = encrypt(bytes);

        /*
         * Cleanup property here. (child data-source 'bytes' is cleaned inside 'encrypt(byte[])').
         * It's not being done because the sources are being used multiple times for the different layer samples.
         */
//      for (int i = 0; i < property.length; i++) { //cleanup allocated data.
//          property[i] = 0;
//      }
//      property = null; //de-allocate data (set for GC).
//      System.gc(); //Attempt triggering garbage-collection.

        return encrypted;
    }

    private static String encrypt(String property) throws GeneralSecurityException {
        String encrypted = encrypt(property.getBytes());
        /*
         * Strings can't really have their allocated data cleaned before CG,
         * that's why secure data should be handled with char[] or byte[].
         * Still, don't forget to set for GC, even for data of sesser importancy;
         * You are making everything safer still, and freeing up memory as bonus.
         */
        property = null;
        return encrypted;
    }

    private static String decrypt(String property) throws GeneralSecurityException, IOException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
        return new String(pbeCipher.doFinal(Base64.decode(property)));
    }

    private static void create_EncryptedFile(
                    String fileName,
                    Map<String, String> commonAttributes,
                    Map<String, char[]> secureAttributes,
                    int layers)
                    throws GeneralSecurityException, FileNotFoundException, IOException {
        StringBuilder sb = new StringBuilder();
        for (String k : commonAttributes.keySet()) {
            sb.append(k).append(": ").append(commonAttributes.get(k)).append(System.lineSeparator());
        }
        //First encryption layer. Encrypts secure attribute values only.
        for (String k : secureAttributes.keySet()) {
            String encryptedValue;
            if (layers >= 1) {
                encryptedValue = encrypt(secureAttributes.get(k));
            } else {
                encryptedValue = new String(secureAttributes.get(k));
            }
            sb.append(k).append(": ").append(encryptedValue).append(System.lineSeparator());
        }

        //Prepare file and file-writing process.
        File f = new File(DESKTOP, fileName);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        } else if (f.exists()) {
            f.delete();
        }
        BufferedWriter bw = new BufferedWriter(new FileWriter(f));
        //Second encryption layer. Encrypts whole file content including previously encrypted stuff.
        if (layers >= 2) {
            bw.append(encrypt(sb.toString().trim()));
        } else {
            bw.append(sb.toString().trim());
        }
        bw.flush();
        bw.close();
    }

    private static String readFile_NoDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException {
        File f = new File(DESKTOP, fileName);
        BufferedReader br = new BufferedReader(new FileReader(f));
        StringBuilder sb = new StringBuilder();
        while (br.ready()) {
            sb.append(br.readLine()).append(System.lineSeparator());
        }
        return sb.toString();
    }

    private static String readFile_ApplyDecryption(String fileName) throws FileNotFoundException, IOException, GeneralSecurityException {
        File f = new File(DESKTOP, fileName);
        BufferedReader br = new BufferedReader(new FileReader(f));
        StringBuilder sb = new StringBuilder();
        while (br.ready()) {
            sb.append(br.readLine()).append(System.lineSeparator());
        }
        return decrypt(sb.toString());
    }

Ein vollständiges Beispiel, das sich mit jedem Schutzschritt befasst, würde weit über das hinausgehen, was ich für diese Frage für angemessen halte, da es um "Was sind die Schritte" und nicht um "Wie man sie anwendet" geht .

Es würde meine Antwort (endlich die Stichprobe) weit überbewerten, während andere Fragen hier auf SO bereits auf das "Wie" dieser Schritte gerichtet sind, weitaus angemessener sind und eine weitaus bessere Erklärung und Stichprobe für die Implementierung von bieten jeder einzelne Schritt.

XenoRo
quelle
3
[*]- @ Damien.Bell Um Ihre Anfrage nicht unbeaufsichtigt zu lassen, habe ich ein Beispiel beigefügt, das den ersten (~) und zweiten Schritt abdeckt. --- Warum nicht alle Schritte, wie Sie sehen können, können Sie nicht mit einem winzigen kleinen Codeausschnitt abtasten. Und ein Beispiel für den Netzwerkschutz würde sogar mehr erfordern als das des lokalen Bereichs, selbst wenn es teilweise pseudocodiert werden soll. Die Verschleierung verfügt auch über eine Vielzahl von Implementierungsmethoden, und obwohl das Konzept einfach ist, ist es aufgrund der Tatsache, dass es auf den Quellcode selbst angewendet wird, in Beispielen schwer zu erklären.
XenoRo
2
Führen Sie schließlich ein Verschleierungstool wie ProGuard für Ihre Quelle aus. Java-Bytecode ist bekanntermaßen einfach zu zerlegen und zu analysieren. Verschleierung ist das i-Tüpfelchen auf Ihrem Sicherheitskuchen und macht es für jemanden viel schwieriger, Ihren Code zurückzuentwickeln und möglicherweise Ihre Sicherheitsmaßnahmen zu hacken. Siehe: proguard.sourceforge.net/index.html#manual/introduction.html
Jarrod Smith
1
@ Woot4Moo - Soweit ich weiß , ist der Bereich nicht der des Authentifizierungsprozesses aus Sicht des Authentifizierers (Servers), sondern aus Sicht des Clients , da er versucht, von einer nicht lokalen Entität (Server) abzurufen . --- Daher muss der Client die Anmeldeinformationen bei der Speicherung und Übertragung schützen, die Anmeldeinformationen jedoch so senden, wie sie sind . Die Übertragungssicherheit wird im dritten Schritt behandelt, bei dem Verschlüsselung, Hashing und Nachrichtenverdauung inhärent sind. ___ Auf dem Server ist ein solcher Hash-Vergleich anwendbar, und der Client sollte aus Sicherheitsgründen keine Server-Duty-Prozesse ausführen.
XenoRo
1
@Roland Ich habe in den letzten Jahren oder so nichts in Java programmiert, daher erinnere ich mich nicht daran, dass ich etwas CharBufferBesonderes getan habe, aber im Grunde genommen, wenn es nicht unveränderlich ist (kann es dazu führen, dass interne Daten überschrieben werden nulloder Nullen ohne Warten auf GC), dann können Sie es verwenden, solange Sie nicht vergessen, es aufzuräumen.
XenoRo
1
Wie ist dies sicherer, wenn alle Entschlüsselungs- und Verschlüsselungsinformationen (einschließlich Salt) in meinem Quellcode enthalten sind?
Herr Derb
7

Wenn Sie die Basisauthentifizierung verwenden, sollten Sie diese mit SSL koppeln, um zu vermeiden, dass Ihre Anmeldeinformationen in Base64-codiertem Klartext übergeben werden. Sie möchten es nicht einfach machen, dass jemand, der an Ihren Paketen schnüffelt, Ihre Anmeldeinformationen erhält. Codieren Sie Ihre Anmeldeinformationen auch nicht fest in Ihrem Quellcode. Machen Sie sie konfigurierbar. Lesen Sie sie aus einer Konfigurationsdatei. Sie sollten die Anmeldeinformationen verschlüsseln, bevor Sie sie in einer Konfigurationsdatei speichern, und Ihre App sollte die Anmeldeinformationen entschlüsseln, sobald sie aus der Konfigurationsdatei gelesen werden.

Athen Holloway
quelle
1
Können Sie einige Beispiele dafür nennen, wie Sie dies programmgesteuert tun würden?
A_Elric
check out stackoverflow.com/questions/20227/…
Athen Holloway
2
  1. Sicherer Computer, der die Anforderung initialisiert (Ihr Computer). Wenn diese Maschine unsicher ist, schützt Sie nichts. Das ist ein völlig anderes Thema (aktuelle Software, richtig konfigurierte, sichere Passwörter, verschlüsselter Swap, Hardware-Sniffer, physische Sicherheit usw.)
  2. Sichern Sie Ihren Speicher. Das Medium, auf dem Sie Ihre Anmeldeinformationen speichern, sollte verschlüsselt sein. Entschlüsselte Anmeldeinformationen sollten nur im RAM Ihres gesicherten Computers gespeichert werden
  3. Personen, die diese Hardware pflegen, müssen vertrauenswürdig sein (wahrscheinlich das schwächste Glied)
  4. Sie sollten auch so wenig wie möglich wissen. Das ist ein Schutz vor der Kryptoanalyse von Gummischläuchen
  5. Ihre Anmeldeinformationen sollten alle Sicherheitsempfehlungen erfüllen (richtige Länge, Zufälligkeit, einzelner Zweck usw.)
  6. Ihre Verbindung zum Remote-Dienst muss gesichert sein (SSL usw.)
  7. Ihr Remote-Dienst muss vertrauenswürdig sein (siehe Punkte 1-4). Außerdem sollte es hackanfällig sein (wenn Ihre Daten / Dienste unsicher sind, ist das Sichern Ihrer Anmeldeinformationen sinnlos). Außerdem sollten Ihre Anmeldeinformationen nicht gespeichert werden

plus wahrscheinlich tausend Dinge, die ich vergessen habe :)

Piotrek
quelle
Ihre Antwort umfasst sowohl clientseitige als auch serverseitige Schutzschritte in einer zusammengefassten, aber sehr klaren und "nachvollziehbaren" Weise. Ich mochte es sehr! [+1] --- Es gibt einige Dinge, die meiner Meinung nach nur ein kleines Stück weiter erklärt werden sollten, und da auch einige Rechtschreib- und Formatierungsprobleme vorhanden waren, habe ich mir die Freiheit genommen, sie zu bearbeiten. --- Die allgemeine Struktur sowie der größte Teil des Textes bleiben unverändert. Ich habe nur hinzugefügt, was mir fehlte, und den vorhandenen Text entsprechend neu organisiert. Ich hoffe es macht dir nichts aus.
XenoRo
Ich habe nichts gegen Rechtschreibung, Links, Grammatik usw. Danke dafür. Wenn Sie jedoch etwas hinzufügen möchten, ändern Sie bitte meine Antwort nicht. Wenn Sie das Gefühl haben, dass etwas fehlt, fügen Sie einen Kommentar hinzu oder erstellen Sie Ihre eigene Antwort. Ich unterschreibe lieber nur unter meinen eigenen Worten
Piotrek
Ich verstehe. --- Nun, meine Bearbeitung hat die Bedeutung Ihrer Antwort in keiner Weise wirklich geändert. Das meiste davon war das Korrigieren von Rechtschreibung und Format, und die Formatierung, die korrigiert werden muss, erfordert sowieso geringfügige Textänderungen. Die wenigen zusätzlichen Erklärungen waren nur Erweiterungen dessen, was bereits gesagt wurde. --- In jedem Fall korrigieren Sie bitte die Rechtschreibung (Großbuchstaben bei Phrasenstarts ist das Hauptproblem) und das Format (trennen Sie "Thema" von "Inhalt" korrekt) und nehmen Sie die erforderlichen Anpassungen an den Text vor. Siehe auch # 7 "anfällig" . --- Und natürlich wäre es schön, das Zusätzliche dabei zu berücksichtigen.
XenoRo
1

Es ist im Allgemeinen kein guter Rat, Anmeldeinformationen zu verschlüsseln. Etwas, das verschlüsselt ist, kann entschlüsselt werden. Es wird empfohlen, Kennwörter als gesalzenen Hash zu speichern. Ein Hash kann nicht entschlüsselt werden. Das Salz wird hinzugefügt, um das Erraten von Brute Force mit Rainbow Tables zu besiegen . Solange jede Benutzer-ID ihr eigenes zufälliges Salz hat, müsste ein Angreifer eine Reihe von Tabellen für jeden möglichen Wert des Salzes erstellen, was diesen Angriff innerhalb der Lebensdauer des Universums schnell unmöglich macht. Dies ist der Grund, warum Websites Ihnen Ihr Passwort im Allgemeinen nicht senden können, wenn Sie es vergessen haben, aber sie können es nur zurücksetzen. Sie haben Ihr Passwort nicht gespeichert, nur einen Hash davon.

Das Hashing von Passwörtern ist nicht sehr schwierig, aber es ist ein so häufiges Problem, dass unzählige andere es für Sie getan haben. Ich habe festgestellt, dass jBcrypt einfach zu bedienen ist.

Als zusätzlichen Schutz gegen das Brute-Force-Erraten von Kennwörtern wird empfohlen, eine Benutzer-ID oder eine Remote-IP nach einer bestimmten Anzahl von Anmeldeversuchen mit dem falschen Kennwort einige Sekunden lang zu warten. Ohne dies kann ein Brute-Force-Angreifer so viele Passwörter pro Sekunde erraten, wie Ihr Server verarbeiten kann. Es gibt einen großen Unterschied zwischen dem Erraten von 100 Passwörtern pro 10 Sekunden oder einer Million.

Ich habe den Eindruck, dass Sie die Kombination aus Benutzername und Passwort in Ihren Quellcode aufgenommen haben. Dies bedeutet, dass Sie, wenn Sie das Kennwort jemals ändern möchten, Ihren Dienst neu kompilieren, stoppen und neu starten müssen. Außerdem bedeutet dies, dass jeder, der Ihren Quellcode erhält, auch über Ihre Kennwörter verfügt. Es wird empfohlen, dies niemals zu tun, sondern die Anmeldeinformationen (Benutzername, Kennwort-Hash, Kennwort-Salt) in Ihrem Datenspeicher zu speichern

Mzzl
quelle
2
Ich bin mir immer noch nicht sicher, aber ich denke, "... ich versuche, aus einer RESTful-API zu ziehen ..." zeigt an, dass das OP nicht über die serverseitige Umgebung spricht. Ich glaube, er spricht von einer clientseitigen Anwendung, die sich bei einem Server authentifiziert. __ Daher sollte die Clientseite nur die im Speicher befindlichen Anmeldeinformationen (Verschlüsselung) schützen und diese sicher an den Server senden (TSL / SSL - die von Natur aus Verschlüsselung und Nachrichtenverdauung anwenden). ___ Die Nachrichtenübersicht (zum Registrieren oder Vergleichen) ) sollte nur serverseitig erfolgen, da dies sonst unsicher wäre. ___ Es steht alles auf den Kommentaren meiner Antwort.
XenoRo
Ihre Antwort weist auch auf die Verwendung einer möglicherweise veralteten API hin (jBcrypt - Es befindet sich in Beta v0.3 und wurde zuletzt im Januar 2010 aktualisiert. Dies ist ein möglicher Hinweis darauf, dass das Projekt ausgestorben ist). Java verfügt bereits über eigene Standardklassen für die Nachrichtenverdauung , und ich glaube nicht, dass APIs von Drittanbietern unbedingt erforderlich sind.
XenoRo
Es sieht so aus, als ob Sie in Bezug auf die Verwirrung zwischen Client und Server Recht haben. Ich würde weiterhin empfehlen, die Anmeldeinformationen in einem Datenspeicher abzulegen, nicht im Quellcode, aber Sie haben Recht, wenn Sie in diesem Fall eher eine Verschlüsselung als ein Hashing benötigen.
Mzzl
Bcrypt ist kein Message Digest, sondern ein auf Blowfish basierendes Schlüsselgenerierungsschema. Ich benutze es als Teil von SpringSecurity, das sehr lebendig ist. Einfache Message Digest-Algorithmen wie SHA-1 oder MD5 sind nicht für das Passwort-Hashing gedacht, sondern für das schnelle Hashing. Wenn Sie so schnell wie möglich einen Teil des Videos oder Textes hashen müssen, können Sie diese oder deren modernere Ersetzungen verwenden. Wenn Sie an Hashing-Passwörtern interessiert sind, ist Geschwindigkeit Ihr Feind. Je schneller der verwendete Hashing-Algorithmus ist, desto schneller kann ein Brute-Force-Angriff erfolgreich sein.
Mzzl
Hmm. Einige Google-Suchanfragen deuten darauf hin, dass Blowfish eine Verschlüsselung (entschlüsselbar) ist, während die Seite von jBcrypt angibt, dass ein Blowfish-basierter Message-Diggest ( eine kryptografische Hash-Funktion ) verwendet wird. Ich bin verwirrt. ___ SpringSecurity lebt, Bcrypt möglicherweise nicht. Sie sind separate Projekte. ___ Wie auch immer, Java 1.7 enthält bereits die Blowfish-Verschlüsselung, und der modulare Aufbau der Sicherheitsklasse ermöglicht eine einfache Implementierung, security.Providerauch in älteren Versionen, sodass ich immer noch keine Notwendigkeit für APIs von Drittanbietern sehe.
XenoRo
0

Warum reden die Leute über Hashing? OP möchte die Anmeldeinformationen seiner Benutzer speichern, um auf externe Ressourcen zuzugreifen. Das Hashing seines Passworts hilft nicht.

Das ist aus dem Weg. Ich würde nur einfache Best Practices für jede Ebene.

1. Speichern Sie Ihr Passwort in der Java-App. : Speichern Sie es als Char Array. Erstellen Sie eine Kennwortspeicherklasse und speichern Sie das Kennwort als Hashmap mit dem Schlüssel als Ressource, auf die Sie zugreifen möchten, und bewerten Sie es als ein Objekt, das Benutzername und Kennwort enthält. Beschränken Sie den Einstiegspunkt auf diese API mit einer gewissen Authentifizierung. Beispiel: Akzeptieren Sie die Anmeldeinformationen der angemeldeten Benutzer, um die Zugriffsebene dieses Benutzers für diese Ressource zu überprüfen (ordnen Sie den Benutzer einfach der Liste der Kennwörter zu, auf die er zugreifen kann. Wenn Sie häufig eine Gruppe erstellt haben und ordnen Sie dieser Gruppe den Kennwortzuordnungsschlüssel zu.) Alles darüber hinaus, um das Kennwort zu speichern, hängt davon ab, wie paranoid Sie sind, wenn Sie jvm selbst verlieren.

  1. Um das Passwort zu übertragen, stellen Sie sicher, dass Sie es über gesicherte Portogramme senden (Beispiel: HTTP ist gut, http ist schlecht). Wenn Sie wirklich über ein unsicheres Protokoll übertragen müssen, verschlüsseln Sie es und codieren Sie es mit base64. Stellen Sie sicher, dass der Empfänger Ihr Passwort entschlüsselt und entschlüsseln kann.
Cybermonk
quelle
-1

Wenn Sie der Umgebung, in der Ihr Programm ausgeführt wird, nicht vertrauen können, sich jedoch über einfache Kennwörter oder Zertifikate authentifizieren müssen, können Sie Ihre Anmeldeinformationen nicht sichern. Sie können sie höchstens mit den in den anderen Antworten beschriebenen Methoden verschleiern.

Als Problemumgehung würde ich alle Anforderungen an die RESTful-API über einen Proxy ausführen, dem Sie vertrauen können, und von dort aus die Klartextkennwortauthentifizierung durchführen.

Twilite
quelle
"Wenn Sie der Umgebung, in der Ihr Programm ausgeführt wird, nicht vertrauen können, ... können Sie nichts tun, um Ihre Anmeldeinformationen zu sichern." - Wenn das wahr wäre, wäre so ziemlich jede Anwendung, die eine Option zum automatischen Ausfüllen der Anmeldeinformationen hat, in großen Schwierigkeiten. ___ Viele Anwendungen mit zwei Enden (diese Frage?) Wie Multiplayer-Spiele und webbasierte Anwendungen speichern die Kontoanmeldeinformationen lokal und haben selten ernsthafte Sicherheitsprobleme. ___ Daten sind niemals 100% sicher, unabhängig von der Umgebung. Eine vertrauenswürdige Umgebung ist nur ein weiterer Sicherheitsschritt ("sicherer").
XenoRo
Nun, in dem gegebenen Szenario können Sie Ihre Anmeldeinformationen verschleiern, aber nicht 100% (kryptografische) Sicherheit erreichen. Das Beste, worauf Sie hoffen können, ist, es für den Angreifer so kompliziert zu machen, die Klartext-Passwörter zu erhalten, dass sich die Mühe nicht lohnt. Um die gespeicherten Passwörter einer typischen webbasierten Anwendung abzurufen, müssen Sie lediglich das Optionsmenü Ihres Browsers aufrufen und "Passwörter anzeigen" auswählen.
Twilite
Sie können niemals 100% ige Sicherheit erreichen, sei es in diesem oder in einem anderen Szenario . Dies liegt daran, dass es letztendlich auf Sequenzen von 0und 1im Speicher ankommt, die nach einem bestimmten Satz logischer Regeln erreicht werden, die von Natur aus immer irgendwie umkehrbar sind. ___ Die Grundlage der kryptografischen Sicherheit ist, war und wird wahrscheinlich immer sein, "es so schwierig zu machen, dass sich die Mühe nicht lohnt". ___ Zuletzt verwechseln Sie das automatische Ausfüllen / Anmelden des Browsers (für Websites) mit dem automatischen Authentifizieren / Anmelden der Anwendung (das nur in verschlüsselten Dateien gespeichert wird).
XenoRo
Sie sollten sich über Kryptographie informieren. Es gibt verschiedene Methoden, um Daten irreversibel zu verschlüsseln. Schauen Sie sich einfach "One-Way-Hashes" (md5) oder Kryptografie mit öffentlichen Schlüsseln an, bei denen es nicht möglich ist, verschlüsselte Daten zu entschlüsseln, selbst wenn Sie sowohl über die verschlüsselten Daten als auch über den Verschlüsselungsschlüssel verfügen. Mit diesen Methoden erhalten Sie sachliche 100% ige Sicherheit.
Twilite
Nicht wirklich. - Wie ich bereits sagte, folgen die Methoden einem bestimmten Satz logischer Regeln, und diese können umgekehrt werden. ___ Bei kryptografischen Hash-Funktionen kann der Hacker, wenn er die Regeln kennt, die den Hash generiert haben, einige Bits der Originaldaten RE abrufen und sich einen guten Überblick über die Länge des Originals verschaffen. --- Es ist eine Menge Brute-Forcing und Rätselraten, aber es ist nicht 100% unzerbrechlich. Und es ist weit entfernt von der unendlichen Anzahl von Versuchen, 100% ige Sicherheit zu gewährleisten. ___ Ich glaube, kein Hacker würde sich die Mühe machen, es hart zu versuchen. Es ist alles andere als die Mühe wert, egal welche Belohnung.
XenoRo