Appearance
17.2 验证码
验证码的作用
验证码(CAPTCHA)是一种用于区分人类和计算机的技术,它可以:
- 防止恶意注册:防止自动化脚本批量注册账号
- 防止暴力破解:防止自动化工具尝试暴力破解密码
- 防止垃圾评论:防止机器人发布垃圾评论
- 防止刷票:防止自动化工具刷票
- 保护敏感操作:在重要操作(如转账、修改密码)时验证用户身份
PHP 生成验证码的方法
1. 使用 GD 库生成验证码
PHP 的 GD 库可以用来生成图形验证码:
php
<?php
// 生成验证码
session_start();
// 设置验证码长度
$length = 4;
// 生成随机字符串
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[rand(0, strlen($chars) - 1)];
}
// 保存验证码到会话
$_SESSION['captcha'] = $code;
// 创建图像
$width = 100;
$height = 40;
$image = imagecreate($width, $height);
// 设置颜色
$bg_color = imagecolorallocate($image, 255, 255, 255);
$text_color = imagecolorallocate($image, 0, 0, 0);
$line_color = imagecolorallocate($image, 100, 100, 100);
// 填充背景
imagefill($image, 0, 0, $bg_color);
// 绘制干扰线
for ($i = 0; $i < 5; $i++) {
imageline($image, 0, rand(0, $height), $width, rand(0, $height), $line_color);
}
// 绘制干扰点
for ($i = 0; $i < 50; $i++) {
imagesetpixel($image, rand(0, $width), rand(0, $height), $text_color);
}
// 绘制验证码文本
$font = 5; // 内置字体大小
$text_width = imagefontwidth($font) * $length;
$text_height = imagefontheight($font);
$x = ($width - $text_width) / 2;
$y = ($height - $text_height) / 2;
imagestring($image, $font, $x, $y, $code, $text_color);
// 输出图像
header('Content-type: image/png');
imagepng($image);
// 释放资源
imagedestroy($image);
?>2. 使用第三方库生成验证码
使用 Gregwar/Captcha
Gregwar/Captcha 是一个简单易用的验证码库:
安装
bash
composer require gregwar/captcha使用
php
<?php
session_start();
require 'vendor/autoload.php';
use Gregwar\Captcha\CaptchaBuilder;
// 创建验证码生成器
$builder = new CaptchaBuilder;
// 生成验证码
$builder->build();
// 保存验证码到会话
$_SESSION['captcha'] = $builder->getPhrase();
// 输出图像
header('Content-type: image/jpeg');
$builder->output();
?>使用 Intervention Image
Intervention Image 是一个功能强大的图像处理库,也可以用来生成验证码:
安装
bash
composer require intervention/image使用
php
<?php
session_start();
require 'vendor/autoload.php';
use Intervention\Image\ImageManagerStatic as Image;
// 生成验证码
$length = 4;
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[rand(0, strlen($chars) - 1)];
}
// 保存验证码到会话
$_SESSION['captcha'] = $code;
// 创建图像
$image = Image::make(100, 40, '#ffffff');
// 添加干扰线
for ($i = 0; $i < 5; $i++) {
$image->line(random_int(0, 100), random_int(0, 40), random_int(0, 100), random_int(0, 40), function ($draw) {
$draw->color('#666666');
$draw->width(1);
});
}
// 添加干扰点
for ($i = 0; $i < 50; $i++) {
$image->pixel('#666666', random_int(0, 100), random_int(0, 40));
}
// 添加验证码文本
$image->text($code, 50, 25, function ($font) {
$font->file('path/to/font.ttf');
$font->size(20);
$font->color('#000000');
$font->align('center');
$font->valign('middle');
});
// 输出图像
header('Content-type: image/png');
$image->save('php://output');
?>验证码验证
在表单提交时,需要验证用户输入的验证码是否正确:
php
<?php
session_start();
if (isset($_POST['submit'])) {
$user_captcha = $_POST['captcha'];
if (isset($_SESSION['captcha']) && strtolower($user_captcha) === strtolower($_SESSION['captcha'])) {
// 验证码正确,处理表单提交
echo '验证码正确,表单提交成功';
// 清除验证码,防止重复使用
unset($_SESSION['captcha']);
} else {
// 验证码错误
echo '验证码错误,请重新输入';
}
}
?>
<form method="POST">
<div class="form-group">
<label for="captcha">验证码</label>
<div class="input-group">
<input type="text" name="captcha" id="captcha" class="form-control" required>
<div class="input-group-append">
<img src="captcha.php" alt="验证码" onclick="this.src='captcha.php?' + Math.random()">
</div>
</div>
</div>
<button type="submit" name="submit" class="btn btn-primary">提交</button>
</form>验证码最佳实践
- 使用适当的验证码长度:通常 4-6 个字符即可,过长会影响用户体验
- 使用大小写不敏感验证:提高用户体验,避免因大小写错误导致验证失败
- 添加干扰元素:添加干扰线、干扰点等,增加机器识别的难度
- 使用适当的字体和大小:确保验证码清晰可辨,同时增加机器识别的难度
- 提供刷新功能:允许用户刷新验证码,避免因验证码难以识别而无法完成验证
- 设置验证码过期时间:防止验证码被长时间使用
- 使用 HTTPS:防止验证码被网络嗅探
- 考虑使用行为验证码:如滑动验证、点选验证等,提高用户体验
实战演练
场景:登录表单验证码
php
<?php
// captcha.php
session_start();
// 生成验证码
$length = 4;
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[rand(0, strlen($chars) - 1)];
}
// 保存验证码到会话
$_SESSION['captcha'] = $code;
// 创建图像
$width = 100;
$height = 40;
$image = imagecreate($width, $height);
// 设置颜色
$bg_color = imagecolorallocate($image, 255, 255, 255);
$text_color = imagecolorallocate($image, 0, 0, 0);
$line_color = imagecolorallocate($image, 100, 100, 100);
// 填充背景
imagefill($image, 0, 0, $bg_color);
// 绘制干扰线
for ($i = 0; $i < 5; $i++) {
imageline($image, 0, rand(0, $height), $width, rand(0, $height), $line_color);
}
// 绘制干扰点
for ($i = 0; $i < 50; $i++) {
imagesetpixel($image, rand(0, $width), rand(0, $height), $text_color);
}
// 绘制验证码文本
$font = 5;
$text_width = imagefontwidth($font) * $length;
$text_height = imagefontheight($font);
$x = ($width - $text_width) / 2;
$y = ($height - $text_height) / 2;
imagestring($image, $font, $x, $y, $code, $text_color);
// 输出图像
header('Content-type: image/png');
imagepng($image);
// 释放资源
imagedestroy($image);
?>
<?php
// login.php
session_start();
$error = '';
if (isset($_POST['login'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$captcha = $_POST['captcha'];
// 验证验证码
if (isset($_SESSION['captcha']) && strtolower($captcha) === strtolower($_SESSION['captcha'])) {
// 验证码正确,验证用户名和密码
if ($username === 'admin' && $password === 'admin123') {
// 登录成功
$_SESSION['user'] = $username;
header('Location: dashboard.php');
exit;
} else {
$error = '用户名或密码错误';
}
// 清除验证码
unset($_SESSION['captcha']);
} else {
$error = '验证码错误';
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="text-center">登录</h3>
</div>
<div class="card-body">
<?php if ($error): ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php endif; ?>
<form method="POST">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="captcha" class="form-label">验证码</label>
<div class="input-group">
<input type="text" class="form-control" id="captcha" name="captcha" required>
<div class="input-group-append">
<img src="captcha.php" alt="验证码" onclick="this.src='captcha.php?' + Math.random()" style="cursor: pointer;">
</div>
</div>
</div>
<button type="submit" name="login" class="btn btn-primary w-100">登录</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>总结
验证码是 Web 应用程序中的重要安全功能,它可以防止自动化攻击,保护系统安全。PHP 提供了多种生成验证码的方法,包括使用 GD 库和第三方库。通过合理使用验证码,可以提高应用程序的安全性和可靠性。
在开发过程中,应该注意以下几点:
- 选择合适的验证码生成方法
- 确保验证码的安全性和可读性
- 提供良好的用户体验
- 正确验证验证码
- 定期更新验证码策略,以应对新的攻击方法
通过合理使用验证码,可以有效防止自动化攻击,保护系统安全。
