开放的目录索引:当你的文件服务器列出所有内容时
启用目录索引后,访问 /uploads/ 这样的路径会返回该目录中所有文件的可浏览列表,而不是 403 或 404。攻击者会首先查看这里,因为备份归档、日志文件和用户上传的内容经常以全局可读的方式留在由 Web 提供服务的目录中。
这是什么
目录列表(也称目录索引)是一项 Web 服务器功能:当请求一个目录且其中没有索引文件时,服务器会生成一个 HTML 文件浏览器。它在某些 nginx 配置(autoindex on)和 Apache(Options +Indexes)中默认启用,对静态托管来说很方便,但在任何不应公开文件的地方都会成为隐患。
AppSafe 会检查一组常被错误配置的路径——/uploads/、/files/、/backup/、/backups/、/logs/ 和 /tmp/——并标记任何返回可识别目录列表响应的路径。即使只有一个开放目录,也可能暴露从未打算被发现的数据。
为什么重要
暴露的目录中经常包含用户上传的文件、压缩的数据库转储、含有凭据或会话令牌的应用日志,以及部署遗留下来的源代码归档。找到它们不需要认证,也不需要任何漏洞利用——只需输入 URL。
自动化扫描器会持续不断地对每台可访问的主机探测与 AppSafe 检查相同的知名路径。一个包含 .sql.gz 转储的开放 /backup/ 目录,实际上就是一份无需密码即可获取的完整数据库导出。
如何修复
在任何提供用户可控目录的 server 或 location 块中,移除 autoindex 指令或显式将其关闭。
location /uploads/ {
autoindex off;
# Serve individual files but never list directory contents
}最安全的修复方法是将上传文件、备份和日志存放在 Web 服务器完全不提供服务的目录中。你的应用通过经过认证的路由读取并流式传输文件——原始文件系统路径永远无法被访问。
切勿将用户上传的文件或备份放在 public/ 目录中。通过能够强制认证并清理所请求路径的 API 路由来提供它们。
// app/api/uploads/[filename]/route.ts
import { getSession } from "@/lib/auth-session";
import { readFile } from "fs/promises";
import path from "path";
export async function GET(
_req: Request,
{ params }: { params: { filename: string } }
) {
const session = await getSession();
if (!session) return new Response(null, { status: 401 });
// path.basename prevents directory traversal
const safe = path.basename(params.filename);
const filePath = path.join(process.cwd(), "private-uploads", safe);
const data = await readFile(filePath);
return new Response(data);
}如果你确实需要一个可浏览的文件界面(媒体库、内部日志查看器),请用认证保护该路径,而不是依赖隐蔽性。
location /logs/ {
autoindex on;
auth_basic "Internal only";
auth_basic_user_file /etc/nginx/.htpasswd;
}常见问题
目录列表总是一个问题,还是只在某些路径上才是?
只要文件中包含任何敏感内容,它就是问题。开启索引的公共 CDN 提供静态图片通常无害;而 /logs/、/backup/ 和 /uploads/ 几乎从来都不是,因为它们会积累未经公开访问审查的文件。
我能不能只在目录中放一个空的 index.html 来阻止列表?
这只能隐藏该目录的列表,但每个子目录仍然是开放的。而且在创建新目录时也很容易遗漏。在服务器层面关闭 autoindex 是一种可靠的配置级修复,能覆盖其下的所有内容。
AppSafe 对开放的目录索引报告的严重程度是多少?
中等。目录列表本身并不是入侵——它是通向入侵的路径。一个包含数据库转储的可浏览 /backup/ 目录,或者一个以明文存放认证令牌的 /logs/ 目录,很快就会使风险远超中等。