flawopen.com/path-traversal/Go

● CWE-918 · 高
セキュリティ研究 · 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.

💡 わかりやすい解説 (ELI5)

ホテルの客室カードリーダーが2階の部屋のみを開けるよう制限されている場面を例えに考えてみてください。もし宿泊客がドアのキーパッドに「../../master-safe」と入力すると、不備のある鍵が廊下を抜け出して支配人の金庫を直接解錠してしまいます。

主要な概念と専門用語

Web Application Security
主要概念 (Web Application Security):Core architecture component affected by CWE-918.
CWE-918
主要概念 (CWE-918):Standard Common Weakness Enumeration classification for path-traversal-go.
Defense-in-Depth
主要概念 (Defense-in-Depth):Multi-layered engineering verification and runtime boundary isolation.

ステップ・バイ・ステップの攻撃フロー

Step 1

ファイルパス入力の受信

エンドポイントがHTTPリクエストパラメータからユーザー指定のファイル名やパスを受け取ります。

Step 2

ディレクトリトラバーサル記号の挿入

攻撃者が '../' や '..%2f' などの相対パス記号や絶対パス指定を挿入します。

Step 3

ベースディレクトリ境界の突破

バックエンドが正規化パスの検証を行わずに文字列を連結し、公開フォルダ外へのアクセスを許容します。

Step 4

機密ファイルの漏洩または上書き

OSが /etc/passwd や設定ファイルなどの重要ファイルを読み取り、攻撃者へ返却します。

ソースコード比較:脆弱 vs 堅牢化

✕ 脆弱な実装
// 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)
}

エンジニアリング&システム堅牢化チェックリスト

References