1. 基本验证原则
客户端验证(HTML/JavaScript):提升用户体验,但不可靠(可被绕过)。
服务器端验证(PHP):必须始终执行,确保数据安全和有效性。
2. 常见验证类型及实现
(1) 必填字段验证
确保关键字段不为空。
$username = trim($_POST['username'] ?? '');
if (empty($username)) {
$errors[] = "用户名不能为空!";
}
(2) 数据类型验证
字符串长度限制:
if (strlen($username) < 4 || strlen($username) > 20) {
$errors[] = "用户名长度需在4-20字符之间!";
}邮箱格式验证:
$email = $_POST['email'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "邮箱格式无效!";
}URL 验证:
$url = $_POST['website'] ?? '';
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = "URL格式无效!";
}数字范围验证:
$age = $_POST['age'] ?? 0;
if (!filter_var($age, FILTER_VALIDATE_INT, ["options" => ["min_range" => 1, "max_range" => 120]])) {
$errors[] = "年龄需在1-120之间!";
}
(3) 正则表达式验证
自定义复杂格式(如密码强度、电话号码):
$password = $_POST['password'] ?? '';
if (!preg_match("/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/", $password)) {
$errors[] = "密码需至少8位,包含字母和数字!";
}
(4) 防止 XSS 攻击
转义输出内容:
$clean_username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
echo "用户名:" . $clean_username;
(5) 防止 SQL 注入
使用预处理语句(以 PDO 为例):
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->bindParam(':username', $username);
$stmt->execute();
(6) 文件上传验证
验证文件类型、大小和内容:
$allowedTypes = ['image/jpeg', 'image/png'];
$file = $_FILES['file'] ?? [];
// 验证 MIME 类型
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file['tmp_name']);
if (!in_array($mimeType, $allowedTypes)) {
$errors[] = "仅支持JPEG和PNG图片!";
}
// 验证文件大小(2MB)
if ($file['size'] > 2 * 1024 * 1024) {
$errors[] = "文件大小超过2MB限制!";
}
3. 错误处理与用户反馈
收集所有错误并返回给用户:
session_start();
if (!empty($errors)) {
$_SESSION['errors'] = $errors;
header("Location: form.php");
exit;
}在 HTML 中显示错误:
<?php if (!empty($_SESSION['errors'])): ?>
<div class="errors">
<?php foreach ($_SESSION['errors'] as $error): ?>
<p><?= htmlspecialchars($error) ?></p>
<?php endforeach; ?>
</div>
<?php unset($_SESSION['errors']); ?>
<?php endif; ?>
4. 高级安全措施
(1) CSRF 防护
生成并验证 Token:
session_start();
// 生成 Token
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// 在表单中嵌入 Token
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
// 验证 Token
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("非法请求!");
}
(2) 防止重复提交
使用一次性 Token 或 Session 标记:
if ($_SESSION['form_submitted'] === true) {
die("请勿重复提交表单!");
}
$_SESSION['form_submitted'] = true;
(3) 验证码(CAPTCHA)
集成 Google reCAPTCHA:
$secretKey = "your-secret-key";
$response = $_POST['g-recaptcha-response'];
$url = "https://www.google.com/recaptcha/api/siteverify?secret=$secretKey&response=$response";
$result = json_decode(file_get_contents($url));
if (!$result->success) {
$errors[] = "验证码验证失败!";
}
5. 完整示例:登录表单验证
<?php
session_start();
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 验证 CSRF Token
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("非法请求!");
}
// 获取并清理数据
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
// 必填字段验证
if (empty($username)) {
$errors[] = "用户名不能为空!";
}
if (empty($password)) {
$errors[] = "密码不能为空!";
}
// 用户名格式验证(字母+数字)
if (!preg_match("/^[a-zA-Z0-9]{4,20}$/", $username)) {
$errors[] = "用户名格式无效!";
}
// 如果没有错误,继续处理
if (empty($errors)) {
// 数据库验证(示例)
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user'] = $user;
header("Location: dashboard.php");
exit;
} else {
$errors[] = "用户名或密码错误!";
}
}
// 保存错误并重定向
$_SESSION['errors'] = $errors;
header("Location: login.php");
exit;
}
?>
6. 最佳实践总结
始终进行服务器端验证:不依赖客户端验证。
过滤输入,转义输出:
使用
filter_var()
、htmlspecialchars()
。使用预处理语句:防止 SQL 注入。
限制文件上传:验证类型、大小和内容。
防护 CSRF:使用 Token 机制。
清晰的错误提示:避免泄露敏感信息。
启用 HTTPS:保护数据传输安全。