نحوه ایجاد CAPTCHA به همراه فرم تماس در PHP

آفلاین
user-avatar
عرفان حشمتی
30 آبان 1400, خواندن در 8 دقیقه

توسعه‌دهندگان هر روز در تلاش‌اند تا فرآیندهای مختلف برنامه‌نویسی را خودکار کنند. طبق این واقعیت که کامپیوترها بسیار سریعتر و دقیق‌تر از انسان هستند، پس به ما امکان می‌دهند بسیاری از کارهای پیچیده روزمره را ساده‌تر کنیم. اما متأسفانه از همین توانایی‌ها می‌توان برای برنامه‌ریزی کامپیوترها در جهت انجام کارهای مخرب مانند ارسال هرزنامه (spam) یا حدس زدن رمزهای عبور استفاده کرد. به همین منظور تمرکز این آموزش بر روی مبارزه با هرزنامه خواهد بود.

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

یکی از راه‌های حل این مشکل، قرار دادن آزمایشی است که می‌تواند بین ربات‌هایی که سعی در انتشار هرزنامه دارند و افرادی که به طور قانونی می‌خواهند با شما تماس بگیرند، تمایز قائل شود. این همان جایی است که CAPTCHA وارد عمل می‌شود. آن‌ها معمولا از تصاویری با ترکیب تصادفی پنج یا شش حرفی که روی پس زمینه رنگی نوشته شده‌اند، تشکیل می‌شوند. ایده این است که یک انسان می‌تواند متن داخل تصویر را بخواند، اما یک ربات نمی‌تواند. بررسی مقدار CAPTCHA پر شده توسط کاربر در برابر نسخه اصلی می‌تواند به شما کمک کند ربات‌ها را از انسان تشخیص دهید. CAPTCHA مخفف عبارت
"completely automated public Turing test to tell computers and humans apart" به معنای تست تورینگ عمومی کاملا خودکار برای تشخیص کامپیوترها و انسان‌هاست.

در این آموزش یاد می‌گیریم که چگونه CAPTCHAهای خود را ساخته و سپس آن‌ها را با فرم تماسی که ایجاد کرده‌ایم، ادغام کنیم.

ایجاد CAPTCHA

برای این کار از کتابخانه PHP GD استفاده خواهیم کرد. می‌توانید خودتان در خصوص نوشتن متن و کشیدن اشکال با GD اطلاعات بیشتری کسب کنید. همچنین باید کمی کد بنویسیم که رشته تصادفی خود را ایجاد کنیم تا روی تصویر ساخته شده نوشته شود.

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

تمام کدهای این بخش در فایل 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$ تمام کاراکترهایی که می‌خواهیم برای تولید رشته CAPTCHA استفاده کنیم را ذخیره می‌کند. ما فقط از حروف بزرگ در الفبای انگلیسی استفاده می‌کنیم تا از هرگونه سردرگمی که ممکن است به دلیل حروف یا اعدادی که می‌توانند شبیه به هم به نظر برسند، جلوگیری شود. همچنین می‌توانید از هر مجموعه‌ای از کاراکترها که دوست دارید برای افزایش یا کاهش سختی CAPTCHA استفاده نمایید.

تابع ما به صورت پیش‌فرض یک رشته پنج حرفی ایجاد می‌کند، اما شما می‌توانید آن مقدار را با ارسال یک پارامتر متفاوت به تابع ()gene_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);


?>

تنها تغییری که در اینجا ایجاد می‌کنیم استفاده از پارامتر سوم در تابع ()gene_string است. این پارامتر سوم به نام secure$ به طور پیش فرض روی true تنظیم شده، بعدا در کد بررسی می‌کنیم که secure$ روی true تنظیم شده است یا خیر و سپس از ()random_int برای تولید رشته استفاده می‌نماییم.

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

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

هنگامی که رشته تصادفی خود را در اختیار داریم، نوبت به نوشتن کد برای ایجاد پس‌زمینه تصویر CAPTCHA می‌رسد. اندازه تصویر 200×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 شروع می‌کنیم. این مقادیر رنگ نهایی پس زمینه تصویر را تعیین می‌کنند. پس از آن حلقه‌ای را برای ایجاد سایه‌های تیره‌تر از رنگ اصلی اجرا می‌کنیم. این رنگ‌ها در یک آرایه ذخیره می‌شوند. روشن‌ترین رنگ اولین عنصر آرایه colors$ ماست و تیره‌ترین رنگ هم آخرین عنصر است. روشن‌ترین رنگ برای پر کردن کل پس زمینه تصویر استفاده می‌شود.

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

کشیدن تمام این مستطیل‌ها رنگ‌های بیشتری را به پس زمینه اضافه می‌کند، در این صورت تشخیص پیش زمینه رشته CAPTCHA از پس زمینه تصویر کمی سخت‌تر می‌شود.

پس زمینه CAPTCHA شما اکنون باید شبیه تصویر زیر باشد.

رندر کردن رشته 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);

?>

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

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

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

اکنون که CAPTCHA خود را ایجاد کرده‌ایم، وقت آن است که آن را به فرم تماس خود اضافه کنیم. برای این منظور از یک فرم تماس نوشته شده در آموزش‌های قبلی کمک می‌گیریم.

همچنین از sessionها برای ذخیره متن CAPTCHA و سپس اعتبارسنجی متن وارد شده توسط بازدیدکنندگان وب سایت استفاده خواهیم کرد. در اینجا کد کامل فایل 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);
?>

فونت‌هایی که می‌خواهید استفاده کنید در دایرکتوری fonts قرار می‌گیرند. اکنون به سادگی می‌توانید کد HTML زیر را در بالای دکمه ارسال پیام در فرم تماس PHP خود اضافه نمایید.

<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 حتی برای انسان نیز سخت خواهد بود. در این مواقع می‌خواهیم که آنها بتوانند یک CAPTCHA جدید را به شیوه‌ای کاربرپسند درخواست کنند. آیکون redo در بالا به ما کمک می‌کند دقیقا این کار را انجام دهیم. بدین منظور تنها کافی است کد جاوااسکریپت زیر را در صفحه HTML فرم تماس اضافه کنید.

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

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

 

آخرین مرحله در ادغام CAPTCHA که با فرم تماس ایجاد کردیم شامل بررسی ورودی مقدار CAPTCHA توسط کاربران هنگام پر کردن فرم و تطبیق آن با مقدار ذخیره شده در session است. فایل 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 = "[email protected]";
        }
        else if($concerned_department == "marketing") {
            $recipient = "[email protected]";
        }
        else if($concerned_department == "technical support") {
            $recipient = "[email protected]";
        }
        else {
            $recipient = "[email protected]";
        }
        
        $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>';
}

?>

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

جمع‌بندی

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

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

منبع

چه امتیازی به این مقاله می دید؟
خیلی بد
بد
متوسط
خوب
عالی

دیدگاه‌ها و پرسش‌ها

برای ارسال دیدگاه لازم است، ابتدا وارد سایت شوید.

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

آفلاین
user-avatar
عرفان حشمتی @heshmati74
مهندس معماری سیستم های کامپیوتری، طراح و توسعه دهنده وب سایت
دنبال کردن

گفتگو‌ برنامه نویسان

بخشی برای حل مشکلات برنامه‌نویسی و مباحث پیرامون آن وارد شو