195 lines
4.8 KiB
Markdown
195 lines
4.8 KiB
Markdown
|
|
# STEP 4.2a – HASH-MIGRATION PROOF (AUDIT-NACHWEIS)
|
|||
|
|
# Status: PASS
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test-Setup
|
|||
|
|
|
|||
|
|
- **Datei:** `basis14.py` (Zeilen 1298–1316)
|
|||
|
|
- **Speicherort:** `kg_diktat_user_profile.json` (JSON, Feld `password_hash`)
|
|||
|
|
- **Testmethode:** Automatisiertes Python-Skript mit Backup/Restore des Original-Profils
|
|||
|
|
- **KDF:** bcrypt, cost=12, Salt eingebettet
|
|||
|
|
- **Legacy:** SHA-256, 64 Hex-Zeichen, kein Salt
|
|||
|
|
- **Testpasswörter:** `AuditTest2026!` (korrekt), `FalschesPasswort` (falsch)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test 1: Legacy-SHA-256-User anlegen
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Passwort: AuditTest2026!
|
|||
|
|
SHA-256 Hash: e99f0f4a91440e4a7d89ebd1db548df0eed586dd16e1b3c0e9bb6356220e0f3b
|
|||
|
|
Speicherort: kg_diktat_user_profile.json
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| Prüfung | Ergebnis |
|
|||
|
|
|---------|----------|
|
|||
|
|
| Profil gespeichert | PASS |
|
|||
|
|
| Hash ist Legacy (64 hex chars) | PASS |
|
|||
|
|
| Hash Länge = 64 | PASS |
|
|||
|
|
| Hash beginnt NICHT mit $2b$ | PASS |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test 2: Login mit falschem Passwort
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Passwort: FalschesPasswort
|
|||
|
|
Ergebnis: REJECTED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| Prüfung | Ergebnis |
|
|||
|
|
|---------|----------|
|
|||
|
|
| Falsches PW abgelehnt | PASS |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test 3: Login mit korrektem Passwort + Rehash
|
|||
|
|
|
|||
|
|
### VORHER (Legacy SHA-256):
|
|||
|
|
```
|
|||
|
|
e99f0f4a91440e4a7d89ebd1db548df0eed586dd16e1b3c0e9bb6356220e0f3b
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### NACHHER (bcrypt):
|
|||
|
|
```
|
|||
|
|
$2b$12$mM1wNUuQJfYMZ23xJRfsJO/JVI0G4ucm9lT.BnTK/LVK8m/gw6CEK
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| Prüfung | Ergebnis |
|
|||
|
|
|---------|----------|
|
|||
|
|
| Korrektes PW akzeptiert | PASS |
|
|||
|
|
| Hash hat sich geändert | PASS |
|
|||
|
|
| Neuer Hash beginnt mit $2b$ | PASS |
|
|||
|
|
| Neuer Hash ist NICHT Legacy | PASS |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test 4: Login nach Rehash (bcrypt-Hash)
|
|||
|
|
|
|||
|
|
| Prüfung | Ergebnis |
|
|||
|
|
|---------|----------|
|
|||
|
|
| Korrektes PW nach Rehash | PASS |
|
|||
|
|
| Falsches PW nach Rehash abgelehnt | PASS |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test 5: Neue Registrierung → direkt bcrypt
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Neues Passwort: NeuerUser2026!
|
|||
|
|
bcrypt Hash: $2b$12$MdhU4H6CihlPMecPxRGgHuZ0rRJw.QXd1B0tqr4A8xK...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| Prüfung | Ergebnis |
|
|||
|
|
|---------|----------|
|
|||
|
|
| Hash ist bcrypt | PASS |
|
|||
|
|
| Hash ist NICHT Legacy | PASS |
|
|||
|
|
| Verify korrekt | PASS |
|
|||
|
|
| Verify falsch abgelehnt | PASS |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Test 6: Passwortänderung → bcrypt
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Neuer Hash: $2b$12$62I3tJxk09DaPmmu.6SsEeTgSp6UJReaJtSzs57SFO6...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| Prüfung | Ergebnis |
|
|||
|
|
|---------|----------|
|
|||
|
|
| Hash ist bcrypt | PASS |
|
|||
|
|
| Altes PW abgelehnt | PASS |
|
|||
|
|
| Neues PW akzeptiert | PASS |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Code-Verifikation
|
|||
|
|
|
|||
|
|
### _hash_password() – Zeile 1298–1301
|
|||
|
|
```python
|
|||
|
|
@staticmethod
|
|||
|
|
def _hash_password(pw: str) -> str:
|
|||
|
|
return bcrypt.hashpw(pw.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("utf-8")
|
|||
|
|
```
|
|||
|
|
→ Erzeugt bcrypt-Hash mit cost=12 und eingebettetem Salt.
|
|||
|
|
|
|||
|
|
### _verify_password() – Zeile 1303–1309
|
|||
|
|
```python
|
|||
|
|
@staticmethod
|
|||
|
|
def _verify_password(pw: str, stored_hash: str) -> bool:
|
|||
|
|
if stored_hash.startswith("$2b$") or stored_hash.startswith("$2a$"):
|
|||
|
|
return bcrypt.checkpw(pw.encode("utf-8"), stored_hash.encode("utf-8"))
|
|||
|
|
legacy = hashlib.sha256(pw.encode("utf-8")).hexdigest()
|
|||
|
|
return legacy == stored_hash
|
|||
|
|
```
|
|||
|
|
→ Erkennt bcrypt-Hashes am Prefix. Fällt auf SHA-256-Vergleich zurück.
|
|||
|
|
|
|||
|
|
### _is_legacy_hash() – Zeile 1311–1316
|
|||
|
|
```python
|
|||
|
|
@staticmethod
|
|||
|
|
def _is_legacy_hash(stored_hash: str) -> bool:
|
|||
|
|
if not stored_hash:
|
|||
|
|
return False
|
|||
|
|
return not stored_hash.startswith("$2") and len(stored_hash) == 64
|
|||
|
|
```
|
|||
|
|
→ Erkennt alte 64-Zeichen SHA-256-Hashes.
|
|||
|
|
|
|||
|
|
### Login mit Rehash – Zeile 1363–1367
|
|||
|
|
```python
|
|||
|
|
stored = self._user_profile.get("password_hash", "")
|
|||
|
|
if self._verify_password(pw, stored):
|
|||
|
|
if self._is_legacy_hash(stored):
|
|||
|
|
self._user_profile["password_hash"] = self._hash_password(pw)
|
|||
|
|
save_user_profile(self._user_profile)
|
|||
|
|
dlg.destroy()
|
|||
|
|
```
|
|||
|
|
→ Bei erfolgreichem Login: Legacy-Hash automatisch durch bcrypt ersetzt.
|
|||
|
|
|
|||
|
|
### Registrierung – Zeile 1465
|
|||
|
|
```python
|
|||
|
|
"password_hash": self._hash_password(pw),
|
|||
|
|
```
|
|||
|
|
→ Nutzt direkt bcrypt.
|
|||
|
|
|
|||
|
|
### Profiländerung (altes PW prüfen) – Zeile 1545
|
|||
|
|
```python
|
|||
|
|
if old_hash and not self._verify_password(pw_old_e.get(), old_hash):
|
|||
|
|
```
|
|||
|
|
→ Verifiziert mit _verify_password (bcrypt + Legacy-kompatibel).
|
|||
|
|
|
|||
|
|
### Profiländerung (neues PW setzen) – Zeile 1554
|
|||
|
|
```python
|
|||
|
|
pw_hash = self._hash_password(new_pw)
|
|||
|
|
```
|
|||
|
|
→ Nutzt direkt bcrypt.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Gesamtergebnis
|
|||
|
|
|
|||
|
|
| Nr | Testfall | Ergebnis |
|
|||
|
|
|----|----------|----------|
|
|||
|
|
| 1 | Legacy-Hash anlegen & erkennen | PASS |
|
|||
|
|
| 2 | Falsches PW → abgelehnt | PASS |
|
|||
|
|
| 3 | Korrektes PW → akzeptiert + Rehash | PASS |
|
|||
|
|
| 4 | Login nach Rehash funktioniert | PASS |
|
|||
|
|
| 5 | Registrierung schreibt bcrypt | PASS |
|
|||
|
|
| 6 | Passwortänderung schreibt bcrypt | PASS |
|
|||
|
|
|
|||
|
|
**18/18 Einzelprüfungen: PASS**
|
|||
|
|
**0 Fixes notwendig**
|
|||
|
|
**Original-Profil wiederhergestellt**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Bewertung: PASS
|
|||
|
|
|
|||
|
|
Alle Anforderungen erfüllt:
|
|||
|
|
- Legacy SHA-256 wird erkannt
|
|||
|
|
- Login mit korrektem/falschem PW korrekt
|
|||
|
|
- Rehash passiert automatisch beim Login
|
|||
|
|
- Nach Rehash ist der gespeicherte Hash bcrypt
|
|||
|
|
- Neue Registrierungen und Passwortänderungen verwenden direkt bcrypt
|
|||
|
|
- Kein Forced Reset notwendig
|