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.
Представьте электронный замок в отеле, запрограммированный открывать только номера на втором этаже. Если гость вводит на панели '../../master-safe', уязвимый замок выходит в коридор и открывает главный сейф управляющего.
Web Application SecurityCWE-918.CWE-918CWE-918): Standard Common Weakness Enumeration classification for path-traversal-go.Defense-in-DepthКонечная точка принимает имя файла или путь к документу через параметр HTTP-запроса.
Злоумышленник передает последовательности '../', '..%2f' или абсолютные пути для выхода из базового каталога.
Бэкенд конкатенирует путь без канонизации и проверки того, что результирующий путь находится внутри базовой папки.
Сервер считывает и передает клиенту конфиденциальные файлы операционной системы (например, /etc/passwd).
// 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.