Ochrona przesyłania plików w Go: ochrona przed 7 typowymi atakami
W systemach bez tradycyjnego uwierzytelniania, przesyłanie plików wymaga niezawodnej identyfikacji źródła żądań. Dla rozszerzenia przeglądarkowego PDF-to-Markdown zaimplementowano model oparty na ECDSA P-256 przez WebCrypto API. Podczas pierwszego uruchomienia generowana jest nieeksportowalna para kluczy:
const keyPair = await crypto.subtle.generateKey(
{ name: 'ECDSA', namedCurve: 'P-256' },
false, // extractable = false
['sign', 'verify']
);
Klucz publiczny rejestrowany jest na serwerze, a użytkownik otrzymuje device_id i device_token. Każde żądanie API zawiera:
Authorization: Bearer <token>X-Timestamp(±5 minut)X-Nonce(przeciwdziałanie replay)X-Body-SHA256X-Signature(podpis ECDSA nad METHOD+PATH+TIMESTAMP+NONCE+BODY_HASH)
Serwer weryfikuje podpis za pomocą klucza publicznego. Zapewnia to:
- Limitowanie szybkości na urządzenie
- Ochronę przed atakami replay
- Kontrolę slotów (maks. 3 aktywne zadania)
- Ograniczenie rejestracji (5/IP/godzina)
Model zwiększa koszt ataku: emulacja wymaga pełnej implementacji podpisów ECDSA.
Atak 1: Fałszowanie typu pliku
Atakujący maskuje plik wykonywalny jako PDF. Ochrona działa na trzech poziomach.
Frontend: Sprawdzenie MIME i rozszerzenia:
const ALLOWED_TYPES = ['application/pdf'];
const ALLOWED_EXTENSIONS = ['.pdf'];
function validateFile(file) {
const isPdf = ALLOWED_TYPES.includes(file.type) ||
ALLOWED_EXTENSIONS.some(ext =>
file.name.toLowerCase().endsWith(ext));
if (!isPdf) return { valid: false, error: 'Tylko PDF' };
}
Backend: Routing na podstawie Content-Type. Kluczowe: Magic bytes:
pdfMagicBytes := "%PDF"
header4 := make([]byte, 4)
_, err := file.Read(header4)
if string(header4) != pdfMagicBytes {
respondError(w, http.StatusBadRequest,
models.ErrCodeValidationError,
"Plik nie jest poprawnym PDFem")
}
file.Seek(0, 0)
Sprawdzenie pierwszych bajtów ignoruje nagłówki i rozszerzenia. Pliki poliglotyczne (poprawne PDF + szkodliwe) wymagają dodatkowych sygnatur.
Atak 2: Przepięcie dysku
DoS przez duże pliki. Ograniczenia:
http.MaxBytesReader(w, r.Body, MaxFileSize + 1MB)— przerwanie odczytu przy limicie.- Sloty na urządzenie: maks. 3 aktywne zadania (
queued,processing,ready,error). - Limity szybkości dla rejestracji i żądań.
Identyfikacja urządzenia sprawia, że zmiana IP/tokena jest bezcelowa.
Atak 3: Przejście przez ścieżkę (Path Traversal)
Atakujący używa ../../../etc/passwd. Rozwiązanie: Stała nazwa {UUID}/input.pdf. Wprowadzane dane użytkownika w ścieżce są całkowicie ignorowane.
Sanitizacja frontendu (dodatkowo):
function sanitizeFileName(name) {
return name
.replace(/[/\\?%*:|"<>]/g, '-')
.replace(/^\.+/, '')
.replace(/\.+$, '')
.substring(0, 255)
.trim();
}
Backend generuje ścieżkę niezależnie od nazwy pliku.
Atak 4: SSRF przez pobieranie z URL
Dla kanału "pobieranie z URL" blokowane są:
- Rozwiązywanie DNS przed pobraniem
- Przekierowania
- Prywatne adresy IP (denylist: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
Atak 5: Ataki replay
X-Nonce + X-Timestamp + podpis. Nonce przechowywane per urządzenie, timestamp w oknie ±5 minut.
Atak 6: Fałszerstwo urządzenia
extractable: false sprawia, że klucz prywatny nie może zostać wyeksportowany. Zabranie device_token jest bezużyteczne bez podpisu.
Atak 7: Nadużycie na poziomie aplikacji
Limity szybkości + sloty + podpis. Maks. 3 sloty, 5 rejestracji/IP/godzina.
Co ważne
- Kryptograficzna identyfikacja per urządzenie zastępuje konta dla limitów.
- Magic bytes (
%PDF) to jedyna wiarygodna weryfikacja typu pliku. MaxBytesReader+ sloty chronią przed DoS.- Stałe ścieżki eliminują przejścia.
- ECDSA P-256 + nonce/timestamp blokują replay i fałszerstwo.
— Editorial Team
Brak komentarzy.