Dateien im Browser mit Web Crypto hashen
Die Verifizierung der Dateiintegrität ist entscheidend für den sicheren Umgang mit Daten in Webanwendungen. Die Web Crypto API stellt integrierte kryptografische Operationen bereit, mit denen Sie sichere Hashwerte direkt im Browser berechnen können. Dieser Beitrag zeigt, wie Sie clientseitiges Datei-Hashing implementieren, um Manipulationen oder Beschädigungen von Daten während Dateiübertragungen zu erkennen.
Browserkompatibilität und Voraussetzungen
Dieses Beispiel benötigt sowohl crypto.subtle als auch File.prototype.arrayBuffer, die in modernen
Browsern verfügbar sind. Prüfen Sie diese Funktionen, bevor Sie die Hashing-UI aktivieren;
Unterstützung für Web Crypto allein garantiert noch keine Unterstützung für die hier verwendete API
zum Lesen von Dateien.
Die Web Crypto API benötigt einen sicheren Kontext (HTTPS), um zu funktionieren. Bei der lokalen
Entwicklung gilt localhost standardmäßig als sicher.
Unterstützte Hash-Algorithmen
Die Web Crypto API unterstützt mehrere Hash-Algorithmen über die Methode crypto.subtle.digest():
- SHA-256 (empfohlen für den allgemeinen Gebrauch)
- SHA-384 (384-Bit-Digest)
- SHA-512 (512-Bit-Digest)
- SHA-1 (aufgrund bekannter Schwachstellen nicht empfohlen)
Datei-Hashing implementieren
Hier ist eine vollständige Implementierung, die Datei-Hashing mit ordentlicher Fehlerbehandlung umsetzt:
async function calculateHash(file, algorithm = 'SHA-256') {
if (!(file instanceof File)) {
throw new Error('Input must be a File object')
}
try {
const arrayBuffer = await file.arrayBuffer()
const hashBuffer = await crypto.subtle.digest(algorithm, arrayBuffer)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
} catch (error) {
if (error instanceof DOMException) {
throw new Error(`Unsupported hash algorithm: ${algorithm}`)
}
throw new Error('Failed to calculate file hash')
}
}
function setupFileHashing() {
const fileInput = document.getElementById('fileInput')
const hashOutput = document.getElementById('hashOutput')
const algorithmSelect = document.getElementById('algorithmSelect')
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0]
if (!file) return
const algorithm = algorithmSelect.value
hashOutput.textContent = 'Computing hash...'
try {
const hash = await calculateHash(file, algorithm)
hashOutput.textContent = `${algorithm}: ${hash}`
} catch (error) {
hashOutput.textContent = `Error: ${error.message}`
console.error('Hashing error:', error)
}
})
}
Die zugehörige HTML-Struktur:
<div class="hash-container">
<label for="algorithmSelect">Hash algorithm</label>
<select id="algorithmSelect">
<option value="SHA-256">SHA-256</option>
<option value="SHA-384">SHA-384</option>
<option value="SHA-512">SHA-512</option>
</select>
<label for="fileInput">Choose a file to hash</label>
<input type="file" id="fileInput" />
<div id="hashOutput" role="status"></div>
</div>
Rufen Sie setupFileHashing() auf, nachdem dieses HTML geladen wurde, zum Beispiel aus einem verzögert
geladenen Skript.
Große Dateien verarbeiten
crypto.subtle.digest() unterstützt keine Streaming-Eingabe.
Die vollständige Datei muss in den Arbeitsspeicher passen. Wenn Sie einzelne Chunks unabhängig
voneinander hashen, ergibt das nicht den Hash der vollständigen Datei; behalten Sie nur das letzte
Ergebnis, verifizieren Sie damit nur den letzten Chunk.
Verwenden Sie bei Dateien, die das Speicherbudget Ihrer Anwendung überschreiten, eine gepflegte Bibliothek für inkrementelles Hashing, oder hashen Sie einen Stream auf dem Server. Ein Web Worker kann die Hashing-Arbeit vom Main Thread fernhalten, aber dieses Beispiel in einen Worker zu verlagern beseitigt seinen Speicherbedarf für die vollständige Datei nicht.
Dateiintegrität verifizieren
Um die Dateiintegrität zu verifizieren, vergleichen Sie den berechneten Hash mit einem erwarteten Wert aus einer vertrauenswürdigen Quelle. Ein Angreifer, der sowohl die Datei als auch die veröffentlichte Prüfsumme ersetzen kann, kann beide zur Übereinstimmung bringen.
function verifyFileIntegrity(computedHash, expectedHash) {
// These are public checksums, not secret authentication values.
if (computedHash.length !== expectedHash.length) {
return false
}
return computedHash.toLowerCase() === expectedHash.toLowerCase()
}
// Pass the selected File and a trusted SHA-256 checksum to this helper.
async function verifySelectedFile(file, expectedHash) {
const computedHash = await calculateHash(file, 'SHA-256')
return verifyFileIntegrity(computedHash, expectedHash)
}
Bewährte Verfahren
- Verwenden Sie für neue Integritätsprüfungen SHA-256, SHA-384 oder SHA-512 statt SHA-1
- Implementieren Sie eine ordentliche Fehlerbehandlung
- Zeigen Sie eine unbestimmte Fortschrittsanzeige an, während Web Crypto den Digest berechnet
- Beziehen Sie die erwartete Prüfsumme über einen vertrauenswürdigen Kanal
- Erwägen Sie, bei mehreren Dateien eine Ratenbegrenzung zu implementieren
Clientseitiges Datei-Hashing fügt Webanwendungen eine wichtige Sicherheitsebene hinzu und trägt dazu bei, die Datenintegrität bei Dateiübertragungen sicherzustellen. Diese Implementierung nutzt zwar die Web Crypto API, in Produktionssystemen kommen jedoch häufig zusätzliche Sicherheitsmaßnahmen und eine serverseitige Verifizierung zum Einsatz.
Viel Spaß beim Coden!
