注意:本教程最初是 10 年前编写的。它现在已经更新了用于生成随机字符串的全新代码。讨论线程中的许多评论都引用了代码的原始版本。
人们每天都在编写代码来自动化各种流程。我们利用计算机比人类更快、更准确的事实,这让我们可以简化许多平凡的任务。不幸的是,这些相同的功能可用于对计算机进行编程以执行恶意操作,例如发送垃圾邮件或猜测密码。本教程的重点是打击垃圾邮件。
假设您有一个带有联系表格的网站,以便访问者可以轻松与您联系。他们所要做的就是填写表格并点击发送按钮,让您知道他们遇到的问题或请求。这是面向公众的网站的一项重要功能,但填写表单值的过程可能会被恶意用户自动化,从而以您的方式发送大量垃圾邮件。这种类型的垃圾邮件技术不限于联系表格。机器人也可用于在您的论坛中填满链接到有害网站的垃圾帖子或评论。
解决此问题的一种方法是设计一种测试,该测试可以区分试图传播垃圾邮件的机器人和合法地想要与您联系的人。这就是验证码的用武之地。它们通常由在彩色背景上书写的五个或六个字母的随机组合组成。这个想法是人类将能够阅读图像中的文本,但机器人不会。对照原件检查用户填写的 CAPTCHA 值可以帮助您区分机器人和人类。CAPTCHA 代表“完全自动化的公共图灵测试,以区分计算机和人类”。
在本教程中,我们将学习如何创建自己的验证码,然后将它们与我们在教程中创建的联系表单集成。
创建验证码
我们将使用 php GD 库来创建我们的验证码。 您可以在我早期的教程之一中了解有关 使用 GD 编写文本和绘制形状的更多信息。我们还必须编写一些代码来创建要写入创建的图像上的随机字符串。另一个名为“ 在 PHP 中生成随机字母数字字符串”的教程可以在这方面为我们提供帮助。
生成随机字符串
本节中的所有代码都将放在 captcha.php 文件中。让我们从编写创建随机字符串的函数开始。
<?php $permitted_chars = 'ABCDEFGHIJKLMNOPQRSTuvWXYZ'; function generate_string($input, $strength = 5) { $input_length = strlen($input); $random_string = ''; for($i = 0; $i < $strength; $i++) { $random_character = $input[mt_rand(0, $input_length - 1)]; $random_string .= $random_character; } return $random_string; } $string_length = 6; $captcha_string = generate_string($permitted_chars, $string_length); ?>
该 $permitted_chars 变量存储了我们要用于生成验证码字符串的所有字符。我们仅在英文字母表中使用 c api tal 字母,以避免由于可能看起来相似的字母或数字而可能引起的任何混淆。您可以使用任何您喜欢的字符集来增加或降低 CAPTCHA 的难度。
我们的函数默认创建一个五个字母的字符串,但您可以通过向函数传递不同的参数来更改该值 generate_string() 。
生成加密安全的随机字符串
您可以使用加密安全函数来生成随机字符串并使 CAPTCHA 更难猜测。
在这种特殊情况下,我们可以使用该 random_int() 函数代替 mt_rand(). 它接受相同的两个参数,但生成密码安全的随机数。这是我们用于生成随机字符串的修改代码。
<?php $permitted_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; function secure_generate_string($input, $strength = 5, $secure = true) { $input_length = strlen($input); $random_string = ''; for($i = 0; $i < $strength; $i++) { if($secure) { $random_character = $input[random_int(0, $input_length - 1)]; } else { $random_character = $input[mt_rand(0, $input_length - 1)]; } $random_string .= $random_character; } return $random_string; } $string_length = 6; $captcha_string = secure_generate_string($permitted_chars, $string_length); ?>
我们在这里所做的唯一改变是在我们的 generate_string() 函数中使用了第三个参数。调用的第三个参数 默认$secure 设置为 true 。稍后在代码中,我们检查是否 $secure 设置为 true ,然后用于 random_int() 生成字符串。
以这种方式修改我们的函数允许我们使我们之前对函数的所有调用的输出在加密上安全,而无需对旧代码进行任何更改。它还允许我们通过false 在以后的任何调用中显式设置第三个参数来生成不加密安全的随机字符串。
渲染验证码背景
一旦我们有了随机字符串,就该编写代码来创建验证码图像的背景了。图像大小为 200 x 50 像素,背景将使用五种不同的颜色。
<?php $image = imagecreatetruecolor(200, 50); imageantialias($image, true); $colors = []; $red = rand(125, 175); $green = rand(125, 175); $blue = rand(125, 175); for($i = 0; $i < 5; $i++) { $colors[] = imagecolorallocate($image, $red - 20*$i, $green - 20*$i, $blue - 20*$i); } imagefill($image, 0, 0, $colors[0]); for($i = 0; $i < 10; $i++) { imagesetthickness($image, rand(2, 10)); $rect_color = $colors[rand(1, 4)]; imagerectangle($image, rand(-10, 190), rand(-10, 10), rand(-10, 190), rand(40, 60), $rect_color); } ?>
$red我们从变量、 $green和 的随机值开始 $blue。这些值决定了图像背景的最终颜色。之后,我们运行一个 for 循环来创建原始颜色逐渐变暗的阴影。这些颜色存储在一个数组中。最浅的颜色是我们 $colors 数组的第一个元素,最深的颜色是最后一个元素。最浅的颜色用于填充图像的整个背景。
在下一步中,我们使用 for 循环在原始图像上的随机位置绘制矩形。矩形的厚度在 2 到 10 之间变化,而颜色是从我们 $colors 数组的最后四个值中随机选择的。
绘制所有这些矩形会为背景添加更多颜色,从而难以区分 CAPTCHA 字符串的前景和图像的背景。
您的 CAPTCHA 背景现在应该类似于下图。
呈现验证码字符串
最后一步,我们只需要在我们的背景上绘制验证码字符串。单个字母的颜色、y 坐标和旋转是随机确定的,以使 CAPTCHA 字符串更难阅读。
<?php $black = imagecolorallocate($image, 0, 0, 0); $white = imagecolorallocate($image, 255, 255, 255); $textcolors = [$black, $white]; $fonts = [dirname(__FILE__).'\fonts\Acme.ttf', dirname(__FILE__).'\fonts\Ubuntu.ttf', dirname(__FILE__).'\fonts\Merriweather.ttf', dirname(__FILE__).'\fonts\PlayfairDisplay.ttf']; $string_length = 6; $captcha_string = secure_generate_string($permitted_chars, $string_length); for($i = 0; $i < $string_length; $i++) { $letter_space = 170/$string_length; $initial = 15; imagettftext($image, 20, rand(-15, 15), $initial + $i*$letter_space, rand(20, 40), $textcolors[rand(0, 1)], $fonts[array_rand($fonts)], $captcha_string[$i]); } header('Content-type: image/png'); imagepng($image); imagedestroy($image); ?>
如您所见,我正在使用从 Google 下载的一些字体来获得字符的一些变化。图像两侧有 15 个像素的填充。剩余空间(170 像素)在所有 CAPTCHA 字母之间平均分配。
在背景上方渲染文本字符串后,您的结果应该类似于下图。字符会有所不同,但它们应该稍微旋转并混合黑色和白色。
将验证码添加到我们的联系表中
现在我们已经创建了验证码,是时候将它添加到我们的联系表单中了。我们将使用 我之前关于如何创建 PHP 联系表单并在发送消息 按钮 上方添加验证码的 教程中的联系表单。
我们将使用会话来存储验证码文本,然后验证网站访问者输入的文本。这是我们的captcha.php 文件的完整代码:
<?php session_start(); $permitted_chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; function generate_string($input, $strength = 10) { $input_length = strlen($input); $random_string = ''; for($i = 0; $i < $strength; $i++) { $random_character = $input[mt_rand(0, $input_length - 1)]; $random_string .= $random_character; } return $random_string; } $image = imagecreatetruecolor(200, 50); imageantialias($image, true); $colors = []; $red = rand(125, 175); $green = rand(125, 175); $blue = rand(125, 175); for($i = 0; $i < 5; $i++) { $colors[] = imagecolorallocate($image, $red - 20*$i, $green - 20*$i, $blue - 20*$i); } imagefill($image, 0, 0, $colors[0]); for($i = 0; $i < 10; $i++) { imagesetthickness($image, rand(2, 10)); $line_color = $colors[rand(1, 4)]; imagerectangle($image, rand(-10, 190), rand(-10, 10), rand(-10, 190), rand(40, 60), $line_color); } $black = imagecolorallocate($image, 0, 0, 0); $white = imagecolorallocate($image, 255, 255, 255); $textcolors = [$black, $white]; $fonts = [dirname(__FILE__).'\fonts\Acme.ttf', dirname(__FILE__).'\fonts\Ubuntu.ttf', dirname(__FILE__).'\fonts\Merriweather.ttf', dirname(__FILE__).'\fonts\PlayfairDisplay.ttf']; $string_length = 6; $captcha_string = generate_string($permitted_chars, $string_length); $_SESSION['captcha_text'] = $captcha_string; for($i = 0; $i < $string_length; $i++) { $letter_space = 170/$string_length; $initial = 15; imagettftext($image, 24, rand(-15, 15), $initial + $i*$letter_space, rand(25, 45), $textcolors[rand(0, 1)], $fonts[array_rand($fonts)], $captcha_string[$i]); } header('Content-type: image/png'); imagepng($image); imagedestroy($image); ?>
您要使用的字体将进入 字体 目录。现在,您只需在我们之前关于使用 html 和 PHP 创建联系表单的 教程中 的“发送消息”按钮上方 添加以下html代码。
<div class="elem-group"> <label for="captcha">Please Enter the Captcha Text</label> <img src="captcha.php" alt="CAPTCHA" class="captcha-image"><i class="fas fa-redo refresh-captcha"></i> <br> <input type="text" id="captcha" name="captcha_challenge" pattern="[A-Z]{6}"> </div>
有时,即使是人类也很难阅读 CAPTCHA 文本。在这些情况下,我们希望他们能够以用户友好的方式请求新的验证码。上面的重做图标可以帮助我们做到这一点。您所要做的就是在与联系表单的 HTML 相同的页面上添加下面的javascript 。
var refreshButton = document.queryselector(".refresh-captcha"); refreshButton.onclick = function() { document.querySelector(".captcha-image").src = 'captcha.php?' + Date.now(); }
在表单中集成 CAPTCHA 并添加刷新按钮后,您应该会得到一个如下图所示的表单。
将我们创建的 CAPTCHA 与联系表单集成的最后一步涉及在填写表单时检查用户输入的 CAPTCHA 值,并将其与会话中存储的值匹配。更新上一教程中的 contact.php 文件以包含以下代码。
<?php session_start(); if($_post) { $visitor_name = ""; $visitor_email = ""; $email_title = ""; $concerned_department = ""; $visitor_message = ""; if(isset($_POST['captcha_challenge']) && $_POST['captcha_challenge'] == $_SESSION['captcha_text']) { if(isset($_POST['visitor_name'])) { $visitor_name = filter_var($_POST['visitor_name'], FILTER_SANITIZE_STRING); } if(isset($_POST['visitor_email'])) { $visitor_email = str_replace(array("\r", "\n", "%0a", "%0d"), '', $_POST['visitor_email']); $visitor_email = filter_var($visitor_email, FILTER_VALIDATE_EMAIL); } if(isset($_POST['email_title'])) { $email_title = filter_var($_POST['email_title'], FILTER_SANITIZE_STRING); } if(isset($_POST['concerned_department'])) { $concerned_department = filter_var($_POST['concerned_department'], FILTER_SANITIZE_STRING); } if(isset($_POST['visitor_message'])) { $visitor_message = htmlspecialchars($_POST['visitor_message']); } if($concerned_department == "billing") { $recipient = "billing@domain.com"; } else if($concerned_department == "marketing") { $recipient = "marketing@domain.com"; } else if($concerned_department == "technical support") { $recipient = "tech.support@domain.com"; } else { $recipient = "contact@domain.com"; } $headers = 'MIME-Version: 1.0' . "\r\n" .'Content-type: text/html; charset=utf-8' . "\r\n" .'From: ' . $visitor_email . "\r\n"; if(mail($recipient, $email_title, $visitor_message, $headers)) { echo '<p>Thank you for contacting us. You will get a reply within 24 hours.</p>'; } else { echo '<p>We are sorry but the email did not go through.</p>'; } } else { echo '<p>You entered an incorrect Captcha.</p>'; } } else { echo '<p>Something went wrong</p>'; } ?>
我们更新了这个文件,首先检查会话中存储的验证码值是否与用户输入的值相同。如果它们不同,我们只会告诉访问者他们输入了错误的验证码。您可以根据项目需要以不同方式处理这种情况。
最后的想法
在本教程中,我们从头开始在 PHP 中创建了自己的 CAPTCHA,并将其与 我们在早期教程之一中构建的PHP 联系表单集成。我们还通过添加刷新按钮使 CAPTCHA 更加用户友好,以便用户获得具有新背景的新字符串,以防前一个字符串不可读。
您还可以使用本教程中的逻辑来创建依赖于求解基本数学方程(如加法和减法)的验证码。
- 生成随机字符串
- 生成加密安全的随机字符串
- 渲染验证码背景
- 呈现验证码字符串