Zum Inhalt springen
HexaTransfer
Zurück zum Blog
Technische Vertiefungen

Erstellen Sie an Verschlüsselt Dateifreigabe App from Scratch

Step-by-step tutorial to build an encrypted Datei sharing application. Frontend encryption, secure backend, und deployment walkthrough.

Eine verschlüsselte Dateifreigabe-App in etwa 400 Codezeilen bauen und für unter 5 USD monatlich hosten — das ist möglich, wenn man die Kryptographie in den Browser legt, nicht auf den Server. Die DSGVO unterscheidet unter Artikel 4 zwischen personenbezogenen Daten und blossen Byteströmen: wer nur Ciphertext speichert, hat kaum personenbezogene Daten auf dem Server. Der minimale Stack: ein Vite-powered React-Frontend, das mit AES-256-GCM via der Web Crypto API verschlüsselt, ein Fastify-Backend, das jeden Upload als opaques Blob behandelt, S3-kompatibler Storage (Cloudflare R2 oder Backblaze B2) und eine kurze Share-URL, bei der der Entschlüsselungsschlüssel nach dem # lebt, sodass er nie den Server erreicht.

Architekturentscheidungen, die wirklich zählen

Die einzige wichtigste Entscheidung ist, wo der Verschlüsselungsschlüssel lebt. Wenn er je den Server berührt, hat man keine Ende-zu-Ende-Verschlüsselung, sondern serverseitige Verschlüsselung mit zusätzlichen Schritten. Das richtige Pattern: einen zufälligen 256-Bit-Schlüssel im Browser generieren, die Datei damit verschlüsseln, den Ciphertext hochladen und den Schlüssel in das URL-Fragment legen (https://yourapp.com/f/abc123#k=base64key). Browser senden Fragmente nie in HTTP-Anfragen, sodass der Schlüssel clientseitig bleibt.

Zweite Entscheidung: Chunked Uploads. Für Dateien über 100 MB braucht man fortsetzbare Multipart-Uploads, oder jedes WLAN-Aussetzer tötet den Transfer. S3s Multipart-API unterstützt 5-MB-Mindest-Chunks und bis zu 10.000 Parts, was eine 50-GB-Obergrenze ergibt. Von Anfang an dafür planen.

Das Projektskelett aufsetzen

Mit zwei Packages starten: ein Vite + React-Frontend und ein Fastify-Backend. Das Frontend übernimmt die gesamte Kryptographie, das Backend übernimmt Storage und Metadaten. TypeScript für Typsicherheit bei ArrayBuffer und CryptoKey verwenden.

pnpm create vite@latest frontend -- --template react-ts
pnpm create fastify backend

Diese Deps zum Backend hinzufügen: @fastify/multipart, @aws-sdk/client-s3, @aws-sdk/s3-request-presigner und better-sqlite3 für Share-Metadaten. Das SQLite-Schema klein halten: shares(id, object_key, size_bytes, expires_at, download_count, max_downloads). Keine Dateinamen, keine Nutzerdaten, keine IPs.

Die Frontend-Verschlüsselungs-Pipeline schreiben

Einen Schlüssel generieren, mit AES-256-GCM verschlüsseln und ein Ciphertext-Blob plus einen Base64-Schlüssel für das URL-Fragment produzieren:

async function encryptFile(file: File) {
  const key = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']
  );
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const plaintext = await file.arrayBuffer();
  const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv }, key, plaintext
  );
  const rawKey = await crypto.subtle.exportKey('raw', key);
  const blob = new Blob([iv, new Uint8Array(ciphertext)]);
  return { blob, keyBase64: toBase64(rawKey) };
}

Für Dateien über 50 MB durch eine Streaming-Version ersetzen, die 4-MB-Chunks verschlüsselt und sie an einen ReadableStream anhängt. Mobile-Safari-Heap chokes bei allem über ~400 MB in einem einzelnen ArrayBuffer.

Den Upload-Endpunkt gestalten

Das Backend sollte nichts Nützliches wissen. Einen POST mit dem Ciphertext akzeptieren, eine zufällige 16-Zeichen-URL-sichere ID generieren, sie in SQLite mit Ablauf speichern und den Body direkt zu S3 streamen:

fastify.post('/upload', async (req, reply) => {
  const id = nanoid(16);
  const key = `blobs/${id}`;
  const upload = new Upload({
    client: s3,
    params: { Bucket: 'hexa-transfers', Key: key, Body: req.raw }
  });
  await upload.done();
  db.prepare('INSERT INTO shares VALUES (?, ?, ?, ?, 0, ?)').run(
    id, key, req.headers['content-length'], Date.now() + 7*86400*1000, 10
  );
  return { id };
});

Standard-Ablauf bei 7 Tagen, max 10 Downloads. Aggressive Defaults, weil die Alternative unbegrenztes Storage-Wachstum ist. Ein nächtlicher Cron löscht abgelaufene Blobs.

Share-Links mit Fragment-Schlüsseln generieren

Sobald der Upload abgeschlossen ist, die Share-URL clientseitig bauen:

const { id } = await uploadResponse.json();
const shareUrl = `${location.origin}/f/${id}#k=${keyBase64}`;

Dieses Fragment verlässt nie den Browser des Nutzers. Wenn ein Empfänger den Link anklickt, liest die React-App window.location.hash, parst den Schlüssel, holt den Ciphertext und entschlüsselt lokal. Der Server hat keinen Pfad zum Plaintext — außer durch das Ausliefern von bösartigem JavaScript, weshalb man eine strikte Content Security Policy einsetzen und Subresource-Hashes pinnen sollte.

Den Download-Flow implementieren

Auf der Download-Seite den Ciphertext als Stream holen und chunk-aligned entschlüsseln:

const keyRaw = base64ToBytes(location.hash.slice(3));
const key = await crypto.subtle.importKey(
  'raw', keyRaw, { name: 'AES-GCM' }, false, ['decrypt']
);
const resp = await fetch(`/blob/${id}`);
const iv = new Uint8Array(await resp.body.getReader().read().then(r => r.value.slice(0, 12)));
const ct = await resp.arrayBuffer();
const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ct.slice(12));
const url = URL.createObjectURL(new Blob([pt]));

Für große Dateien StreamSaver.js oder die File System Access API nutzen, sodass die entschlüsselten Bytes direkt auf die Disk gehen statt in RAM.

Das Ganze deployen

Frontend: zu Cloudflare Pages oder Vercel pushen, beide kostenlosen Tiers bewältigen dies. Backend: ein einzelner 5-USD-DigitalOcean-Droplet, der Node 22 hinter Caddy (automatisches TLS 1.3) betreibt. Storage: Cloudflare R2 bietet null Egress-Kosten — das ist die Killer-Feature für Datei-Sharing im Scale. Strikte Header via Caddy setzen: Strict-Transport-Security, Content-Security-Policy: default-src 'self'; script-src 'self', X-Content-Type-Options: nosniff und Referrer-Policy: no-referrer.

Die App DSGVO-konform machen

Da der Server nur Ciphertext sieht, gibt es kaum personenbezogene Daten im Sinne von Art. 4 DSGVO. Dennoch Auftragsverarbeitungsbeziehungen dokumentieren (R2 und DigitalOcean), eine 30-Tage-Blob-Aufbewahrungsgrenze setzen und einen klaren Hinweis veröffentlichen, dass Share-Links Schlüssel enthalten und über vertrauenswürdige Kanäle übermittelt werden sollten. Rate-Limiting bei 50 Uploads pro IP pro Stunde, um Missbrauch zu erschweren, ohne Nutzeridentitäten zu loggen. Der BfDI hat wiederholt betont, dass Privacy-by-Design-Architekturen — also Systeme, die Daten gar nicht erst erheben — präventiv DSGVO-konform sind.

HexaTransfer basiert auf genau dieser Architektur — Web Crypto im Browser, opaker Blob-Storage, Schlüssel in URL-Fragmenten, kein Plaintext auf Servern.

Jetzt ausprobieren: https://hexatransfer.com — kostenlos, kein Account, bis zu 10 GB.

Große Dateien sicher mit Ende-zu-Ende-Verschlüsselung senden

Übertragen Sie Dateien bis zu 10 GB kostenlos mit Ende-zu-Ende-Verschlüsselung. Kein Konto erforderlich. Ihre Dateien werden in Ihrem Browser verschlüsselt, bevor sie hochgeladen werden — niemand sonst kann sie lesen.

Datei senden