Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Many file operations are intended to take place within a restricted directory. By using special elements such as '..' and '/' separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system.
Comment corriger cette vulnérabilité
Stratégies de prévention pour Path Traversal basées sur 6 règles de détection Shoulder.
Resolve the full path and verify it stays within the intended base directory
package main import ( "net/http" "os" - ) - - func handler(w http.ResponseWriter, r *http.Request) { - filename := r.URL.Query().Get("file") - // Vulnerable: user input directly in file path - data, err := os.ReadFile("/uploads/" + filename) + "path/filepath" + "strings" + ) + + const baseDir = "/uploads" + + func handler(w http.ResponseWriter, r *http.Request) { + filename := r.URL.Query().Get("file") + // Clean and resolve the path + cleanName := filepath.Clean(filename) + fullPath := filepath.Join(baseDir, cleanName) + absPath, _ := filepath.Abs(fullPath) + absBase, _ := filepath.Abs(baseDir) + // Verify path is within base directory + if !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) { + http.Error(w, "Forbidden", 403) + return + } + data, err := os.ReadFile(absPath) if err != nil { http.Error(w, "Not found", 404) return } w.Write(data) }
Validate that extracted archive paths resolve within the target directory
package main import ( "archive/zip" - "io" - "os" - ) - - func extractZip(zipPath, destDir string) error { - r, _ := zip.OpenReader(zipPath) - defer r.Close() - for _, f := range r.File { - // Vulnerable: using archive filename directly - outFile, _ := os.Create(f.Name) + "errors" + "io" + "os" + "path/filepath" + "strings" + ) + + func extractZip(zipPath, destDir string) error { + r, _ := zip.OpenReader(zipPath) + defer r.Close() + destDir = filepath.Clean(destDir) + string(os.PathSeparator) + for _, f := range r.File { + // Safe: validate path is within destination + destPath := filepath.Join(destDir, filepath.Clean(f.Name)) + if !strings.HasPrefix(destPath, destDir) { + return errors.New("illegal file path in archive: " + f.Name) + } + if f.FileInfo().IsDir() { + os.MkdirAll(destPath, 0755) + continue + } + outFile, _ := os.Create(destPath) rc, _ := f.Open() io.Copy(outFile, rc) rc.Close() outFile.Close() } return nil }
Use path.basename() to strip directory components or validate resolved paths stay within allowed directories
const express = require('express'); const fs = require('fs'); const path = require('path'); const app = express(); app.get('/files/:filename', (req, res) => { - const filePath = path.join(__dirname, 'uploads', req.params.filename); + const safeName = path.basename(req.params.filename); + const filePath = path.join(__dirname, 'uploads', safeName); fs.readFile(filePath, (err, data) => { if (err) return res.status(404).send('Not found'); res.send(data); }); });
Validate that extracted archive entry paths resolve within the target directory before writing
const express = require('express'); const AdmZip = require('adm-zip'); const path = require('path'); const app = express(); - app.post('/upload', (req, res) => { - const zip = new AdmZip(req.file.path); - const entries = zip.getEntries(); - for (const entry of entries) { - zip.extractEntryTo(entry, './uploads/', false, true); + function isPathSafe(baseDir, targetPath) { + const resolvedBase = path.resolve(baseDir); + const resolvedTarget = path.resolve(baseDir, targetPath); + return resolvedTarget.startsWith(resolvedBase + path.sep); + } + + app.post('/upload', (req, res) => { + const targetDir = './uploads/'; + const zip = new AdmZip(req.file.path); + const entries = zip.getEntries(); + for (const entry of entries) { + if (!isPathSafe(targetDir, entry.entryName)) { + return res.status(400).json({ error: 'Path traversal detected' }); + } + zip.extractEntryTo(entry, targetDir, false, true); } res.json({ success: true }); });
Use os.path.basename() or pathlib to restrict file access to intended directories
- from flask import request - - @app.route('/read') - def read_file(): - filename = request.args.get('file') - with open(f'/var/uploads/{filename}', 'r') as f: + import os + from flask import request, abort + + UPLOAD_DIR = '/var/uploads' + + @app.route('/read') + def read_file(): + filename = request.args.get('file', '') + safe_name = os.path.basename(filename) + filepath = os.path.join(UPLOAD_DIR, safe_name) + if not os.path.isfile(filepath): + abort(404) + with open(filepath, 'r') as f: return f.read()
Validate extracted file paths stay within the target directory before writing
import zipfile - - def extract_archive(zip_path, dest): - with zipfile.ZipFile(zip_path, 'r') as zf: - zf.extractall(dest) + import os + + def safe_extract(zip_path, dest): + with zipfile.ZipFile(zip_path, 'r') as zf: + for member in zf.namelist(): + target = os.path.normpath(os.path.join(dest, member)) + if not target.startswith(os.path.abspath(dest)): + raise ValueError(f"Path traversal: {member}") + zf.extract(member, dest)
Trouvez les vulnérabilités dans votre code
Utilisez Shoulder pour scanner votre code à la recherche de patterns Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'). 6 règles.
# Scan with Shoulder CLI npx @shoulderdev/cli trust --cwe=22 # Or scan entire project npx @shoulderdev/cli trust .
Règles de Détection (6)
Ce qu'il faut surveiller lors des revues de code
Ces patterns indiquent des vulnérabilités potentielles Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'). Recherchez-les lors des revues de code et des audits de sécurité.
Scannez votre base de code pour Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Shoulder CLI trouve les motifs vulnérables dans toute votre base de code.