对于中小团队来说,安全建设往往面临一个尴尬的现实:没有专职安全工程师,预算有限,但攻击者并不会因为团队规模小就手下留情。好在绝大多数自动化攻击和常见漏洞利用都有成熟的防御手段,只要把基础工作做扎实,就能挡住 90% 以上的风险。本文从 XSS、SQL 注入、服务器加固三个最常被利用的切入点出发,给出一套可以直接落地的基础防御方案。
XSS 的常见方式与前端防御
XSS(跨站脚本攻击)的本质是攻击者让恶意脚本在受害者的浏览器中执行。常见方式主要有三类:
存储型 XSS 是最危险的一种。攻击者将恶意脚本提交到服务器并持久化存储(如评论、昵称、文章内容),所有访问该页面的用户都会中招。反射型 XSS 则通过构造恶意 URL 诱导用户点击,脚本随请求参数“反射”回页面并执行。DOM 型 XSS 不经过服务器,完全在前端 JavaScript 中触发,例如将 location.hash 直接写入 innerHTML。
前端防御的核心原则只有一条:永远不要信任任何用户输入,永远不要将不可信数据当作 HTML 或脚本执行。
具体做法包括:
- 输出编码:根据输出位置选择正确的编码方式。HTML 内容区使用 HTML 实体编码,属性区使用属性编码,JavaScript 上下文使用 JS 编码。不要用一个
escapeHtml函数打天下。 - 避免危险 API:优先使用
textContent而非innerHTML;如果必须渲染富文本,使用 DOMPurify 等成熟库进行白名单过滤,而不是自己写正则。 - 启用 CSP:通过
Content-Security-Policy响应头限制脚本来源,例如script-src 'self',可以有效阻断内联脚本和外部恶意脚本的执行。CSP 是纵深防御的重要一环,即使编码出现疏漏也能兜底。 - 设置 HttpOnly Cookie:让敏感 Cookie 无法被 JavaScript 读取,降低 XSS 得手后的危害。
需要强调的是,前端防御是必要的,但不能作为唯一防线。服务端同样要对输入进行校验和过滤,形成双层防护。
MySQL 防止 SQL 注入的几种写法
SQL 注入的根源是用户输入被拼接进 SQL 语句,改变了原有语义。防御手段按推荐程度从高到低排列:
1. 参数化查询(预处理语句)——首选方案
这是最可靠的方式。以 PHP PDO 为例:
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute(['email' => $email, 'status' => 'active']);
Java 的 PreparedStatement、Python 的 cursor.execute 带参数、Node.js 的 mysql2 占位符,原理都一样:SQL 结构与数据分离,数据库先编译语句模板,数据永远只作为值传入,不会被解析为 SQL 语法。
2. 使用 ORM 框架
Laravel 的 Eloquent、Django ORM、TypeORM 等默认使用参数化查询。但要注意,ORM 中如果使用原生查询方法(如 whereRaw)并拼接字符串,同样会产生注入。ORM 不是免死金牌,关键还是看是否拼接了用户输入。
3. 输入类型强制转换
对于明确是整数的参数(如 ID),强制转换是简单有效的补充:
$id = (int)$_GET['id'];
$sql = "SELECT * FROM articles WHERE id = $id";
但这种方式只适用于类型确定的场景,不能作为通用方案。
4. 白名单校验
对于排序字段、表名等无法参数化的位置,使用白名单映射:
$allowed = ['created_at', 'updated_at', 'title'];
$order = in_array($_GET['order'], $allowed) ? $_GET['order'] : 'created_at';
要避免的做法:使用 addslashes()、mysql_real_escape_string() 等转义函数。它们在特定字符集下可能被绕过,且容易遗漏。
Linux 服务器安全加固清单
服务器是最后一道防线,加固工作可以按以下清单逐项落实:
账户与认证
- 禁用 root 远程 SSH 登录(
PermitRootLogin no),使用普通用户 + sudo - 全面启用 SSH 密钥认证,关闭密码登录(
PasswordAuthentication no) - 修改 SSH 默认端口只能减少扫描噪音,不能替代密钥认证
- 删除或锁定不必要的系统账户,定期审计
/etc/passwd
网络与防火墙
- 使用 ufw 或 firewalld 配置默认拒绝策略,只开放必要端口(80、443、SSH)
- 数据库(MySQL 3306、Redis 6379)绝不暴露公网,仅监听 127.0.0.1 或内网地址
- 配置 fail2ban 自动封禁暴力破解 IP
系统与软件更新
- 开启自动安全更新(Ubuntu 的 unattended-upgrades),或建立每月手动更新机制
- 及时更新 Web 服务器、数据库、运行时环境(PHP/Node/Python)版本
- 移除不需要的软件包和服务,减少攻击面
权限与文件系统
- Web 目录文件权限设为 644、目录 755,属主为部署用户而非 root
- 敏感配置文件(如
.env、数据库配置)设为 600,确保 Web 服务器无法直接读取 - 关闭目录列表(Nginx
autoindex off),禁止访问.git、.env等敏感路径
日志与监控
- 开启并集中管理关键日志(auth.log、Nginx access/error log、MySQL slow log)
- 配置日志轮转,避免磁盘被写满导致服务不可用
- 对异常登录、异常请求频率设置告警
备份
- 数据库每日自动备份,保留至少 7 天
- 备份文件存储在独立于生产服务器的位置
- 定期演练恢复流程——没有验证过的备份等于没有备份
结语
中小团队的安全建设不需要一步到位,但需要形成习惯。把参数化查询作为编码规范、把输出编码写进代码审查清单、把服务器加固清单变成新机器上线的标准流程——这些基础动作做到位,就已经比大多数同类团队安全得多。安全不是一次性的项目,而是持续的过程。从今天开始,先完成清单上最容易的三项,再逐步推进。
未经允许不得转载:任鹏个人博客 » 中小团队如何建立基础 Web 安全防御体系


朋友圈点赞图在线生成源码