首页 / 帮助文档 / 建站必备网站漏洞防护基础实操指南

建站必备网站漏洞防护基础实操指南

网站漏洞防护这件事,很多站长一开始就理解偏了。不是装个安全插件、开个HTTPS就完事,真正的漏洞防护是建站阶段就要嵌入到目录权限、文件上传、数据库查询、后台入口这些具体环节里的操作。大部分中小网站被黑,不是因为攻击者技术多高明,而是因为基础防护动作根本没做。下面直接讲实操,按建站流程把每个环节的防护点拆开说清楚。

目录权限最小化:先把服务器上的门锁好

建站第一步上传源码到服务器后,第一件事不是急着安装,而是检查目录权限。Linux服务器上用chmod和chown命令,Windows服务器上通过文件属性设置。核心原则只有一个:可写目录越少越好,权限越低越好。

常见的PHP站点,上传目录、缓存目录、日志目录需要写权限,其他目录全部只读。比如ThinkPHP框架,runtime目录需要可写,但很多人图省事直接把整个项目目录设为777,这等于把服务器上所有房间的门都拆了。正确的做法是:

# 项目根目录设为755
chmod 755 /www/wwwroot/site

# 仅上传目录和运行时目录设为775或755,所有者设为web运行用户
chmod -R 775 /www/wwwroot/site/upload
chmod -R 775 /www/wwwroot/site/runtime

# 配置文件单独设为644,禁止web用户修改
chmod 644 /www/wwwroot/site/config.php

Windows服务器同理,IIS的应用程序池账户只对特定目录授予“修改”权限,其他目录只给“读取”权限。很多Windows服务器被挂马,就是因为站点目录直接给了Everyone完全控制权限。

还有一个经常被忽略的点:禁止web目录下出现备份文件。很多站长习惯把源码压缩包、数据库备份文件放在网站根目录,文件名还叫wwwroot.zip或者db.sql。攻击者用扫描器一跑就能直接下载整个源码和数据库。备份文件要么放在web目录之外,要么用随机文件名加访问密码。

SQL注入防护:从代码层杜绝拼接查询

SQL注入是二十年前就存在的漏洞类型,到现在依然大量存在。根本原因不是程序员不知道,而是开发时图方便用了字符串拼接。防护方法很明确:全部使用参数化查询,任何用户输入都不能直接进入SQL语句。

PHP用PDO预处理语句:

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND status = ?");
$stmt->execute([$username, $status]);
$result = $stmt->fetchAll();

如果是老项目还在用mysql扩展或者mysqli拼接,必须逐个改掉。对于无法立即修改的旧代码,至少在入口处加一层全局过滤,把单引号、双引号、反斜杠、NULL字符等危险字符转义。但要注意,全局过滤只是临时措施,不能替代参数化查询。

另外,数据库账号权限也要分离。网站连接数据库的账号,只授予SELECT、INSERT、UPDATE、DELETE权限,不要给DROP、ALTER、CREATE权限。即使发生注入,攻击者也无法通过注入直接删库或建表。很多网站被注入后整个数据库被清空,就是因为用了root账号连接数据库。

XSS跨站脚本防护:输出转义比输入过滤更可靠

XSS攻击的本质是攻击者把恶意脚本注入到网页中,当其他用户访问时脚本执行。防护的关键在于输出转义,而不是输入过滤。因为输入过滤很难覆盖所有场景,而且容易误伤正常内容。

具体做法是:所有输出到HTML页面的动态内容,必须根据输出位置做对应转义。输出到HTML标签之间时转义<、>、&、"、'这五个字符;输出到JavaScript代码中时使用json_encode;输出到HTML属性中时额外转义引号。

PHP中可以使用htmlspecialchars函数:

echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');

如果使用模板引擎,比如Blade、Twig、Smarty,它们默认会对变量进行HTML转义,不要随意关闭这个功能。对于确实需要输出富文本的场景,使用白名单过滤,只允许特定的HTML标签和属性,比如p、br、strong、em、a等,其他全部剔除。

还有一个重要的防护头:设置Content-Security-Policy。这个HTTP响应头可以限制浏览器只加载指定来源的脚本,即使有XSS漏洞,攻击者的外部脚本也无法执行。基础配置可以这样写:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;

这个头在Nginx或Apache配置中全局添加,对所有页面生效。

文件上传漏洞:从扩展名到内容的多层校验

文件上传是攻击者获取服务器权限最直接的途径。一个允许上传PHP文件的表单,等于把服务器钥匙交给了攻击者。防护要从多个层面同时做,不能只靠前端JS校验。

第一层是扩展名白名单。只允许特定扩展名,比如图片只允许jpg、jpeg、png、gif、webp,文档只允许pdf、docx、xlsx。不要用黑名单,因为黑名单总有遗漏。

第二层是MIME类型校验。但要注意,MIME类型来自客户端请求头,可以伪造,所以只能作为辅助判断。

第三层是文件内容检测。用PHP的getimagesize函数检测图片文件头,或者用finfo扩展获取真实的MIME类型。对于图片,还可以用imagecreatefromjpeg等函数尝试重新生成图片,能有效去除图片中嵌入的恶意代码。

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);

$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($mime, $allowedMimes)) {
    die('文件类型不允许');
}

第四层是存储隔离。上传的文件不要直接存放在web可访问的目录中,而是存放在web根目录之外,通过PHP脚本读取后输出。如果必须放在web目录内,要在该目录下放置一个.htaccess文件(Apache)或修改Nginx配置,禁止该目录执行PHP脚本。

# Apache .htaccess 禁止执行PHP
php_flag engine off

第五层是文件名处理。上传的文件不要保留原始文件名,使用随机字符串重命名,扩展名使用白名单中的扩展名。这样可以防止攻击者构造特殊文件名绕过过滤。

后台入口防护:隐藏、限制、强密码三管齐下

网站后台是攻击者的首要目标。很多CMS默认后台地址是/admin或者/admin.php,攻击者用扫描器批量扫描就能找到。防护措施从三个方面入手。

第一,修改默认后台路径。把后台入口改成不容易猜到的地址,比如/manage_8f3k2x或者使用二级目录加随机字符串。但要注意,单纯隐藏路径只是增加了攻击难度,不能作为唯一的防护手段。

第二,限制访问来源。如果只有固定IP需要访问后台,可以在服务器配置中限制后台目录只允许特定IP访问。Nginx配置示例:

location /admin {
    allow 192.168.1.100;
    deny all;
}

Apache配置示例:

<Directory /www/wwwroot/site/admin>
    Order deny,allow
    Deny from all
    Allow from 192.168.1.100
</Directory>

第三,后台登录增加额外验证。除了用户名密码,可以加一个简单的HTTP Basic Auth,或者使用双因素认证。HTTP Basic Auth在服务器层面实现,即使后台程序有漏洞,攻击者也无法直接访问后台页面。

密码策略也要强制。后台密码至少12位以上,包含大小写字母、数字、特殊字符。同时限制登录失败次数,连续失败5次锁定账号30分钟。很多后台被暴力破解就是因为没有登录限制。

文件包含与远程代码执行防护

文件包含漏洞常见于PHP程序中使用了include或require函数,并且参数来自用户输入。攻击者可以通过构造特殊参数,让服务器包含远程恶意文件或本地敏感文件。防护方法很明确:禁止用户输入直接进入文件包含函数。

// 错误做法
include($_GET['page'] . '.php');

// 正确做法:使用白名单映射
$pages = [
    'home' => 'home.php',
    'about' => 'about.php',
    'contact' => 'contact.php',
];
$page = $_GET['page'] ?? 'home';
if (isset($pages[$page])) {
    include($pages[$page]);
} else {
    include('home.php');
}

同时,在php.ini中设置allow_url_include = Off,禁止远程文件包含。这个配置在默认情况下是关闭的,但有些老服务器可能被开启过,需要检查确认。

远程代码执行漏洞通常出现在使用了eval、assert、preg_replace的/e修饰符等危险函数的地方。这些函数在开发中应该完全禁用。可以在php.ini的disable_functions中禁用这些函数:

disable_functions = eval,assert,system,exec,shell_exec,passthru,proc_open,popen

如果程序确实需要执行系统命令,使用escapeshellcmd和escapeshellarg对命令和参数进行转义,并且严格限制可执行的命令范围。

会话安全与Cookie防护

会话劫持是攻击者获取已登录用户权限的常见手段。防护措施包括:使用HTTPS加密传输,设置Cookie的Secure和HttpOnly属性,使用安全的会话ID生成机制。

PHP中设置Cookie属性:

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => '',
    'secure' => true,   // 仅HTTPS传输
    'httponly' => true, // 禁止JavaScript读取
    'samesite' => 'Strict' // 防止CSRF
]);
session_start();

同时设置session.use_strict_mode = On,防止会话固定攻击。登录成功后使用session_regenerate_id(true)重新生成会话ID,避免会话固定。

定期备份与日志监控

防护做得再好,也不能保证百分百安全。定期备份是最后的防线。数据库每天备份一次,网站文件每周备份一次,备份文件存储在服务器之外的位置,比如对象存储或另一台服务器。备份文件要加密,防止泄露。

日志监控同样重要。开启服务器访问日志和错误日志,定期检查异常访问记录。重点关注:大量404请求、异常POST请求、访问敏感文件的请求、来自同一IP的频繁登录尝试。可以使用简单的脚本定时分析日志,发现异常立即告警。

网站漏洞防护是一个持续的过程,不是一次性的工作。建站时把这些基础防护做好,能挡住绝大多数自动化攻击和脚本小子的扫描。剩下的就是保持程序和插件更新,关注安全公告,及时修补新出现的漏洞。安全防护的投入,永远比网站被黑后的恢复成本低得多。