目录遍历漏洞详解
目录遍历(Directory Traversal)也称为路径遍历(Path Traversal),是一种常见的Web安全漏洞。我将从原理、利用和防御三个方面详细讲解。
一、漏洞原理
1.1 基本概念
目录遍历漏洞允许攻击者访问Web应用程序根目录之外的文件和目录。当应用程序使用用户提供的输入来构造文件路径,但未进行充分验证时,就会产生这个漏洞。
1.2 核心原理
- 特殊字符序列:
../(Unix/Linux)或..\(Windows)用于返回上级目录
- 不当的输入处理:应用程序直接将用户输入拼接到文件路径中
- 缺乏访问控制:没有限制可访问的目录范围
1.3 典型场景
易受攻击的代码示例:
1 2 3 4 5 6
| <?php
$file = $_GET['filename']; $content = file_get_contents("/var/www/html/files/" . $file); echo $content; ?>
|
1 2 3 4 5 6 7
| from flask import request, send_file
@app.route('/download') def download(): filename = request.args.get('filename') return send_file(f'/app/files/{filename}')
|
二、漏洞利用
2.1 基本利用方法
简单遍历:
1
| http://example.com/download?file=../../../etc/passwd
|
Windows系统:
1
| http://example.com/download?file=..\..\..\windows\system32\drivers\etc\hosts
|
2.2 绕过技术
URL编码:
1
| http://example.com/file?name=..%2F..%2F..%2Fetc%2Fpasswd
|
双重编码:
1
| http://example.com/file?name=..%252F..%252F..%252Fetc%252Fpasswd
|
16位Unicode编码:
1
| ..%c0%af..%c0%af..%c0%afetc/passwd
|
添加空字节(旧版本语言):
1
| ../../../etc/passwd%00.jpg
|
绝对路径:
1
| http://example.com/file?name=/etc/passwd
|
过滤器绕过:
....//....//....//etc/passwd (过滤单个../)
..;/..;/..;/etc/passwd
.././.././.././etc/passwd
2.3 常见攻击目标
Linux系统:
/etc/passwd - 用户账户信息
/etc/shadow - 密码哈希(需要权限)
/etc/hosts - 主机名映射
/proc/self/environ - 环境变量
/var/log/apache2/access.log - 日志文件
~/.ssh/id_rsa - SSH私钥
Windows系统:
C:\Windows\System32\drivers\etc\hosts
C:\Windows\win.ini
C:\boot.ini
C:\inetpub\wwwroot\web.config
应用程序配置文件:
.env - 环境配置
config.php - 数据库凭证
web.xml - Java配置
settings.py - Python配置
三、防御措施
3.1 输入验证
白名单验证:
1 2 3 4 5 6 7 8 9 10 11
| import os from pathlib import Path
ALLOWED_FILES = {'report1.pdf', 'report2.pdf', 'image.jpg'}
def safe_download(filename): if filename not in ALLOWED_FILES: raise ValueError("文件不允许访问") filepath = os.path.join('/app/files', filename) return filepath
|
文件名清理:
1 2 3 4 5 6 7 8 9
| import os import re
def sanitize_filename(filename): clean_name = re.sub(r'[^a-zA-Z0-9_\-\.]', '', filename) clean_name = clean_name.replace('/', '').replace('\\', '') return clean_name
|
3.2 路径规范化
使用标准库函数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import os from pathlib import Path
def safe_path_join(base_dir, user_input): base_path = Path(base_dir).resolve() requested_path = (base_path / user_input).resolve() if not str(requested_path).startswith(str(base_path)): raise ValueError("路径遍历尝试被拒绝") return requested_path
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.nio.file.*;
public String validatePath(String baseDir, String userInput) { Path basePath = Paths.get(baseDir).toAbsolutePath().normalize(); Path requestedPath = basePath.resolve(userInput).normalize(); if (!requestedPath.startsWith(basePath)) { throw new SecurityException("路径遍历攻击"); } return requestedPath.toString(); }
|
3.3 访问控制
文件系统权限:
- 使用最小权限原则运行Web应用
- 限制Web用户只能访问必要的目录
- 使用chroot或容器隔离
应用层限制:
1 2 3 4 5 6 7 8
| def check_file_access(filepath, allowed_dir): real_path = os.path.realpath(filepath) allowed_path = os.path.realpath(allowed_dir) common_prefix = os.path.commonprefix([real_path, allowed_path]) if common_prefix != allowed_path: raise PermissionError("访问被拒绝")
|
3.4 使用安全API
避免直接文件操作:
1 2 3 4 5 6 7 8 9 10 11 12 13
| file_content = open(user_input).read()
FILE_MAP = { 'report1': '/safe/path/report1.pdf', 'report2': '/safe/path/report2.pdf' }
def get_file(file_id): if file_id not in FILE_MAP: raise ValueError("无效的文件ID") return open(FILE_MAP[file_id], 'rb').read()
|
3.5 其他防御措施
Web应用防火墙(WAF):
日志和监控:
1 2 3 4 5 6
| import logging
def access_file(filename): if '..' in filename or '/' in filename: logging.warning(f"可疑的文件访问尝试: {filename}") raise ValueError("非法路径")
|
安全配置:
- 禁用目录列表
- 配置适当的错误页面(避免信息泄露)
- 使用安全的文件上传目录(与应用代码分离)
3.6 框架特定的防御
Express.js:
1 2 3 4 5 6 7 8 9 10 11 12 13
| const path = require('path'); const express = require('express');
app.get('/download', (req, res) => { const safePath = path.normalize(req.query.file).replace(/^(\.\.(\/|\\|$))+/, ''); const fullPath = path.join(__dirname, 'files', safePath); if (!fullPath.startsWith(path.join(__dirname, 'files'))) { return res.status(403).send('禁止访问'); } res.sendFile(fullPath); });
|
四、检测方法
4.1 手动测试
- 在文件参数中输入
../../../etc/passwd
- 尝试各种编码变体
- 测试绝对路径访问
4.2 自动化工具
- Burp Suite - 使用Intruder模块
- OWASP ZAP - 自动扫描
- DotDotPwn - 专门的目录遍历工具
五、总结
目录遍历漏洞的防御核心是:
- 永远不要信任用户输入
- 使用白名单验证
- 规范化并验证所有路径
- 实施最小权限原则
- 使用安全的API和框架功能
通过综合使用这些防御措施,可以有效防止目录遍历攻击,保护应用程序和服务器安全。