网站目录遍历漏洞是最常见也最容易被忽视的安全隐患之一,攻击者通过修改URL路径中的参数,比如把"../../../etc/passwd"这样的路径拼接到请求中,就能绕过正常访问权限读取服务器上的敏感文件。要解决这个问题,核心思路就是三步:严格设置文件和目录的访问权限、禁用目录索引浏览功能、以及在Web服务器层面做好路径过滤和访问控制。这三步缺一不可,任何一步没做到位,都可能给攻击者留下可乘之机。下面我会把每一步的具体操作、配置方法和注意事项全部讲清楚。

一、什么是目录遍历漏洞,为什么必须重视

目录遍历(Directory Traversal),也叫路径遍历(Path Traversal),本质上就是攻击者利用Web应用程序对用户输入的文件路径没有做充分校验,通过构造包含"../"(上级目录符号)的恶意请求,跳出网站根目录的限制,访问到服务器上不应该被公开的文件。比如你的网站根目录是/var/www/html/,正常用户只能访问这个目录下的内容,但如果存在遍历漏洞,攻击者就可能访问到/var/www/html/../../../etc/passwd,最终读取到系统密码文件。

这种漏洞在老旧的CMS系统、自研的文件下载功能、日志查看功能中特别常见。一旦被利用,轻则泄露网站源代码、配置文件、数据库连接信息,重则直接拿到服务器的控制权。所以这不是一个"有没有"的问题,而是"什么时候被发现"的问题。你必须主动去排查和防护。

二、文件和目录权限设置:从操作系统层面锁死访问边界

权限设置是最基础也是最有效的防线。在Linux服务器上,每个文件和目录都有所有者(owner)、所属组(group)和其他用户(others)三个维度的读(r)、写(w)、执行(x)权限。Web服务通常以www-data、nginx、apache等用户身份运行,你要做的就是确保Web进程只能访问它需要的目录,其他目录一律没有权限。

具体操作方法如下:首先,找到你的网站根目录,假设是/var/www/html/,执行以下命令设置目录权限:

chown -R www-data:www-data /var/www/html/
chmod -R 750 /var/www/html/

这里750的意思是:所有者有读写执行权限(7),所属组有读和执行权限(5),其他用户没有任何权限(0)。这样即使Web进程是以www-data用户运行的,它也只能在所属组权限范围内操作,不会影响到其他敏感目录。

对于特别敏感的文件,比如配置文件.env、数据库配置文件、密钥文件等,要单独收紧权限:

chmod 640 /var/www/html/.env
chmod 640 /var/www/html/config/database.php
chmod 600 /var/www/html/ssl/private.key

640意味着只有所有者能读写,所属组只能读,其他用户无权限。600则是只有所有者能读写,连所属组都不能访问。密钥文件尤其要用600,因为一旦泄露后果极其严重。

另外,要特别注意上传目录的权限。如果你的网站有文件上传功能,上传目录不要放在Web根目录下,或者至少要确保上传目录没有执行权限,防止攻击者上传恶意脚本后直接执行:

chmod 755 /var/www/html/uploads/
# 注意:不要给uploads目录加执行权限,更不要给777

755是目录的常见权限,表示可以进入和列出内容,但不意味着里面的文件可以被执行。如果你不小心给了777,那就等于把大门敞开,任何人都能读写执行,这是绝对要避免的。

三、禁用目录索引:不让攻击者看到目录结构

很多Web服务器默认开启了目录索引功能(Directory Indexing),当你访问一个没有默认首页文件(比如index.html、index.php)的目录时,服务器会自动生成一个文件列表页面,把目录下所有文件和文件夹的名称都展示出来。这等于直接告诉攻击者你的目录结构,他可以根据这个列表有针对性地去尝试访问敏感文件。

在Apache服务器上,禁用目录索引需要修改配置文件httpd.conf或者.htaccess文件,找到Options指令,把Indexes去掉:

# 修改前(可能包含Indexes)
Options Indexes FollowSymLinks

# 修改后
Options -Indexes +FollowSymLinks

如果你用的是.htaccess文件,直接在网站根目录下创建或编辑.htaccess,写入以下内容:

Options -Indexes

在Nginx服务器上,禁用目录索引更加简单,在server块或者location块中添加:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    location / {
        autoindex off;
    }
}

autoindex off;这一行就搞定了。如果你只想对特定目录禁用,可以把location指向那个具体路径。禁用之后,当用户访问没有默认文件的目录时,服务器会返回403 Forbidden错误,而不是文件列表。这一步看似简单,但很多中小型网站根本没做,等于白白暴露了内部结构。

四、Web服务器层面的路径过滤和访问控制

光靠操作系统权限和禁用目录索引还不够,你还需要在Web服务器层面做更细粒度的控制。核心思路是:对用户输入的路径参数进行严格过滤,拒绝包含"../"、绝对路径、编码后的遍历字符等恶意内容的请求。

在Nginx中,可以通过正则表达式匹配来拦截恶意请求。在server块中添加:

if ($request_uri ~* "\.\.") {
    return 403;
}

# 或者更精确地拦截包含../的请求
location ~* \.\. {
    return 403;
}

在Apache中,可以使用mod_rewrite模块来实现类似功能,在.htaccess中添加:

RewriteEngine On
RewriteCond %{REQUEST_URI} \.\. [NC]
RewriteRule .* - [F,L]

这段规则的意思是:如果请求的URI中包含".."(不区分大小写),直接返回403禁止访问。注意,这里用的是[F]标志,表示Forbidden,不是重定向。

除了拦截"../",还要注意防范URL编码绕过。攻击者可能会用%2e%2e%2f(即"../"的URL编码)或者双重编码来绕过简单的过滤。所以更稳妥的做法是在应用层代码中做解码后再校验,而不是只在Web服务器层面做字符串匹配。在PHP中可以这样处理:

$path = realpath($user_input);
if (strpos($path, '/var/www/html/') !== 0) {
    die('Access denied');
}

realpath()函数会把路径解析为绝对路径并消除其中的".."和符号链接,然后你再判断这个解析后的路径是否以你允许的根目录开头。如果不是,直接拒绝。这种方法比单纯的字符串匹配要可靠得多。

五、应用层代码的安全实践:从源头堵住漏洞

Web服务器层面的防护是第一道防线,但真正的漏洞往往出现在应用代码里。如果你的网站有文件下载功能,比如用户点击链接下载文件,后端代码可能是这样的:

// 危险写法!
$file = $_GET['file'];
readfile('/downloads/' . $file);

攻击者只要传入file=../../../etc/passwd,就能读取系统文件。正确的做法是:

// 安全写法
$file = basename($_GET['file']); // 只取文件名,去掉路径部分
$safe_path = '/var/www/html/downloads/' . $file;

if (file_exists($safe_path) && is_file($safe_path)) {
    // 额外检查:确保路径没有被篡改
    $real_path = realpath($safe_path);
    if (strpos($real_path, '/var/www/html/downloads/') === 0) {
        readfile($real_path);
    } else {
        die('Access denied');
    }
} else {
    die('File not found');
}

basename()函数会把路径中的目录部分全部去掉,只保留文件名,这样"../../../etc/passwd"就变成了"passwd",攻击者无法通过路径穿越。再加上realpath()的二次校验,双重保险。另外,永远不要相信用户输入,所有从外部获取的参数都要经过白名单验证。

六、日志监控与定期安全扫描

防护做好了不代表万事大吉,你还需要持续监控。开启Web服务器的访问日志,定期分析日志中是否有大量包含"../"、".."、"%2e%2e"等特征的请求。如果发现异常,说明有人在尝试攻击,你需要及时排查是否有漏洞被利用。

建议使用自动化安全扫描工具定期对网站进行漏洞扫描,比如检测目录遍历、文件包含、SQL注入等常见漏洞。同时,保持Web服务器、PHP、数据库等所有组件的版本更新,很多已知漏洞都有官方补丁,及时打补丁是最低成本的防护手段。

另外,配置好错误页面,不要让服务器在出错时暴露详细的错误信息和文件路径。在生产环境中,应该关闭详细错误显示,只给用户一个通用的错误提示:

# PHP配置 php.ini
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log

这样即使攻击者触发了错误,也看不到具体的文件路径和代码信息,增加了他利用漏洞的难度。

七、总结:多层防御才是正道

网站漏洞防护不是单一措施能解决的,目录遍历防护需要从操作系统权限、Web服务器配置、应用代码逻辑、日志监控四个层面同时入手。权限设置是基础,禁用目录索引是屏障,路径过滤是关卡,代码安全是核心。任何一个环节的疏忽都可能让前面的努力白费。建议网站运维人员把这些配置作为上线前的必检项,定期复查,形成常态化的安全运维机制。安全不是一次性的工作,而是持续的过程。