我的 PHP 网站遭到黑客入侵后如何进行锁定:我的实际服务器加固清单

网站遭到黑客入侵会改变你对 Web 开发的看法。在事情发生之前,安全性可能会让人觉得属于与设计、性能、SEO 和功能完全不同的类别。事情发生之后,你会意识到安全性几乎涉及应用程序的每个部分。如果处理不当,文件上传、数据库权限、登录会话、错误消息、服务器配置、备份,甚至看似无害的联系表单都可能成为攻击面。


开发人员在遭遇入侵后最容易犯的错误之一,是只试图修复表面上可见的损害。他们删除垃圾页面、更改管理员密码、更新几个插件,然后以为问题已经解决。这些操作可能会移除症状,但不一定能消除最初允许攻击者进入的漏洞。如果原始入口仍然存在,攻击者只需再次返回即可。

我采取不同的方法。网站一旦遭到入侵,我就会假设与该应用程序相关的所有内容都需要检查。这包括公开文件、数据库、服务器配置、计划任务、管理员账户、文件权限、API 密钥、上传目录和身份验证系统。目标不仅仅是清理网站,而是重新建立对整个环境的信任。

本文介绍我在加固自定义 PHP 和 MySQL 网站时采用的安全模型。其中一些技术同样适用于 WordPress、Laravel、Symfony 和其他平台,但本文中的示例主要侧重于运行在 Apache 上的传统 PHP 应用程序。

从正确的假设开始

发现入侵后,我首先会假设攻击者可能获得了比目前所能看到的更多的访问权限。找到一个恶意 PHP 文件,并不意味着只有一个恶意 PHP 文件。攻击者通常会创建备用访问点,这样即使删除了原始恶意软件,也不会将他们锁在系统之外。

例如,攻击者可能会将 Web Shell 上传到图像目录,将 PHP 注入合法的应用程序文件,创建隐藏的管理员账户,修改计划任务,或将恶意 JavaScript 插入数据库。他们还可能创建名称看似无害的文件,例如 cache.phpclass.api.phpsystem-helper.php,使恶意软件混入合法的应用程序代码中。

因此,仅按文件名对目录排序是不够的。我想知道哪些文件是最近修改的,哪些文件包含可执行 PHP,哪些目录意外包含脚本,以及是否有文件与已知的干净版本存在差异。

一个有用的 Linux 命令是:

find /var/www/html -type f -mtime -14 -print

此命令会列出过去十四天内修改过的文件。它不能证明这些文件具有恶意,但在调查近期入侵事件时,可以为你提供一个起点。

你还可以搜索通常应仅包含静态内容的目录中的 PHP 文件:

find /var/www/html/uploads -type f \( -name "*.php" -o -name "*.phtml" -o -name "*.phar" \)

如果你的上传目录按设计应包含照片、PDF 和用户文档,那么在该目录中发现可执行 PHP 文件就值得立即调查。

轮换所有可能已暴露的凭据

仅更改网站管理员密码是不够的。如果攻击者获得了服务器或应用程序文件的访问权限,他们可能已经能够读取数据库密码、API 凭据、SMTP 密码、Stripe 密钥、会话密钥以及其他身份验证信息。

因此,我会将凭据轮换视为清理过程的一部分。数据库密码应当更改。托管控制面板密码应当更改。应当检查 SSH 凭据。在适当的情况下,应当重新生成 API 密钥。如果应用程序存储了电子邮件凭据,也应当替换这些凭据。

环境配置需要特别注意。许多 PHP 应用程序会将敏感值存储在 .env 文件或配置文件中。典型的配置可能包含类似这样的内容:

DB_HOST=localhost
DB_DATABASE=production
DB_USERNAME=website_user
DB_PASSWORD=replace_this_password

STRIPE_SECRET_KEY=replace_this_key
OPENAI_API_KEY=replace_this_key

这些文件绝不应当通过公共 Web 服务器访问。如果有人可以访问 https://example.com/.env 并下载该文件,那么应用程序就存在严重的配置问题。

在 Apache 上,我会明确阻止访问敏感文件。

<FilesMatch "^(\.env|\.git|composer\.(json|lock)|package(-lock)?\.json)$">
    Require all denied
</FilesMatch>

这不能替代正确的目录结构,但可以为你提供另一层保护。

将敏感配置保存在公共 Web 根目录之外

你可以进行的最重要的架构改进之一,就是将应用程序机密与可公开访问的文件分开。理想情况下,公共 Web 目录中只应包含确实需要通过浏览器访问的文件。

与其将数据库凭据存储在类似以下位置:

/public_html/config.php

我更倾向于采用类似这样的方式:

/home/account/private/config.php
/home/account/public_html/index.php

应用程序可以在内部包含私有配置文件,但 Apache 无法直接提供该文件,因为它位于文档根目录之外。

这很重要,因为服务器配置错误确实会发生。PHP 处理程序可能会失效。Apache 配置可能会发生变化。备份文件可能会意外变得可下载。将机密信息保存在 Web 可访问目录之外,可以减少其泄露的途径。

禁用目录列表

目录索引是另一个不必要的信息泄露来源。如果某个目录不包含索引文件,一些 Apache 配置可能会显示该目录的内容。攻击者可以利用该列表发现文件名、备份存档、脚本以及内部目录结构。

我会为应用程序全局禁用目录索引。

Options -Indexes

通常,没有理由让普通 Web 应用程序向匿名访客公开其目录结构。