Sichere Datei-Uploads: Berechtigungen, Limits, signierte URLs, Scans
Sichere Datei-Uploads in Web-Apps benötigen strikte Berechtigungen, Größenlimits, signierte URLs und einfache Malware-Scanning-Patterns, um Vorfälle zu vermeiden.

Warum Datei-Uploads riskant sind (einfach erklärt)
Datei-Uploads sehen harmlos aus: ein Profilbild, ein PDF, eine Tabelle. Trotzdem sind sie oft der erste Sicherheitsvorfall, weil sie Fremden erlauben, deinem System eine Wundertüte zu schicken. Wenn du sie annimmst, speicherst und anderen wieder zeigst, schaffst du einen neuen Angriffsvektor auf deine App.
Das Risiko ist nicht nur „jemand lädt ein Virus hoch“. Eine bösartige Datei kann private Dokumente leaken, deine Speicherkosten explodieren lassen oder Nutzer dazu bringen, Zugangsdaten preiszugeben. Eine Datei namens „invoice.pdf“ ist vielleicht gar kein PDF. Selbst echte PDFs und Bilder können Probleme verursachen, wenn deine App Metadaten vertraut, Inhalte automatisch vorschaut oder sie mit falschen Regeln ausliefert.
Typische Ausfälle sehen so aus:
- Jemand errät eine Datei-URL und lädt das Dokument eines anderen Nutzers herunter.
- Eine hochgeladene HTML-Datei wird wie eine Webseite ausgeliefert und zeigt eine Login-Diebstahl-Eingabe an.
- Ein Angreifer lädt wiederholt riesige Dateien hoch, bis deine App langsam wird oder abstürzt.
- Ein „sicherer“ Dateityp wird gefälscht und von Mitarbeitern auf einem internen Rechner geöffnet.
Ein Detail verursacht viele Vorfälle: Speichern ist nicht gleich Ausliefern. Storage ist, wo du die Bytes ablegst. Serving ist, wie diese Bytes an Browser und Apps geliefert werden. Probleme entstehen, wenn die App Nutzer-Uploads mit dem gleichen Vertrauensniveau und den gleichen Regeln ausliefert wie die Hauptseite, sodass der Browser das Upload als „vertrauenswürdig“ einstuft.
„Sicher genug“ für eine kleine oder wachsende App heißt meist, dass du vier Fragen ohne Ausflüchte beantworten kannst: Wer darf hochladen, was akzeptierst du, wie groß und wie oft, und wer darf die Datei später lesen. Selbst wenn du schnell baust (generierter Code oder Chat-gesteuerte Plattform), sind diese Leitplanken wichtig.
Ein einfaches Bedrohungsmodell für Uploads
Behandle jeden Upload wie untrusted input. Der praktische Weg, Uploads sicher zu halten, ist, dir vorzustellen, wer sie missbrauchen könnte und was für Erfolge dieser Missbrauch bringen würde.
Die meisten Angreifer sind entweder Bots, die nach schwachen Upload-Formularen scannen, oder echte Nutzer, die Grenzen ausreizen, um kostenlosen Speicher zu bekommen, Daten zu scrapen oder zu trollen. Manchmal ist es ein Wettbewerber, der auf Lecks oder Ausfälle sondiert.
Was wollen sie erreichen? Meistens eines der folgenden Ziele:
- Code auf deinen Servern ausführen, indem sie etwas hochladen, das ausgeführt wird.
- Private Dateien stehlen, indem Download-URLs erraten, wiederverwenden oder teilen.
- Verfügbarkeit schädigen, indem sie Uploads fluten oder teure Verarbeitung erzwingen.
- Deine Rechnung durch Speicherwachstum oder bandbreitenintensive Downloads aufblasen.
Dann mappe die Schwachstellen: Der Upload-Endpunkt ist die Eingangstür (zu große Dateien, seltsame Formate, hohe Request-Raten). Der Storage ist der Hinterraum (öffentliche Buckets, falsche Berechtigungen, geteilte Ordner). Download-URLs sind der Ausgang (vorhersehbar, langlebig oder nicht an einen Nutzer gebunden).
Beispiel: eine „Lebenslauf-Upload“-Funktion. Ein Bot lädt tausende große PDFs hoch, um Kosten zu erzeugen, während ein missbräuchlicher Nutzer eine HTML-Datei hochlädt und sie als „Dokument“ teilt, um andere zu täuschen.
Bevor du Controls hinzufügst, entscheide, was für deine App am wichtigsten ist: Privatsphäre (wer darf lesen), Verfügbarkeit (kannst du weiter ausliefern), Kosten (Speicher und Bandbreite) und Compliance (wo Daten liegen und wie lange du sie aufbewahrst). Diese Prioritätenliste sorgt für konsistente Entscheidungen.
Berechtigungen und Zugriffskontrolle, die wirklich halten
Die meisten Upload-Vorfälle sind keine großen Hacks. Es sind einfache „Ich kann die Datei eines anderen sehen“-Bugs. Betrachte Berechtigungen als Teil des Uploads, nicht als etwas, das du später draufsetzt.
Fang mit einer Regel an: Default deny. Geh davon aus, dass jedes hochgeladene Objekt privat ist, bis du explizit Zugriff erlaubst. „Standardmäßig privat“ ist eine starke Grundlage für Rechnungen, medizinische Dateien, Kontodokumente und alles, was an einen Nutzer gebunden ist. Mache Dateien nur dann öffentlich, wenn der Nutzer das klar erwartet (z. B. ein öffentliches Avatar), und erwäge selbst dann zeitlich begrenzten Zugriff.
Rollen, die echte Arbeit widerspiegeln
Halte Rollen einfach und getrennt. Eine übliche Aufteilung ist:
- Uploader: kann Uploads für das eigene Konto erstellen
- Viewer: kann Dateien herunterladen, die ihnen erlaubt sind
- Support: kann auf Dateien nur mit einem auditierbaren, temporären Grant zugreifen
- Admin: verwaltet Richtlinien, sollte aber nicht automatisch alles lesen können
Verlasse dich nicht auf Ordner-Regeln wie „alles in /user-uploads/ ist ok“. Prüfe Eigentum oder Tenant-Zugriff bei jedem Lesevorgang, für jede Datei. Das schützt, wenn jemand das Team wechselt, eine Organisation verlässt oder eine Datei neu zugewiesen wird.
Ein gutes Support-Pattern ist eng gefasst und temporär: gewähre Zugriff auf genau eine Datei, protokolliere es und lasse ihn automatisch ablaufen.
Dateityp validieren, ohne zu vertrauen
Die meisten Upload-Angriffe beginnen mit einem einfachen Trick: Eine Datei sieht aufgrund ihres Namens oder eines Browser-Headers sicher aus, ist es aber nicht. Behandle alles, was der Client schickt, als untrusted.
Fang mit einer Allowlist (Positivliste) an: Entscheide die genauen Formate, die du akzeptierst (z. B. .jpg, .png, .pdf) und lehne alles andere ab. Vermeide „irgendein Bild“ oder „irgendein Dokument“, es sei denn, du brauchst das wirklich.
Vertraue weder der Dateiendung noch dem Content-Type-Header vom Client. Beides ist leicht zu fälschen. Eine Datei namens invoice.pdf kann ausführbar sein, und Content-Type: image/png kann gelogen sein.
Stärker ist, die ersten Bytes der Datei zu prüfen, oft „magic bytes“ oder Dateisignatur genannt. Viele Formate haben konsistente Header (wie PNG und JPEG). Wenn der Header nicht dem entspricht, was du erlaubst, lehne ab.
Ein praktisches Validierungs-Setup:
- Allowlist der Extensions, die du akzeptierst (serverseitige Liste)
- MIME-Typ serverseitig erkennen (nicht vom Client-Header abhängig)
- Magic Bytes für unterstützte Formate prüfen
- Einen neuen zufälligen Speicher-Namen erzeugen und den Originalnamen als Metadaten speichern
- Riskante Formate blocken, es sei denn, du brauchst sie wirklich, besonders HTML, SVG und skriptähnliche Inhalte
Das Umbenennen ist wichtiger, als es klingt. Wenn du nutzereigene Namen direkt speicherst, lädst du Pfad-Tricks, seltsame Zeichen und unbeabsichtigtes Überschreiben ein. Verwende eine generierte ID für den Storage und behalte den Originalnamen nur zur Anzeige.
Für Profilbilder: nur JPEG und PNG akzeptieren, Header verifizieren und Metadaten entfernen, wenn möglich. Für Dokumente: evtl. nur PDF erlauben und alles mit aktiven Inhalten ablehnen. Wenn du später SVG oder HTML erlaubst, behandle sie als potenziell ausführbar und isoliere sie.
Größenlimits, Rate-Limits und DoS-Basics
Die meisten Upload-Ausfälle sind keine „raffinierten Hackerangriffe“. Es sind riesige Dateien, zu viele Requests oder langsame Verbindungen, die Server blockieren, bis die App nicht mehr reagiert. Behandle jedes Byte als Kostenfaktor.
Setze Größengrenzen, die tatsächlich wirken
Wähle ein maximales Size-Limit pro Feature, nicht nur eine globale Zahl. Ein Avatar braucht nicht dasselbe Limit wie ein Steuerdokument oder ein kurzes Video. Setze das kleinstmögliche Limit, das noch normal erscheint, und füge nur bei echtem Bedarf einen separaten „Large Upload“-Weg hinzu.
Durchsetze Limits an mehreren Stellen, weil Clients lügen können: in der App-Logik, am Webserver oder Reverse Proxy, mit Upload-Timeouts und durch frühe Ablehnung, wenn die deklarierte Größe zu groß ist (bevor du den ganzen Body liest).
Konkretes Beispiel: Avatare auf 2 MB begrenzen, PDFs auf 20 MB, und alles Größere braucht einen anderen Flow (z. B. Direct-to-Object-Storage mit signierter URL).
Rate-Limits und Abuse-Controls
Auch kleine Dateien können zu DoS werden, wenn jemand sie in einer Schleife hochlädt. Füge Rate-Limits für Upload-Endpunkte pro Nutzer und pro IP hinzu. Erwäge strengere Limits für anonymen Traffic im Vergleich zu eingeloggten Nutzern.
Resumable Uploads helfen echten Nutzern mit schlechten Netzen, aber das Session-Token muss eng sein: kurze Gültigkeit, an den Nutzer gebunden und an eine bestimmte Dateigröße und ein Ziel gebunden. Sonst wird der „Resume“-Endpunkt zur kostenlosen Rohrleitung in deinen Speicher.
Wenn du einen Upload blockst, gib klare, nutzerfreundliche Fehler zurück (Datei zu groß, zu viele Anfragen), aber leak keine Interna (Stacktraces, Bucket-Namen, Vendor-Details).
Sichere Speicher- und Auslieferungsentscheidungen
Sichere Uploads betreffen nicht nur, was du akzeptierst. Es geht auch darum, wohin die Datei geht und wie du sie später zurückgibst.
Halte Upload-Bytes aus deiner Hauptdatenbank heraus. Die meisten Apps brauchen nur Metadaten in der DB (Owner-User-ID, Originaldateiname, ermittelter Typ, Größe, Checksumme, Storage-Key, Erstellungszeit). Speichere die Bytes in Objektspeicher oder einem File-Service, der für große BLOBs gebaut ist.
Trenne public und private Dateien auf Storage-Ebene. Verwende verschiedene Buckets oder Container mit unterschiedlichen Regeln. Öffentliche Dateien (z. B. öffentliches Avatar) können ohne Login lesbar sein. Private Dateien (Verträge, Rechnungen, medizinische Dokumente) dürfen niemals öffentlich lesbar sein, auch nicht, wenn jemand die URL errät.
Vermeide es, Nutzerdateien von derselben Domain wie deine App auszuliefern, wenn möglich. Wenn eine riskante Datei durchrutscht (HTML, SVG mit Skripten oder Browser-MIME-Sniffing-Anomalien), kann das Hosten auf deiner Hauptdomain in eine Account-Übernahme münden. Eine eigene Download-Domain (oder Storage-Domain) begrenzt den Schaden.
Beim Download zwinge sichere Header. Setze einen vorhersehbaren Content-Type basierend auf dem, was du erlaubst, nicht auf das, was der Nutzer behauptet. Bei allem, was vom Browser interpretiert werden könnte, schicke es lieber als Download.
Ein paar Defaults, die Überraschungen vermeiden:
- Verwende
Content-Disposition: attachmentfür Dokumente. - Nutze einen sicheren
Content-Type(oderapplication/octet-stream). - Speichere und liefere mit undurchsichtigen Objekt-Keys (nicht Benutzerdateinamen).
- Protokolliere Downloads privater Dateien.
Retention ist auch Sicherheit. Lösche verwaiste Uploads, entferne alte Versionen nach Ersatz und setze Zeitlimits für temporäre Dateien. Weniger gespeicherte Daten bedeuten weniger, was geleakt werden kann.
Signierte URLs: wann und wie du sie eng hältst
Signierte URLs (oft pre-signed URLs genannt) sind ein übliches Mittel, um Nutzern Uploads oder Downloads zu erlauben, ohne dein Storage-Bucket öffentlich zu machen und ohne jeden Byte über deine API zu schicken. Die URL trägt temporäre Berechtigung und läuft dann ab.
Zwei gängige Flows:
- Direct-to-storage Upload: deine App gibt eine kurzlebige signierte URL aus und der Browser lädt direkt in den Objektspeicher.
- Upload-via-Server: die Datei landet zuerst auf deiner API, dann speichert dein Server sie.
Direct-to-storage reduziert die API-Last, macht aber Storage-Regeln und URL-Constraints wichtiger.
So hältst du signierte URLs eng
Behandle eine signierte URL wie einen Einmalschlüssel. Mach sie spezifisch und kurzlebig.
- Lasse Schreib-URLs schnell verfallen (oft 1–5 Minuten). Lese-URLs in Minuten, nicht Tagen.
- Binde die URL an den exakten Objekt-Key, den du erwartest (ein Objekt, nicht ein Ordner).
- Füge Constraints hinzu, wo unterstützt: erwarteter Content-Type, max. Größe, Checksumme.
- Gib URLs nur nach Berechtigungsprüfungen aus.
- Protokolliere, wer die URL angefordert hat und warum (User-ID, Objekt-Key, Zweck, IP/User-Agent).
Ein praktisches Muster ist: Lege zuerst einen Upload-Eintrag an (Status: pending), dann gib die signierte URL aus. Nach dem Upload bestätige, dass das Objekt existiert und erwartete Größe und Typ hat, bevor du es als bereit markierst.
Schritt-für-Schritt: ein sicherer Upload-Flow zum Implementieren
Ein sicherer Upload-Flow ist vor allem klare Regeln und klarer Zustand. Behandle jeden Upload als untrusted, bis die Prüfungen abgeschlossen sind.
Schreibe auf, was jedes Feature erlaubt. Ein Profilfoto und ein Steuerdokument sollten nicht dieselben Dateitypen, Größenlimits oder Sichtbarkeiten teilen.
Ein praktischer Flow (mit echten Status)
-
Definiere erlaubte Typen und ein per-Feature Größenlimit (z. B.: Fotos bis 5 MB; PDFs bis 20 MB). Erzwinge dieselben Regeln im Backend.
-
Erstelle einen „Upload-Record“, bevor Bytes ankommen. Speichere: Eigentümer (User oder Org), Zweck (avatar, invoice, attachment), Originaldateiname, erwartete Max-Größe und einen Status wie
pending. -
Upload in einen privaten Bereich. Lass den Client nicht den finalen Pfad wählen.
-
Validere erneut serverseitig: Größe, Magic-Bytes/Typ, Allowlist. Wenn bestanden, setze Status auf
uploaded. -
Scanne auf Malware und aktualisiere den Status zu
cleanoderquarantined. Wenn das Scanning asynchron ist, bleibe beim Sperrstatus, bis es abgeschlossen ist. -
Erlaube Download, Vorschau oder Verarbeitung erst, wenn der Status
cleanist.
Kleines Beispiel: Für ein Profilbild erstellst du einen Datensatz, der an den Nutzer und den Zweck avatar gebunden ist, speicherst es privat, bestätigst, dass es wirklich JPEG/PNG ist (nicht nur so benannt), scannst es und generierst dann eine Vorschau-URL.
Basis-Malware-Scanning-Patterns (ohne überzogene Versprechungen)
Malware-Scanning ist ein Sicherheitsnetz, kein Versprechen. Es fängt bekannte schlechte Dateien und offensichtliche Tricks ab, erkennt aber nicht alles. Das Ziel ist einfach: Risiko reduzieren und unbekannte Dateien standardmäßig harmlos machen.
Ein zuverlässiges Muster ist: zuerst quarantänen. Speichere jeden neuen Upload in einem privaten, nicht öffentlichen Ort und markiere ihn als pending. Erst nach Prüfungen verschiebst du ihn in einen „clean“-Ort oder markierst ihn als verfügbar.
Synchrones Scanning funktioniert nur für kleine Dateien und niedrigen Traffic, weil der Nutzer warten muss. Die meisten Apps scannen asynchron: akzeptiere den Upload, gib einen „processing“-Status zurück, scanne im Hintergrund.
Was „Basic Scanning“ typischerweise beinhaltet
Basic Scanning ist üblicherweise eine Antivirus-Engine (oder ein Service) plus einige Guardrails: AV-Scan, Dateityp-Prüfungen (Magic Bytes), Archiv-Limits (Zip-Bombs, verschachtelte Zips, enorme entpackte Größe) und das Blocken von Formaten, die du nicht brauchst.
Wenn der Scanner fehlschlägt, timeoutet oder „unknown“ meldet, behandle die Datei als verdächtig. Quarantäne sie und gib keinen Download-Link frei. Hier verbrennen Teams sich oft: „Scan fehlgeschlagen“ darf nicht zu „trotzdem ausliefern“ werden.
Wenn du eine Datei blockierst, formuliere die Meldung neutral: „Wir konnten diese Datei nicht akzeptieren. Versuche eine andere Datei oder kontaktiere den Support.“ Behaupte nicht, du hättest Malware detektiert, es sei denn, du bist dir sicher.
Beispiel: Profilfoto- und Dokument-Upload in einer typischen App
Betrachte zwei Features: ein Profilfoto (öffentlich angezeigt) und eine PDF-Quittung (privat, für Abrechnung oder Support). Beides sind Upload-Probleme, aber sie sollten nicht dieselben Regeln teilen.
Für das Profilfoto: streng bleiben: nur JPEG/PNG erlauben, Größenbegrenzung (z. B. 2–5 MB), serverseitig neu kodieren, damit du nicht die ursprünglichen Bytes auslieferst. Nur nach Prüfungen öffentlich speichern.
Für die PDF-Quittung: größere Größe erlauben (z. B. bis 20 MB), standardmäßig privat halten und vermeiden, sie inline von deiner Haupt-App-Domain zu rendern.
Ein einfaches Status-Modell hält Nutzer informiert, ohne Interna preiszugeben:
- pending: Nutzer hat eine Datei ausgewählt, Upload noch nicht gestartet
- uploaded: Storage hat die Bytes erhalten
- scanning: Hintergrundjob prüft die Datei
- clean (oder rejected): Datei ist verfügbar (oder blockiert)
Signierte URLs fügen sich gut ein: nutze eine kurzlebige signierte URL für den Upload (write-only, ein Objekt-Key). Gib eine separate kurzlebige signierte URL für das Lesen aus und nur, wenn der Status clean ist.
Protokolliere, was du für Untersuchungen brauchst, nicht die Datei selbst: User-ID, File-ID, Typ-Vermutung, Größe, Storage-Key, Zeitstempel, Scan-Ergebnis, Request-IDs. Vermeide es, rohen Inhalt oder sensible Daten aus Dokumenten zu loggen.
Häufige Fehler und einfache Fallen
Die meisten Upload-Bugs entstehen, weil eine kleine „temporäre“ Abkürzung dauerhaft wird. Geh davon aus, dass jede Datei untrusted ist, jede URL geteilt wird und jede „wir fixen das später“-Einstellung vergessen wird.
Wiederkehrende Fallen:
- Verlassen auf clientseitige Prüfungen allein. Browser lassen sich in Sekunden umgehen.
- Nutzern Einfluss auf Pfade, Dateinamen oder Objekt-Keys geben.
- Uploads kurzzeitig öffentlich machen „nur für einen Moment".
- Signierte URLs mit zu langer Lebensdauer oder für mehrere Nutzer funktionieren lassen.
- Dateien mit falschem
Content-Typeausliefern und dem Browser erlauben, riskante Inhalte zu interpretieren.
Monitoring ist etwas, das Teams überspringen, bis die Storage-Rechnung explodiert. Verfolge Upload-Volumen, Durchschnittsgröße, Top-Uploader und Fehlerquoten. Ein kompromittiertes Konto kann über Nacht heimlich tausende große Dateien hochladen.
Beispiel: Ein Team speichert Avatare unter nutzergenerierten Namen wie „avatar.png“ in einem gemeinsamen Ordner. Ein Nutzer überschreibt die Bilder anderer. Die Lösung ist langweilig, aber effektiv: generiere Objekt-Keys serverseitig, halte Uploads standardmäßig privat und liefere ein skaliertes Bild über eine kontrollierte Antwort aus.
Schnelle Checkliste und nächste Schritte
Nutze das als finalen Durchgang, bevor du shippst. Behandle jeden Punkt als Release-Blocker, denn die meisten Vorfälle kommen von einer fehlenden Leitplanke.
Schnelle Checkliste
- Validierung auf dem Server mit Allowlist, echter Inhaltsprüfung (nicht nur Dateiname) und harten Max-Größen pro Datei.
- Uploads standardmäßig privat speichern und Berechtigungen bei jedem Lesen/Download/Vorschau prüfen.
- Wenn du signierte URL-Uploads nutzt: kurzlebig, auf einen Objekt-Key begrenzt und Ausgabe protokollieren, damit du Missbrauch nachverfolgen kannst.
- Erst quarantänen, dann scannen: keine Vorschauen oder Downloads, bis die Datei clean ist.
- Sichere Download-Verhalten erzwingen: vorhersehbarer
Content-Type, sichere Dateinamen undattachmentfür Dokumente.
Nächste Schritte, die sich lohnen
Schreibe deine Regeln in einfacher Sprache auf: erlaubte Typen, Max-Größen, wer auf was zugreifen darf, wie lange signierte URLs leben und was „Scan bestanden“ bedeutet. Das wird zum gemeinsamen Vertrag zwischen Produkt, Engineering und Support.
Füge ein paar Tests hinzu, die häufige Fehler auffangen: übergroße Dateien, umbenannte Executables, unautorisierte Reads, abgelaufene signierte URLs und „Scan pending“-Downloads. Diese Tests sind billig im Vergleich zu einem Vorfall.
Wenn du schnell baust und iterierst, hilft ein Workflow, in dem du Änderungen planen und sicher zurückrollen kannst. Teams, die Koder.ai (koder.ai) verwenden, nutzen oft Planungsmodus und Snapshots/Rollback beim Verschärfen der Upload-Regeln über die Zeit, aber die Kernanforderung bleibt: die Policy wird im Backend durchgesetzt, nicht in der UI.
FAQ
Was ist das Minimum, das ich tun sollte, damit Datei-Uploads „sicher genug“ sind?
Beginne mit standardmäßig privat und behandle jeden Upload als untrusted input. Setze serverseitig vier Grundlagen durch:
- Wer darf hochladen
- Welche Dateitypen du akzeptierst (Allowlist/Positivliste)
- Wie groß/wie oft (Größen- und Rate-Limits)
- Wer die Datei später lesen darf (pro Datei Berechtigungsprüfungen)
Wenn du diese Fragen klar beantworten kannst, bist du den meisten Vorfällen schon voraus.
Warum sind Datei-Uploads ein häufiger erster Sicherheitsvorfall?
Weil Nutzer eine „Wundertüte“ hochladen können, die dein System speichert und später anderen ausliefert. Das kann zu führen zu:
- Unbefugtem Zugriff auf private Dokumente
- Phishing oder Account-Übernahme, wenn eine Datei als vertrauenswürdiger Webinhalt ausgeliefert wird
- Ausfällen und hohen Kosten durch Upload-Fluten oder riesige Dateien
Es ist selten nur „jemand hat ein Virus hochgeladen“.
Was ist der Unterschied zwischen Dateien speichern und Dateien ausliefern, und warum ist das wichtig?
Speichern bedeutet, die Bytes irgendwo abzulegen. Ausliefern bedeutet, wie diese Bytes an Browser oder Apps gesendet werden.
Die Gefahr entsteht, wenn deine App Nutzer-Uploads mit dem gleichen Vertrauen und den gleichen Regeln ausliefert wie die Hauptseite. Wenn eine riskante Datei wie eine normale Seite behandelt wird, kann der Browser sie ausführen (oder Nutzer schenken ihr zu viel Vertrauen).
Eine sichere Default-Strategie ist: zunächst privat speichern und dann über kontrollierte Download-Antworten mit sicheren Headern ausliefern.
Wie verhindere ich, dass Nutzer die Datei eines anderen herunterladen?
Verwende default deny und prüfe den Zugriff bei jedem Download oder jeder Vorschau.
Praktische Regeln:
- Jede Datei muss einen Owner (User/Org) und einen Zweck (Avatar, Rechnung, Anhang) haben
- Prüfe beim Lesen/Download, ob die anfragende Person für diese spezifische Datei berechtigt ist
- Vermeide ordnerbasierte Sicherheit wie „alles unter /uploads/ ist OK“
- Supportzugriff sollte zeitlich begrenzt und protokolliert sein (Zugriff auf eine Datei, automatisch ablaufend)
Die meisten echten Bugs sind einfache „Ich sehe die Datei eines anderen“-Fehler.
Wie validiere ich den Dateityp, ohne der Dateiendung oder dem Content-Type zu vertrauen?
Vertraue weder der Dateiendung noch dem Content-Type des Browsers. Validierung gehört auf den Server:
- Nutze eine Allowlist von Formaten pro Feature (z. B. JPEG/PNG für Avatare, PDF für Belege)
- Erkenne den Typ serverseitig und prüfe die Magic Bytes (Dateisignatur)
- Benenne Dateien für die Speicherung mit einer zufälligen ID; behalte den Originalnamen nur als Metadaten
- Blockiere riskante Formate, die du nicht brauchst (insbesondere HTML, SVG, skriptähnliche Inhalte)
Wenn die Bytes nicht zu einem erlaubten Format passen, lehne den Upload ab.
Welche Limits sollte ich setzen, um DoS-Angriffe über Uploads zu verhindern?
Ausfälle resultieren häufig aus langweiligem Missbrauch: zu viele Uploads, riesige Dateien oder langsame Verbindungen, die Server blockieren.
Gute Defaults:
- Setze pro-Feature Maximalgrößen (Avatare klein, Dokumente größer)
- Erzwinge Limits auf mehreren Ebenen (App + Reverse Proxy + Timeouts)
- Füge Rate-Limits pro Nutzer und pro IP hinzu, mit strengeren Regeln für anonyme Zugriffe
Behandle jedes Byte als Kostenfaktor und jede Anfrage als potenziellen Missbrauch.
Sollte ich signierte URLs für Uploads verwenden und was ist der sicherste Default?
Ja, aber mit Vorsicht. Signierte URLs erlauben Browsern, direkt in den Objektspeicher zu laden/zu lesen, ohne das Bucket öffentlich zu machen.
Gute Defaults:
- Schreibe Schreib-URLs kurzlebig (häufig 1–5 Minuten)
- Scope jede URL auf einen Objekt-Key, nicht auf einen Ordner
- Gib URLs nur nach Berechtigungsprüfungen aus
- Protokolliere, wer die URL angefordert hat und für welche Datei
Direct-to-storage reduziert API-Load, macht aber gutes Scoping und kurze Laufzeiten notwendig.
Was ist ein sicherer Schritt-für-Schritt Upload-Flow, den ich umsetzen kann?
Das sicherste Muster ist:
- Erstelle vor dem Hochladen einen Upload-Datensatz mit Status
pending - Lade Bytes in einen privaten Speicherort hoch
- Validere serverseitig Größe + Typ (Magic Bytes)
- Scanne (meist asynchron) und setze Status auf
cleanoderquarantined - Erlaube Download/Vorschau erst, wenn Status
cleanist
So verhinderst du, dass „Scan fehlgeschlagen“ oder „wird noch verarbeitet“ versehentlich geteilt werden.
Brauche ich wirklich Malware-Scanning und wie sieht „basic scanning“ aus?
Scanning ist nützlich, aber keine Garantie. Nutze es als Sicherheitsnetz, nicht als alleiniges Kontrollwerkzeug.
Praktischer Ansatz:
- Zuerst quarantänen: Links erst freigeben, wenn der Scan abgeschlossen ist
- Scans asynchron durchführen für Skalierbarkeit; zeige dem Nutzer „wird verarbeitet“
- Wenn ein Scan fehlschlägt oder timeoutet, behandle die Datei als verdächtig und blockiere sie
- Füge Guardrails für Archive hinzu (Zip-Bombs, riesige entpackte Größen), wenn du Archive erlaubst
Wichtig ist die Policy: „nicht gescannt“ darf nie „verfügbar“ bedeuten.
Wie sollte ich hochgeladene Dateien sicher ausliefern (Header, Domains, Downloads)?
Liefere Dateien so, dass der Browser sie nicht als Webseiten interpretiert.
Gute Defaults:
- Setze
Content-Disposition: attachmentfür Dokumente - Verwende einen sicheren, servergewählten
Content-Type(oderapplication/octet-stream) - Nutze undurchsichtige Speicher-Keys (nicht Benutzernamen oder Dateinamen) in URLs
- Bevorzuge eine separate Download-Domain für Nutzerinhalte, wenn möglich
Das reduziert das Risiko, dass eine hochgeladene Datei zu einer Phishing-Seite oder zur Ausführung von Skripten wird.