Rutas Dinámicas y Autenticación con Cookies en net/http: Implementando DeadDrop
Continuando nuestra serie sobre net/http puro, implementamos enrutamiento dinámico para el servicio DeadDrop. Los usuarios podrán ver secretos a través de enlaces como /secret/{id}. El mux estándar no admite parámetros de ruta hasta Go 1.22, así que escribimos nuestro propio analizador.
El manejador se registra en /secret/ con una barra diagonal final — esto captura todas las rutas hijas. Extraemos el ID manualmente de 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
}
La función verifica el prefijo, lo recorta y excluye IDs inválidos (vacíos o que contienen /). Esto proporciona protección básica contra solicitudes malformadas.
SecretHandler con Inyección de Dependencias
Creamos una estructura de manejador para inyección de dependencias:
type SecretHandler struct {
// marcador de posición para 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("Secreto solicitado con ID: " + id))
}
Regístralo en router.go: mux.Handle("/secret/", secretHandler). Prueba casos límite:
/secret/12345/→ 404/secret/→ 404/secret/axQ/233→ 404/secret/ad31H234→ OK con ID
Caso de Uso para Recuperar un Secreto
Define la solicitud y respuesta:
type GetSecretRequest struct {
ID string
Password string
}
type GetSecretResponse struct {
ID string
Message string
FileName string
FileExt string
HasFile bool
ExpiresAt time.Time
}
Lógica del caso de uso:
- Recupera el secreto del almacenamiento
- Verifica la expiración (
time.After()), elimina los expirados - Valida la contraseña mediante
CheckPasswordHash - Devuelve los datos o un error
Autenticación con Cookies
Para proteger las descargas de archivos, implementamos autenticación mediante cookies. Después de una verificación exitosa de la contraseña, establece una cookie de sesión con un token de acceso cifrado.
- Ventajas: sin estado, escalable, sin almacenamiento de sesión
- Parámetros de la cookie: HttpOnly, Secure, SameSite=Strict
Al solicitar /download/{id}:
- Extrae el ID de la ruta
- Lee la cookie
auth_token - Decodifica y verifica el token (ID + hash de contraseña + nonce)
- Si es válido — sirve el archivo
Estructura del Token de Acceso
El token se forma como base64(ID + ":" + hash + ":" + nonce), donde:
hash = HMAC-SHA256(ID + contraseña, claveSecreta)nonce— un número de un solo uso para proteger contra ataques de repetición
Ejemplo de validación:
func validateDownloadToken(id, token string) bool {
// decodifica el token
// extrae el hash y el nonce
// recalcula HMAC
// compara
return true // o false
}
Flujo Completo del Secreto
- Creación: POST
/create→ genera ID, hashea contraseña, guarda - Visualización: GET
/secret/{id}→ formulario de entrada de contraseña - Autenticación: POST contraseña → cookie + redirección
- Descarga: GET
/download/{id}→ verifica cookie → archivo - Limpieza: TTL expirado → eliminación
Prototipo de interfaz: página con campo de contraseña, botón "Abrir". Después del éxito — contenido + enlace al archivo.
Pruebas de Enrutamiento
| Ruta de Prueba | Expectativa | Resultado |
|-----------|-------------|--------|
| /secret/123/ | 404 | 404 |
| /secret/ | 404 | 404 |
| /secret/id/with/slash | 404 | 404 |
| /secret/valid123 | OK: "ID: valid123" | OK |
Los casos límite cubren la seguridad del analizador.
Puntos Clave
- Rutas dinámicas: análisis manual de
r.URL.Pathsin enrutadores de terceros - Seguridad: validación de ID, verificaciones de barras diagonales, TTL para secretos
- Autenticación: cookie con token HMAC en lugar de sesiones
- Arquitectura: caso de uso + DI, lógica limpia fuera de los manejadores
- Escalabilidad: sin estado, listo para agrupamiento
— Editorial Team
Aún no hay comentarios.