flawopen.com/path-traversal/Go

● CWE-918 · Hoch
Sicherheitsforschung · FlawOpen

Path Traversal in Go

How filepath.Join in Go fails to restrict file boundaries, and how to enforce containment using filepath.Clean and filepath.Rel.

💡 Einfache Erklärung (ELI5)

Stellen Sie sich ein Hotelschloss vor, das eigentlich nur Zimmer im zweiten Stock öffnen soll. Wenn ein Gast am Tastenfeld '../../master-safe' eingibt, verlässt das Schloss den Flur und öffnet den Haupttresor des Hotelmanagers.

Kernkonzepte & Begriffe

Web Application Security
Kernkomponente der Architektur, die von CWE-918 betroffen ist.
CWE-918
Standardmäßige Common Weakness Enumeration (CWE)-Klassifizierung für path-traversal-go.
Defense-in-Depth
Mehrschichtige technische Verifikation und Laufzeit-Grenzenisolation.

Schritt-für-Schritt Angriffsablauf

Step 1

Eingabe des Client-Dateipfads

Ein Endpunkt akzeptiert Dateinamen oder Ressourcenbezeichner direkt über einen HTTP-Anfrageparameter.

Step 2

Injektion von Verzeichnis-Traversierungssequenzen

Der Angreifer schleust relative Pfadsequenzen wie '../', '..%2f' oder absolute Pfadüberschreibungen ein.

Step 3

Umgehung der Verzeichnisgrenze

Das Backend verbindet die Eingabe mit dem Basispfad, ohne kanonische Pfade aufzulösen oder Grenzen zu prüfen.

Step 4

Unbefugtes Lesen oder Überschreiben von Dateien

Das System öffnet und übermittelt vertrauliche Systemdateien (z. B. /etc/passwd oder Konfigurationsgeheimnisse).

Quellcode: Verwundbar vs. Sicher

✕ VERWUNDBARE IMPLEMENTIERUNG
// 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)
}
✓ GEHÄRTETER SICHERHEITS-PATCH
// 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)
}

Checkliste für Engineering & Systemsicherheit

References