Dynamické trasy a cookie autentizace v net/http: implementace DeadDrop
V pokračování cyklu o čistém net/http implementujeme dynamické směrování pro službu DeadDrop. Uživatelé budou moci prohlížet tajemství pomocí odkazů ve tvaru /secret/{id}. Standardní mux nepodporuje parametry cest až do Go 1.22, proto píšeme vlastní parser.
Ošetřovač se registruje na /secret/ s koncovým lomítkem – to zachytí všechny podřízené cesty. ID extrahujeme ručně z r.URL.Path.
func extractIDFromPath(path string) (string, bool) {
const prefix = "/secret/"
if !strings.HasPrefix(path, prefix) {
return "", false
}
id := strings.TrimPrefix(path, prefix)
if id == "" || strings.Contains(id, "/") {
return "", false
}
return id, true
}
Funkce kontroluje prefix, ořízne ho a vyloučí neplatná ID (prázdná nebo s /). Toto je základní ochrana proti nesprávným požadavkům.
Ošetřovač SecretHandler s DI
Vytvoříme strukturu-ošetřovač pro vkládání závislostí:
type SecretHandler struct {
// zástupný symbol pro DI
}
func NewSecretHandler() *SecretHandler {
return &SecretHandler{}
}
func (h *SecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
id, ok := url.ExtractIDFromPath(r.URL.Path)
if !ok {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write([]byte("Požadováno tajemství s ID: " + id))
}
Zaregistrujeme v router.go: mux.Handle("/secret/", secretHandler). Otestujeme hraniční případy:
/secret/12345/→ 404/secret/→ 404/secret/axQ/233→ 404/secret/ad31H234→ OK s ID
Use Case pro získání tajemství
Definujeme požadavek a odpověď:
type GetSecretRequest struct {
ID string
Password string
}
type GetSecretResponse struct {
ID string
Message string
FileName string
FileExt string
HasFile bool
ExpiresAt time.Time
}
Logika use case:
- Extrahujeme tajemství z úložiště
- Kontrolujeme dobu platnosti (
time.After()), odstraníme vypršená - Ověříme heslo pomocí
CheckPasswordHash - Vrátíme data nebo chybu
Cookie autentizace
Pro ochranu stahování souborů zavádíme autentizaci pomocí cookies. Po úspěšném ověření hesla nastavíme sezení cookie se zašifrovaným přístupovým tokenem.
- Výhody: bezstavové, škálovatelné, žádné ukládání sezení
- Parametry cookie: HttpOnly, Secure, SameSite=Strict
Při požadavku /download/{id}:
- Extrahujeme ID z cesty
- Přečteme cookie
auth_token - Dekódujeme a ověříme token (ID + hash hesla + nonce)
- Při platnosti – poskytneme soubor
Struktura přístupového tokenu
Token je vytvořen jako base64(ID + ":" + hash + ":" + nonce), kde:
hash = HMAC-SHA256(ID + password, secretKey)nonce– jednorázové číslo pro ochranu proti replay útokům
Příklad validace:
func validateDownloadToken(id, token string) bool {
// dekódujeme token
// extrahujeme hash a nonce
// přepočítáme HMAC
// porovnáme
return true // nebo false
}
Úplný cyklus práce s tajemstvím
- Vytvoření: POST
/create→ vygenerujeme ID, zahashujeme heslo, uložíme - Prohlížení: GET
/secret/{id}→ formulář pro zadání hesla - Autentizace: POST heslo → cookie + přesměrování
- Stahování: GET
/download/{id}→ kontrola cookie → soubor - Vyčištění: TTL vypršelo → odstranění
Prototyp UI: stránka s polem pro heslo, tlačítko "Otevřít". Po úspěchu – obsah + odkaz na soubor.
Testování směrování
| Testovací cesta | Očekávání | Výsledek |
|---------------|----------|-----------|
| /secret/123/ | 404 | 404 |
| /secret/ | 404 | 404 |
| /secret/id/with/slash | 404 | 404 |
| /secret/valid123 | OK: "ID: valid123" | OK |
Hraniční případy pokrývají bezpečnost parseru.
Co je důležité
- Dynamické trasy: ruční parsování
r.URL.Pathbez externích routerů - Bezpečnost: validace ID, kontrola lomítek, TTL pro tajemství
- Autentizace: cookie s HMAC tokenem místo sezení
- Architektura: use case + DI, čistá logika mimo handler
- Škálovatelnost: bezstavové, připraveno pro cluster
— Editorial Team
Zatím žádné komentáře.