flawopen.com/path-traversal/Go
How filepath.Join in Go fails to restrict file boundaries, and how to enforce containment using filepath.Clean and filepath.Rel.
Imagina un escáner de llaves de hotel programado para abrir únicamente habitaciones del segundo piso. Si un huésped escribe '../../master-safe' en el teclado de la puerta, la cerradura vulnerable sube por el pasillo y abre la caja fuerte principal del gerente.
Web Application SecurityCWE-918.CWE-918Defense-in-DepthUn endpoint acepta un nombre de archivo o identificador de recurso provisto por el usuario vía parámetro HTTP.
El atacante inyecta secuencias relativas como '../', '..%2f' o rutas absolutas en el nombre del archivo.
El backend concatena el archivo al directorio base sin resolver rutas canónicas ni validar límites seguros.
El servidor lee y transmite archivos confidenciales del sistema (/etc/passwd, claves de entorno) al atacante.
// VULNERABLE: filepath.Join does not prevent directory breakout
package main
import (
"net/http"
"os"
"path/filepath"
)
func downloadHandler(w http.ResponseWriter, r *http.Request) {
filename := r.URL.Query().Get("file")
baseDir := "/var/app/public/files"
// Attacker input: "../../etc/shadow"
targetPath := filepath.Join(baseDir, filename)
data, err := os.ReadFile(targetPath)
if err != nil {
http.NotFound(w, r)
return
}
w.Write(data)
}
// HARDENED: Use filepath.Rel to verify target is strictly inside baseDir
package main
import (
"net/http"
"os"
"path/filepath"
"strings"
)
func downloadHandler(w http.ResponseWriter, r *http.Request) {
filename := r.URL.Query().Get("file")
baseDir := "/var/app/public/files"
// 1. Resolve absolute paths
absBase, err := filepath.Abs(baseDir)
if err != nil {
http.Error(w, "Server error", http.StatusInternalServerError)
return
}
absTarget := filepath.Join(absBase, filepath.Clean("/"+filename))
// 2. Verify relative containment
rel, err := filepath.Rel(absBase, absTarget)
if err != nil || strings.HasPrefix(rel, "..") || rel == "." {
http.Error(w, "Access denied: Path Traversal detected", http.StatusForbidden)
return
}
data, err := os.ReadFile(absTarget)
if err != nil {
http.NotFound(w, r)
return
}
w.Write(data)
}
filepath.Clean().filepath.Rel().filepath.EvalSymlinks() where necessary.os.DirFS() which enforce chroot-like root boundaries.