sha2 / bcrypt in Rust: Komplettanleitung mit Beispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

sha2 / bcrypt in Rust: Komplettanleitung mit Beispielen

In der modernen Softwareentwicklung ist Datensicherheit ein kritischer Aspekt jeder Anwendung. Die Bibliotheken sha2 und bcrypt für die Programmiersprache Rust bieten leistungsstarke und sichere Werkzeuge für kryptografisches Hashing. sha2 implementiert die SHA-2-Familie (Secure Hash Algorithm 2), einschließlich SHA-224, SHA-256, SHA-384, SHA-512 sowie deren verkürzte Varianten (SHA-512/224, SHA-512/256). bcrypt ist eine adaptive kryptografische Hash-Funktion, die speziell für die sichere Speicherung von Passwörtern entwickelt wurde. Sie verwendet einen Salt und eine konfigurierbare Anzahl von Runden (Cost Factor), was sie resistent gegen Rainbow-Table-Angriffe und Brute-Force macht.

Wofür wird das benötigt? SHA-2 wird zur Überprüfung der Datenintegrität, für digitale Signaturen, in Versionskontrollsystemen (Git), der Blockchain und vielen anderen Bereichen eingesetzt, in denen ein schneller und zuverlässiger Hash erforderlich ist. Bcrypt hingegen ist der De-facto-Standard für das Hashing von Passwörtern in Webanwendungen, Datenbanken und Authentifizierungssystemen. Sein entscheidender Vorteil ist die konfigurierbare "Langsamkeit": Sie können die Anzahl der Runden erhöhen, um die steigende Rechenleistung von Angreifern zu kompensieren.

In diesem Artikel betrachten wir beide Bibliotheken im Detail: Installation, wichtigste Methoden, praktische Anwendungsbeispiele und typische Fehler. Sie erfahren, wie Sie Daten korrekt hashen, Passwörter überprüfen und diese Funktionen in Ihre Rust-Projekte integrieren.

Installation

Um sha2 und bcrypt in Rust zu verwenden, müssen Sie sie zur Cargo.toml-Datei Ihres Projekts hinzufügen. Öffnen Sie ein Terminal im Stammverzeichnis Ihres Projekts und führen Sie Folgendes aus:

cargo add sha2 bcrypt

Dieser Befehl fügt die neuesten Versionen der Bibliotheken zum Abschnitt [dependencies] hinzu. Alternativ können Sie die Cargo.toml manuell bearbeiten:

[dependencies]
sha2 = "0.10"
bcrypt = "0.15"

Führen Sie anschließend cargo build aus, um die Abhängigkeiten herunterzuladen und sicherzustellen, dass alles korrekt installiert ist.

Schnellstart: Minimales funktionierendes Beispiel

Beginnen wir mit einem einfachen Beispiel: Hashing eines Strings mit SHA-256 und Passwortüberprüfung mit bcrypt.

use sha2::{Sha256, Digest};
use bcrypt::{hash, verify, DEFAULT_COST};

fn main() {
    // SHA-256 Hashing
    let data = b"Hallo, Welt!";
    let mut hasher = Sha256::new();
    hasher.update(data);
    let result = hasher.finalize();
    println!("SHA-256 Hash: {:x}", result);

    // Bcrypt Passwort-Hashing
    let password = "mein_sicheres_passwort";
    let hashed = hash(password, DEFAULT_COST).unwrap();
    println!("Bcrypt Hash: {}", hashed);

    // Passwort-Überprüfung
    let is_valid = verify(password, &hashed).unwrap();
    println!("Passwort gültig: {}", is_valid);
}

Dieser Code zeigt die grundlegenden Funktionen beider Bibliotheken. Die Ausgabe enthält einen 64-stelligen Hex-Hash von SHA-256 und einen langen bcrypt-String (beginnend mit $2b$).

Wichtige Methoden und Funktionen

Betrachten wir die wichtigsten Funktionen und Methoden aus sha2 und bcrypt. Für jede geben wir die Signatur, eine Beschreibung und ein funktionierendes Beispiel an.

1. sha2::Sha256::new()

Signatur: pub fn new() -> Sha256

Beschreibung: Erstellt eine neue Instanz des SHA-256-Hashers. Dies ist der Ausgangspunkt für das Hashing von Daten.

use sha2::{Sha256, Digest};

let mut hasher = Sha256::new();
hasher.update(b"testdaten");
let hash = hasher.finalize();
println!("{:x}", hash);

2. sha2::Sha256::update()

Signatur: pub fn update(&mut self, data: impl AsRef<[u8]>)

Beschreibung: Fügt dem aktuellen Hash weitere Daten hinzu. Kann mehrmals aufgerufen werden, um Daten in Teilen zu hashen.

use sha2::{Sha256, Digest};

let mut hasher = Sha256::new();
hasher.update(b"Teil 1");
hasher.update(b"Teil 2");
let hash = hasher.finalize();
println!("{:x}", hash);

3. sha2::Sha256::finalize()

Signatur: pub fn finalize(self) -> GenericArray<u8, U32>

Beschreibung: Schließt den Hashing-Prozess ab und gibt den endgültigen Hash als Byte-Array zurück. Nach diesem Aufruf kann der Hasher nicht mehr verwendet werden.

use sha2::{Sha256, Digest};

let mut hasher = Sha256::new();
hasher.update(b"abschließende daten");
let hash = hasher.finalize();
println!("{:x}", hash);

4. bcrypt::hash()

Signatur: pub fn hash<S: AsRef<str>>(password: S, cost: u32) -> Result<String, BcryptError>

Beschreibung: Hasht ein Passwort mit bcrypt. Der Parameter cost bestimmt die Anzahl der Runden (Standard: DEFAULT_COST = 12). Höhere Werte erhöhen die Sicherheit, aber auch die Rechenzeit.

use bcrypt::{hash, DEFAULT_COST};

let hashed = hash("mein_passwort", DEFAULT_COST).unwrap();
println!("Gehashtes Passwort: {}", hashed);

5. bcrypt::verify()

Signatur: pub fn verify<S: AsRef<str>>(password: S, hashed_password: &str) -> Result<bool, BcryptError>

Beschreibung: Überprüft ein Klartext-Passwort gegen einen bcrypt-Hash. Gibt true zurück, wenn das Passwort korrekt ist.

use bcrypt::{hash, verify, DEFAULT_COST};

let hashed = hash("mein_passwort", DEFAULT_COST).unwrap();
let is_valid = verify("mein_passwort", &hashed).unwrap();
println!("Passwort korrekt: {}", is_valid);

Praktische Beispiele

Hier sind zwei vollständige Beispiele, die zeigen, wie Sie diese Bibliotheken in realen Szenarien einsetzen können.

Beispiel 1: Datei-Integritätsprüfung mit SHA-256

use sha2::{Sha256, Digest};
use std::fs::File;
use std::io::{BufReader, Read};

fn hash_file(path: &str) -> Result<String, std::io::Error> {
    let file = File::open(path)?;
    let mut reader = BufReader::new(file);
    let mut hasher = Sha256::new();
    let mut buffer = [0; 8192];

    loop {
        let bytes_read = reader.read(&mut buffer)?;
        if bytes_read == 0 {
            break;
        }
        hasher.update(&buffer[..bytes_read]);
    }

    Ok(format!("{:x}", hasher.finalize()))
}

fn main() {
    match hash_file("beispiel.txt") {
        Ok(hash) => println!("SHA-256 der Datei: {}", hash),
        Err(e) => eprintln!("Fehler beim Lesen der Datei: {}", e),
    }
}

Beispiel 2: Passwort-Manager mit bcrypt

use bcrypt::{hash, verify, DEFAULT_COST};
use std::collections::HashMap;

struct PasswortManager {
    speicher: HashMap<String, String>,
}

impl PasswortManager {
    fn new() -> Self {
        PasswortManager {
            speicher: HashMap::new(),
        }
    }

    fn passwort_speichern(&mut self, benutzername: &str, passwort: &str) {
        let hashed = hash(passwort, DEFAULT_COST).unwrap();
        self.speicher.insert(benutzername.to_string(), hashed);
    }

    fn passwort_pruefen(&self, benutzername: &str, passwort: &str) -> bool {
        if let Some(hashed) = self.speicher.get(benutzername) {
            verify(passwort, hashed).unwrap_or(false)
        } else {
            false
        }
    }
}

fn main() {
    let mut manager = PasswortManager::new();
    manager.passwort_speichern("max", "geheimes_passwort_123");
    println!("Passwort korrekt: {}", manager.passwort_pruefen("max", "geheimes_passwort_123"));
    println!("Passwort falsch: {}", manager.passwort_pruefen("max", "falsches_passwort"));
}

Häufige Fehler und Best Practices

  • Niedriger Cost-Faktor bei bcrypt: Verwenden Sie immer mindestens DEFAULT_COST (12). Niedrigere Werte machen Ihre Passwörter anfällig für Brute-Force-Angriffe.
  • SHA-2 für Passwörter verwenden: SHA-2 ist zu schnell und daher ungeeignet für Passwort-Hashing. Verwenden Sie dafür immer bcrypt oder Argon2.
  • Hardcodierte Salts: Bcrypt generiert automatisch einen sicheren Salt. Verwenden Sie niemals eigene, selbst erstellte Salts.
  • Fehlerbehandlung ignorieren: Beide Bibliotheken geben Result-Typen zurück. Ignorieren Sie Fehler nicht mit .unwrap() in Produktionscode.
  • Veraltete Versionen: Halten Sie Ihre Abhängigkeiten immer aktuell, um von Sicherheitsupdates zu profitieren.

Fazit

Die Bibliotheken sha2 und bcrypt sind unverzichtbare Werkzeuge für jeden Rust-Entwickler, der Wert auf Datensicherheit legt. SHA-2 eignet sich hervorragend für Integritätsprüfungen und allgemeines Hashing, während bcrypt der Goldstandard für die sichere Passwortspeicherung ist. Mit den hier gezeigten Beispielen und Best Practices können Sie diese Bibliotheken sofort in Ihren eigenen Projekten einsetzen.

Experimentieren Sie mit den verschiedenen Algorithmen und Cost-Faktoren, um ein Gefühl für die Leistungsfähigkeit und Sicherheit dieser Bibliotheken zu bekommen. Denken Sie daran: Sicherheit ist kein Feature, sondern ein Prozess – und die Wahl der richtigen Hashing-Bibliothek ist ein wichtiger Schritt in die richtige Richtung.

Empfehlungen