做Web开发,安全是一个绕不开的话题。但很多开发者(包括以前的我)对安全不够重视,总觉得"我的网站又不是什么大网站,没人会攻击我"。直到有一次,我的一个小项目被黑客注入了SQL,数据库被篡改,首页被挂了黑页,我才意识到安全的重要性。
Web安全漏洞有很多种,其中最常见、也最容易被利用的两种是SQL注入和XSS(跨站脚本攻击)。根据OWASP(开放式Web应用程序安全项目)的统计,这两种漏洞常年位居Web安全漏洞排行榜前列。今天就来分享我在项目中防范这两种攻击的实践经验。
SQL注入的原理和防范
SQL注入的原理很简单:开发者在拼接SQL语句时,直接把用户输入拼接到SQL里,没有做任何过滤或转义。攻击者可以在输入中注入恶意的SQL代码,改变SQL语句的原意,从而执行非授权操作。
举个例子,一个登录功能的SQL:
$sql = "SELECT * FROM users WHERE username = '{$_POST['username']}' AND password = '{$_POST['password']}'";如果攻击者在用户名输入框里输入 admin' -- ,SQL就变成了:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''-- 是SQL的注释符,后面的内容被注释掉了,密码验证被绕过,攻击者直接以admin身份登录了。
防范SQL注入的方法是使用预处理语句(Prepared Statement),也就是参数化查询。PDO和MySQLi都支持预处理语句,把SQL模板和参数分开,参数会被自动转义,从根本上杜绝SQL注入。
用PDO预处理语句重写上面的登录:
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$_POST['username'], $_POST['password']]);
$user = $stmt->fetch();这样,用户输入会被当作参数处理,不会被解析为SQL代码,无论输入什么都不会改变SQL语句的结构。
还有一个好习惯:永远不要信任用户输入。所有来自客户端的数据($GET、$POST、$COOKIE、$SERVER等)都要视为不可信的,使用前必须验证和过滤。
XSS攻击的原理和防范
XSS(跨站脚本攻击)的原理是:攻击者在网页中注入恶意的JavaScript代码,当其他用户访问这个页面时,恶意代码会在用户的浏览器中执行,从而窃取用户信息(如Cookie、登录态)、篡改页面内容、诱导用户操作等。
XSS分为三种类型:反射型、存储型、DOM型。
反射型XSS:恶意代码通过URL参数传递,服务器把参数直接输出到页面上。比如搜索功能,用户输入的关键词直接显示在页面上:
echo "搜索结果:" . $_GET['keyword'];攻击者可以构造这样的URL:http://example.com/search?keyword=<script>alert(document.cookie)</script>,用户点击后,恶意脚本就在页面上执行了。
存储型XSS:恶意代码被存储到数据库中,其他用户访问包含该数据的页面时,恶意代码被执行。比如评论功能,用户提交的评论内容直接存储到数据库,显示时直接输出:
echo $comment['content'];攻击者在评论里输入 <script>stealCookie()</script>,所有看到这条评论的用户都会中招。存储型XSS比反射型危害更大,因为它影响所有访问该页面的用户。
防范XSS的核心原则是:输出时转义。所有输出到HTML页面的用户数据,都必须经过HTML转义,把特殊字符(<、>、"、'、&)转换成HTML实体。
PHP里用htmlspecialchars函数:
echo htmlspecialchars($comment['content'], ENT_QUOTES, 'UTF-8');ENT_QUOTES表示同时转义单引号和双引号,UTF-8指定字符编码。这样,<script> 会被转义成 <script>,浏览器会把它当作文本显示,而不是当作脚本执行。
还有一个更安全的做法是使用CSP(内容安全策略),通过HTTP头限制页面可以加载和执行的脚本来源,即使有XSS漏洞,恶意脚本也很难执行。Nginx里配置CSP:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'";其他安全建议
除了SQL注入和XSS,还有一些常见的安全问题需要注意:
第一,密码存储。永远不要明文存储用户密码,要用bcrypt或passwordhash函数哈希存储。PHP 5.5以后内置了passwordhash函数,使用bcrypt算法,自动加盐,非常安全:
$hash = password_hash($password, PASSWORD_DEFAULT);
// 验证
if (password_verify($inputPassword, $hash)) {
// 密码正确
}第二,CSRF(跨站请求伪造)。攻击者诱导用户在已登录的网站上执行非预期操作(如修改密码、转账)。防范方法是使用CSRF Token,每个表单包含一个随机Token,提交时验证Token是否匹配。
第三,文件上传。如果网站允许用户上传文件,一定要严格验证文件类型和大小,不要把上传目录放在Web可访问的路径下,防止攻击者上传恶意脚本执行。
第四,错误信息。生产环境不要显示详细的错误信息(如数据库错误、文件路径),这些信息会帮助攻击者了解系统结构。设置display_errors = Off,把错误记录到日志文件里。
总结
Web安全不是一蹴而就的事情,需要在开发的每个环节都保持警惕。核心原则就两条:永远不要信任用户输入,输出时永远转义。只要做到这两点,SQL注入和XSS这两种最常见的漏洞就能基本杜绝。
安全是一场持续的战争,新的漏洞不断出现,攻击者的手段不断升级。作为开发者,我们要持续学习安全知识,关注安全公告,定期审计代码,把安全意识融入到日常开发中。希望这篇文章能帮到大家。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录