Web安全是每个PHP程序员必须重视的问题。SQL注入和XSS攻击是最常见、最危险的两种Web安全漏洞,如果不做好防护,网站很容易被黑客攻击,数据泄露,甚至服务器被接管。今天就来分享PHP安全编程的实战经验,重点讲解如何防止SQL注入和XSS攻击。
1. SQL注入防护
SQL注入是指攻击者通过在输入中插入恶意SQL代码,从而操纵数据库查询的攻击方式。SQL注入可能导致数据泄露、数据篡改、数据删除,甚至服务器被接管。
不安全的写法:
// 危险!直接拼接用户输入到SQL中
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
$result = mysqli_query($conn, $sql);攻击者可以传入 id=1 OR 1=1,这样SQL就变成了 SELECT * FROM users WHERE id = 1 OR 1=1,会返回所有用户数据。更危险的是,攻击者还可以传入 id=1; DROP TABLE users;,直接删除表。
安全的写法:使用预处理语句(Prepared Statements)
预处理语句是防止SQL注入最有效的方法。预处理语句会先发送SQL模板给数据库,数据库编译后,再发送参数,参数会被当作纯数据处理,不会被当作SQL代码执行。
PDO预处理:
// PDO预处理
$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);MySQLi预处理:
// MySQLi预处理
$id = $_GET['id'];
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();LIKE查询的预处理:
// LIKE查询需要自己加通配符
$keyword = $_GET['keyword'];
$stmt = $pdo->prepare("SELECT * FROM posts WHERE title LIKE :keyword");
$stmt->execute(['keyword' => "%$keyword%"]);IN查询的预处理:
// IN查询需要动态生成占位符
$ids = $_GET['ids']; // 数组
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($placeholders)");
$stmt->execute($ids);其他SQL注入防护措施:
- 永远不要信任用户输入,所有用户输入都要验证和过滤
- 使用最小权限原则,数据库用户只给必要的权限
- 关闭错误显示,生产环境不要显示数据库错误信息
- 使用WAF(Web应用防火墙)作为额外防护
- 定期安全审计和渗透测试
2. XSS攻击防护
XSS(跨站脚本攻击)是指攻击者在网页中注入恶意脚本,当其他用户访问该网页时,恶意脚本会在用户浏览器中执行,从而窃取用户信息、劫持用户会话、篡改网页内容等。
XSS分为三种类型:
- 存储型XSS:恶意脚本存储在服务器数据库中,用户访问时执行
- 反射型XSS:恶意脚本通过URL参数传递,服务器反射回页面执行
- DOM型XSS:恶意脚本通过修改DOM执行,不经过服务器
不安全的写法:
// 危险!直接输出用户输入,不做转义
echo $_GET['name'];
echo $user['bio'];攻击者可以传入 name=<script>alert('XSS')</script>,这样恶意脚本就会在用户浏览器中执行。
安全的写法:使用htmlspecialchars转义输出
htmlspecialchars 会把特殊字符转换为HTML实体,从而防止XSS攻击。
// 安全!转义输出
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
echo htmlspecialchars($user['bio'], ENT_QUOTES, 'UTF-8');htmlspecialchars 会转换以下字符:
&→&"→"'→'(当ENT_QUOTES时)<→<>→>
封装转义函数:
// 封装转义函数,方便使用
function e($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
// 使用
echo e($_GET['name']);
echo e($user['bio']);不同上下文的XSS防护:
XSS防护要根据输出的上下文来选择合适的转义方式:
- HTML内容中:使用htmlspecialchars
<div><?php echo e($content); ?></div>- HTML属性中:使用htmlspecialchars,并且属性值用引号括起来
<input type="text" value="<?php echo e($value); ?>">- JavaScript中:使用json_encode
<script>
var data = <?php echo json_encode($data); ?>;
</script>- URL中:使用urlencode
<a href="search?q=<?php echo urlencode($keyword); ?>">搜索</a>- CSS中:尽量不要在CSS中使用用户输入,如果必须使用,要严格验证
其他XSS防护措施:
- Content Security Policy (CSP):设置CSP头,限制脚本来源,防止XSS
header("Content-Security-Policy: default-src 'self'; script-src 'self'");- HttpOnly Cookie:设置Cookie的HttpOnly属性,防止JavaScript读取Cookie
setcookie("session", $sessionId, time()+3600, "/", "", true, true);- 输入验证:对用户输入进行验证,只允许合法的字符和格式
- 使用成熟的模板引擎:如Twig、Blade等,模板引擎默认会自动转义输出
- 定期更新依赖:及时更新框架和库,修复已知的安全漏洞
3. 其他安全注意事项
1. CSRF防护:使用CSRF Token防止跨站请求伪造
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// 表单中添加Token
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// 验证Token
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF token validation failed");
}2. 文件上传安全:
- 验证文件类型和扩展名
- 限制文件大小
- 重命名上传文件
- 将上传文件存储在Web根目录之外
- 不要执行上传目录中的PHP文件
3. 密码安全:
- 使用password_hash哈希密码,不要用MD5、SHA1
- 使用password_verify验证密码
- 强制用户使用强密码
- 敏感操作需要重新验证密码
// 哈希密码
$hash = password_hash($password, PASSWORD_DEFAULT);
// 验证密码
if (password_verify($password, $hash)) {
// 密码正确
}4. 会话安全:
- 使用sessionregenerateid防止会话固定
- 设置会话超时
- 敏感操作使用HTTPS
- 销毁会话时彻底清除会话数据
总结
Web安全是每个PHP程序员必须重视的问题。SQL注入和XSS攻击是最常见、最危险的两种Web安全漏洞,必须做好防护。
防止SQL注入的核心是使用预处理语句,永远不要直接拼接用户输入到SQL中。防止XSS攻击的核心是转义输出,根据不同的上下文选择合适的转义方式。
安全没有一劳永逸,需要持续关注和维护。希望这篇文章能帮助大家写出更安全的PHP代码。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录