ساخت کپچا (CAPTCHA) و فرم تماس با PHP — از صفر تا صد

۹۶۸ بازدید
آخرین به‌روزرسانی: ۰۸ مهر ۱۴۰۲
زمان مطالعه: ۷ دقیقه
ساخت کپچا (CAPTCHA) و فرم تماس با PHP — از صفر تا صد

برنامه نویسان همواره کدهایی برای خودکارسازی فرایندهای مختلف می‌نویسند. در واقع ما از این واقعیت که رایانه‌ها بسیار سریع‌تر و دقیق‌تر از انسان‌ها عمل می‌کنند، استفاده کرده و بدین ترتیب انجام بسیاری از کارهای خسته‌کننده را ساده‌تر می‌سازیم. متأسفانه از همین امکانات می‌توان برای برنامه‌نویسی رایانه‌ها جهت انجام کارهای مخرب مانند ارسال اسپم یا حدس زدن رمزهای عبور نیز استفاده کرد. در این نوشته به معرفی یکی از قدیمی‌ترین روش‌های مبارزه با اسپم یعنی CAPTCHA  و روش پیاده‌سازی آن در PHP خواهیم پرداخت.

فرض کنید وب‌سایتی با یک فرم تماس دارید که موجب می‌شود بازدیدکنندگان بتوانند با شما در تماس باشند. تنها کاری که بازدیدکنندگان باید انجام دهند، این است که فرم را پر کنند و دکمه ارسال را بزنند تا شما در مورد مشکل یا درخواستی که دارند مطلع شوید. این یک ویژگی مهم برای وب‌سایت‌هایی است که در دسترس عموم هستند؛ اما کاربران خرابکار می‌توانند فرایند پر کردن فرم را به صورت خودکار درآورند و بدین ترتیب اسپم‌های بسیار زیادی به شما ارسال کنند. این نوع از تکنیک اسپم کردن تنها محدود به فرم‌های تماس نیست. ربات‌ها می‌توانند برای پر کردن نوشته‌های اسپم یا درج نظرات اسپم که حاوی لینک‌هایی به وب‌سایت‌های نامناسب هستند نیز مورد استفاده قرار گیرند.

تشخیص انسان از ربات

یک روش برای حل این مشکل آن است که آزمونی طراحی کنیم تا تشخیص دهیم کسی که می‌خواهد فرم تماس را ارسال کند یک ربات ارسال اسپم یا یک کاربر معمولی وب‌سایت است که می‌خواهد با مالک وب‌سایت تماس بگیرد. این همان جایی است که مفهوم CAPTCHA به کار می‌آید. CAPTCHA–ها عموماً شامل تصاویری حاوی ترکیب تصادفی از پنج یا شش حرف هستند که روی پس‌زمینه رنگی نوشته شده‌اند. ایده CAPTCHA این است که انسان می‌تواند متن درون تصویر را بخواند؛ اما ربات قادر به این کار نیست. بررسی مقدار CAPTCHA که از سوی کاربر انسانی وارد شده است با مقدار اصلی تعریف شده، به ما کمک می‌کند که ربات‌ها را از انسان‌ها تمییز دهیم.

CAPTCHA اختصاری برای عبارت زیر است:

completely automated public Turing test to tell computers and humans apart

در این راهنما با روش ایجاد CAPTCHA-ی سفارشی و سپس ادغام آن در فرم تماس آشنا می‌شویم.

ایجاد CAPTCHA

ما از کتابخانه GD در PHP برای ایجاد CAPTCHA سفارشی خودمان استفاده می‌کنیم. اگر می‌خواهید در مورد این کتابخانه اطلاعات بیشتری کسب کنید، می‌توانید به مقاله «تغییر اندازه و دستکاری تصاویر در PHP — راهنمای کاربردی» مراجعه کنید. همچنین باید کدی بنویسیم که رشته‌های متنی تصادفی برای ما تولید بکند.

تولید یک رشته تصادفی

همه کدی که در این بخش می‌نویسیم در فایلی به نام captcha.php قرار خواهد گرفت. بنابراین کار خود را با نوشتن تابعی که رشته تصادفی تولید می‌کند، آغاز خواهیم کرد:

1<?php
2 
3$permitted_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
4  
5function generate_string($input, $strength = 5) {
6    $input_length = strlen($input);
7    $random_string = '';
8    for($i = 0; $i < $strength; $i++) {
9        $random_character = $input[mt_rand(0, $input_length - 1)];
10        $random_string .= $random_character;
11    }
12  
13    return $random_string;
14}
15 
16$string_length = 6;
17$captcha_string = generate_string($permitted_chars, $string_length);
18 
19 
20?>

متغیر permitted_chars$ همه کاراکترهایی را که می‌خواهیم برای تولید رشته استفاده کنیم را نگهداری می‌کند. ما تنها از حروف بزرگ الفبای زبان انگلیسی استفاده می‌کنیم تا از بروز سردرگمی به دلیل شباهت برخی حروف یا اعداد به هم اجتناب کنیم. شما می‌تواند از هر ترکیبی از مجموعه کاراکترهایی که می‌خواهید استفاده کنید تا میزان دشواری CAPTCHA را افزایش یا کاهش دهید.

تابع ما به طور پیش‌فرض یک رشته پنج حرفی ایجاد می‌کند؛ اما شما می‌توانید مقدار آن را با ارسال پارامتر متفاوت به تابع ()generate_string تغییر دهید.

رندر کردن پس‌زمینه CAPTCHA

زمانی که رشته تصادفی ما آماده شد، نوبت آن می‌رسد که کدی برای ایجاد پس‌زمینه تصویر CAPTCHA بنویسیم. این تصویر در ابعاد 50 × 200 خواهد بود و از پنج رنگ متفاوت برای پس‌زمینه استفاده می‌کنیم.

1<?php
2 
3$image = imagecreatetruecolor(200, 50);
4 
5imageantialias($image, true);
6 
7$colors = [];
8 
9$red = rand(125, 175);
10$green = rand(125, 175);
11$blue = rand(125, 175);
12 
13for($i = 0; $i < 5; $i++) {
14  $colors[] = imagecolorallocate($image, $red - 20*$i, $green - 20*$i, $blue - 20*$i);
15}
16 
17imagefill($image, 0, 0, $colors[0]);
18 
19for($i = 0; $i < 10; $i++) {
20  imagesetthickness($image, rand(2, 10));
21  $rect_color = $colors[rand(1, 4)];
22  imagerectangle($image, rand(-10, 190), rand(-10, 10), rand(-10, 190), rand(40, 60), $rect_color);
23}
24 
25?>

کار خود را با تعیین مقادیر تصادفی برای red ،$green$ و blue$ آغاز می‌کنیم. این مقادیر، رنگ نهایی تصویر پس‌زمینه را مشخص خواهند کرد. پس از آن یک حلقه for اجرا می‌کنیم که به تدریج سایه‌های تیره‌تری از رنگ اصلی تولید می‌کند. این رنگ‌ها در یک آرایه ذخیره می‌شوند. روشن‌ترین رنگ نخستین عنصر آرایه colors$ خواهد بود و تیره‌ترین رنگ نیز آخرین عنصر این آرایه است. روشن‌ترین رنگ برای پر کردن کل پس‌زمینه تصویر استفاده می‌شود.

در مرحله بعدی، از یک حلقه for برای رسم مستطیل‌هایی در مکان‌های تصادفی روی تصویر اصلی استفاده می‌کنیم. ضخامت مستطیل‌ها بین 2 تا 10 تغییر می‌یابد و رنگ آن‌ها نیز از میان چهار عنصر آخر آرایه colors$ انتخاب خواهد شد.

ترسیم همه مستطیل‌ها روی تصویر موجب می‌شود که رنگ‌های بیشتری وارد آن شود و تمییز دادن پیش‌زمینه رشته CAPTCHA از تصویر پس‌زمینه دشوارتر شود. پس‌زمینه CAPTCHA اینک باید چیزی مانند تصویر زیر باشد:

CAPTCHA

رندر کردن رشته CAPTCHA

در گام نهایی باید یک رشته CAPTCHA را روی تصویر پس‌زمینه که در گام قبلی ایجاد شده، رندر کنیم. رنگ، مختصات y و چرخش حروف منفرد به صورت تصادفی تعیین می‌شود تا خواندن رشته CAPTCHA دشوارتر شود.

1<?php
2 
3$black = imagecolorallocate($image, 0, 0, 0);
4$white = imagecolorallocate($image, 255, 255, 255);
5$textcolors = [$black, $white];
6 
7$fonts = [dirname(__FILE__).'\fonts\Acme.ttf', dirname(__FILE__).'\fonts\Ubuntu.ttf', dirname(__FILE__).'\fonts\Merriweather.ttf', dirname(__FILE__).'\fonts\PlayfairDisplay.ttf'];
8 
9$string_length = 6;
10$captcha_string = generate_string($permitted_chars, $string_length);
11 
12for($i = 0; $i < $string_length; $i++) {
13  $letter_space = 170/$string_length;
14  $initial = 15;
15   
16  imagettftext($image, 20, rand(-15, 15), $initial + $i*$letter_space, rand(20, 40), $textcolors[rand(0, 1)], $fonts[array_rand($fonts)], $captcha_string[$i]);
17}
18 
19header('Content-type: image/png');
20imagepng($image);
21imagedestroy($image);
22 
23?>

همان طور که می‌بینید ما از برخی فونت‌هایی که از گوگل دانلود کرده‌ایم در این کد استفاده می‌کنیم تا کاراکترهایمان شبیه به هم نباشند. یک فاصله‌گذاری 15 پیکسلی در هر دو سمت تصویر وجود دارد. بدین ترتیب فضای 170 پیکسلی باقی مانده به صورت مساوی بین حروف CAPTCHA تقسیم می‌شود.

پس از رندر کردن رشته متنی روی پس‌زمینه، نتیجه کار چیزی مانند تصویر زیر خواهد بود. حروف متفاوت خواهند بود؛ اما در هر صورت اندکی چرخش دارند و ترکیبی از رنگ‌های سیاه و سفید را نیز نمایش می‌دهند.

captcha_text

افزودن CAPTCHA به فرم تماس

اینک که CAPTCHA آماده شده است، زمان آن فرا رسیده که CAPTCHA را به فرم تماس خود اضافه کنیم. ما از فرم تماسی که قبلاً در مقاله «ساخت یک فرم تماس با PHP — از صفر تا صد» ساخته‌ایم، در این نوشته استفاده خواهیم کرد و یک CAPTCHA را درست پیش از دکمه «ارسال پیام» (Send Message) اضافه می‌کنیم.

همچنین از «نشست» (Session) برای ذخیره‌سازی متن CAPTCHA و سپس اعتبارسنجی متن وارد شده از سوی بازدیدکنندگان وب‌سایت استفاده می‌کنیم. کد کامل فایل captcha.php در این مرحله به صورت زیر درآمده است:

1<?php
2 
3session_start();
4 
5$permitted_chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
6  
7function generate_string($input, $strength = 10) {
8    $input_length = strlen($input);
9    $random_string = '';
10    for($i = 0; $i < $strength; $i++) {
11        $random_character = $input[mt_rand(0, $input_length - 1)];
12        $random_string .= $random_character;
13    }
14  
15    return $random_string;
16}
17 
18$image = imagecreatetruecolor(200, 50);
19 
20imageantialias($image, true);
21 
22$colors = [];
23 
24$red = rand(125, 175);
25$green = rand(125, 175);
26$blue = rand(125, 175);
27 
28for($i = 0; $i < 5; $i++) {
29  $colors[] = imagecolorallocate($image, $red - 20*$i, $green - 20*$i, $blue - 20*$i);
30}
31 
32imagefill($image, 0, 0, $colors[0]);
33 
34for($i = 0; $i < 10; $i++) {
35  imagesetthickness($image, rand(2, 10));
36  $line_color = $colors[rand(1, 4)];
37  imagerectangle($image, rand(-10, 190), rand(-10, 10), rand(-10, 190), rand(40, 60), $line_color);
38}
39 
40$black = imagecolorallocate($image, 0, 0, 0);
41$white = imagecolorallocate($image, 255, 255, 255);
42$textcolors = [$black, $white];
43 
44$fonts = [dirname(__FILE__).'\fonts\Acme.ttf', dirname(__FILE__).'\fonts\Ubuntu.ttf', dirname(__FILE__).'\fonts\Merriweather.ttf', dirname(__FILE__).'\fonts\PlayfairDisplay.ttf'];
45 
46$string_length = 6;
47$captcha_string = generate_string($permitted_chars, $string_length);
48 
49$_SESSION['captcha_text'] = $captcha_string;
50 
51for($i = 0; $i < $string_length; $i++) {
52  $letter_space = 170/$string_length;
53  $initial = 15;
54   
55  imagettftext($image, 24, rand(-15, 15), $initial + $i*$letter_space, rand(25, 45), $textcolors[rand(0, 1)], $fonts[array_rand($fonts)], $captcha_string[$i]);
56}
57 
58header('Content-type: image/png');
59imagepng($image);
60imagedestroy($image);
61?>

استفاده از فونت‌های مختلف

فونت‌هایی که می‌خواهید استفاده کنید را در دایرکتوری fonts قرار دهید. اینک کافی است کد HTML زیر را درست بالای دکمه Send Message در فرم تماس که قبلاً ساختیم اضافه کنیم.

1<div class="elem-group">
2    <label for="captcha">Please Enter the Captcha Text</label>
3    <img src="captcha.php" alt="CAPTCHA" class="captcha-image"><i class="fas fa-redo refresh-captcha"></i>
4    <br>
5    <input type="text" id="captcha" name="captcha_challenge" pattern="[A-Z]{6}">
6</div>

گاهی اوقات خواندن متن CAPTCHA حتی برای انسان‌ها نیز دشوار است. در این موقعیت‌ها باید به افراد اجازه بدهیم که به روشی آسان درخواست یک تصویر جدید CAPTCHA بکنند. آیکون redo به ما کمک می‌کند که دقیقاً همین کار را انجام دهیم. کافی است کد جاوا اسکریپت زیر را در انتهای کد HTML در فرم تماس اضافه کنیم:

1var refreshButton = document.querySelector(".refresh-captcha");
2refreshButton.onclick = function() {
3  document.querySelector(".captcha-image").src = 'captcha.php?' + Date.now();
4}

پس از ادغام CAPTCHA در فرم و افزودن یک دکمه رفرش، اینک فرم ما شبیه به تصویر زیر شده است:

CAPTCHA

ادغام CAPTCHA در فرم تماس

گام نهایی در فرایند ادغام CAPTCHA در فرم تماس، این است که مقدار CAPTCHA وارد شده از سوی کاربر را در هنگام پر کردن فرم با مقدار ذخیره شده در session مقایسه کنیم. بدین منظور فایل contact.php را که در یکی از آموزش‌های قبلی بلاگ فرادرس ساختیم، به صورت زیر تغییر می‌دهیم:

1<?php
2 
3session_start();
4 
5if($_POST) {
6    $visitor_name = "";
7    $visitor_email = "";
8    $email_title = "";
9    $concerned_department = "";
10    $visitor_message = "";
11 
12    if(isset($_POST['captcha_challenge']) && $_POST['captcha_challenge'] == $_SESSION['captcha_text']) {
13     
14        if(isset($_POST['visitor_name'])) {
15            $visitor_name = filter_var($_POST['visitor_name'], FILTER_SANITIZE_STRING);
16        }
17         
18        if(isset($_POST['visitor_email'])) {
19            $visitor_email = str_replace(array("\r", "\n", "%0a", "%0d"), '', $_POST['visitor_email']);
20            $visitor_email = filter_var($visitor_email, FILTER_VALIDATE_EMAIL);
21             
22        }
23         
24        if(isset($_POST['email_title'])) {
25            $email_title = filter_var($_POST['email_title'], FILTER_SANITIZE_STRING);
26        }
27         
28        if(isset($_POST['concerned_department'])) {
29            $concerned_department = filter_var($_POST['concerned_department'], FILTER_SANITIZE_STRING);
30        }
31         
32        if(isset($_POST['visitor_message'])) {
33            $visitor_message = htmlspecialchars($_POST['visitor_message']);
34        }
35         
36        if($concerned_department == "billing") {
37            $recipient = "billing@domain.com";
38        }
39        else if($concerned_department == "marketing") {
40            $recipient = "marketing@domain.com";
41        }
42        else if($concerned_department == "technical support") {
43            $recipient = "tech.support@domain.com";
44        }
45        else {
46            $recipient = "contact@domain.com";
47        }
48         
49        $headers  = 'MIME-Version: 1.0' . "\r\n"
50        .'Content-type: text/html; charset=utf-8' . "\r\n"
51        .'From: ' . $visitor_email . "\r\n";
52         
53        if(mail($recipient, $email_title, $visitor_message, $headers)) {
54            echo '<p>Thank you for contacting us. You will get a reply within 24 hours.</p>';
55        } else {
56            echo '<p>We are sorry but the email did not go through.</p>';
57        }
58    } else {
59        echo '<p>You entered an incorrect Captcha.</p>';
60    }
61     
62} else {
63    echo '<p>Something went wrong</p>';
64}
65 
66?>

در واقع ما فایل فوق را طوری به‌روزرسانی کردیم که بررسی کند آیا مقدار CAPTCHA ذخیره شده در Session همان مقدار وارد شده از سوی کاربر است یا نه. اگر این دو مقدار متفاوت باشند، به بازدیدکنندگان اطلاع می‌دهیم که CAPTCHA نادرستی را وارد کرده‌اند. شما می‌توانید بر اساس نیازهای پروژه خودتان موقعیت‌های مختلفی را مدیریت کنید.

سخن پایانی

در این راهنما یک CAPTCHA سفارشی در فرم PHP ساختیم و آن را در فرم تماس که در یکی از مطالب قبلی بلاگ فرادرس آموزش داده بودیم ادغام کردیم. همچنین با افزودن دکمه رفرش میزان سهولت استفاده از CAPTCHA را افزایش دادیم. بدین ترتیب کاربران در مواردی که خوانایی CAPTCHA دشوار باشد، می‌توانند با زدن این دکمه یک تصویر جدید دریافت کنند. شما می‌توانید از منطق مطرح شده در این راهنما استفاده کنید و CAPTCHA-یی ایجاد کنید که بر روی حل کردن معادله‌های ریاضیاتی ساده مانند جمع و تفریق استوار باشد.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱۴ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
tutsplus
۲ دیدگاه برای «ساخت کپچا (CAPTCHA) و فرم تماس با PHP — از صفر تا صد»

خیلی هم عالی مرسی

آقا سلام
یه کپچای خیلی ساده در حد 2 + 2 که جوابش همیشه میشه 4، چطوری میشه ساخت؟ یا همین کپچای ساده خودتون که میگه پایتخت ایرن کدام شهر است. یه همچین چیز ساده ای میخوام میتونید کمک کنید؟
لطفا پاسخ دادید از طریق ایمیل اعلام بفرستید که گمتون نکنم. سپاس

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *