📜 ⬆️ ⬇️

Captcha What's again?

Dobvresu, Habravchane.

Disclaimer


Captcha does not pretend to be unique and unbreakable. But it will definitely add difficulty to some bots. It will be considered its graphic, as well as the protective part. Captcha was created specifically for one of my projects, but I still can not give the link - the server is not combat, and it will not sustain the habraeffect. But in the "Startups" section I will definitely write about him when the time comes.

So, anyone interested - welcome to habrakat.
')

Start


First of all, let's decide what the captcha is for. To protect against all kinds of programs acting to the detriment of the project, of course. And to protect this you need a captcha, which simple programs will not cope with. How to achieve this effect? First, we need a reliable “client-server” part - the very layer that can be cracked under certain conditions quite easily. If after receiving 2-3 captcha the attacker realizes that domain.ru/captcha-get.php?code= 12345 - this is our captcha - there will be nothing to hack.

In addition, a simple partitioning of a captcha into elements and an attempt to determine them by any kind of similarity-correspondence is also a popular practice (and even works on projects such as Yandex). It means that it is impossible to single out strictly separate elements.

The grafical part


The longest. A lot of code and some pictures.

Point one. We generate the text. How to generate 5-6 characters in PHP, I think, many people know. I took the path of least resistance:
$text = ""; for($i = 0; $i < 5; $i++){ switch(mt_rand(1,3)){ case(1): {$c=chr(rand(ord('a'),ord('z')));break;} case(2): {$c=chr(rand(ord('A'),ord('Z')));break;} case(3): {$c=rand(0,9);break;} } $text.=$c; } 


Point two. The text should be displayed on some image. I chose the size of the image 200x60 pixels - it just came up to my needs and a pop-up window.
 $im = imageCreateTrueColor(200,60); 


Point three. You need some background that will be sufficiently contrasted with the inscription, but at the same time, it can not be simply filtered. I decided not to bother with this at this stage, but simply to put some kind of filter on the final result. In the meantime, the background will be a purely random color.

 $white = imageColorAllocate($im, mt_rand(170,255),mt_rand(170,255),mt_rand(170,255)); $black = imageColorAllocate($im, mt_rand(0,140),mt_rand(0,140),mt_rand(0,140)); imagefilledrectangle($im,0,0,200,60,$white); 

Here it is necessary to explain a little. I assign conditionally white (light) and conditionally black (dark) colors, after which ... yes, I fill the whole field with them.

The next is better. Point four - the text. It can be oblique, and should be drawn in one of several fonts. I typed 6 out of my system palette and wrote this code:

 $a = mt_rand(-5,5); $s = 40; $f = "captcha_font".mt_rand(1,6).".ttf"; do{ $s--; $b = imagettfbbox ($s, $a, $f, $text); $x = $b[2]-$b[0]; $y = $b[1]-$b[7]; }while(($x>=200)||($y>=60)); $black = imageColorAllocate($im, mt_rand(0,140),mt_rand(0,140),mt_rand(0,140)); imagettftext($im, $s, $a, 100-$x/2, 30+$y/2, $black, $f, $text); imagewave($im); 


Yes, since it is not known exactly which font with which inclination and size will exactly fit into the frame, I used exactly the reverse brute force method. And he ... works. At the moment we have the following result:

imageimage

But wait, what is the ImageWave function? And this is the first part of our distortion. Adds random waves to our pictures. In general, it looks something like this:

 function imagewave($im){ $sx = imagesx($im); $sy = imagesy($im); $dx = mt_rand(0,$sx/2); $xf = mt_rand(-100,100)/20; for($x = 0; $x < $sx; $x++){ $yd = floor(sin(deg2rad($dx+$x)*$xf)*2); $l = array(); for($y = 0; $y < $sy; $y++) $l[$y] = imagecolorat($im,$x,$y); if($yd>0) for($y = 0; $y < $yd; $y++) array_push($l,array_shift($l)); elseif($yd<0) for($y = 0; $y > $yd; $y--) array_unshift($l,array_pop($l)); for($y = 0; $y < $sy; $y++) imagesetpixel($im,$x,$y,$l[$y]); } $dy = mt_rand(0,$sy/2); $yf = mt_rand(-100,100)/20; for($y = 0; $y < $sy; $y++){ $xd = floor(sin(deg2rad($dy+$y)*$yf)*2); $l = array(); for($x = 0; $x < $sx; $x++) $l[$x] = imagecolorat($im,$x,$y); if($xd>0) for($x = 0; $x < $xd; $x++) array_push($l,array_shift($l)); elseif($xd<0) for($x = 0; $x > $xd; $x--) array_unshift($l,array_pop($l)); for($x = 0; $x < $sx; $x++) imagesetpixel($im,$x,$y,$l[$x]); } } 

Perhaps I didn’t choose the fastest method, but it still works. And the captcha after its use becomes this:
imageimage

Already much better. I even decided to add one more text output at a random angle, a random font and the same color (!), Which complicates the analysis of the picture into parts:
imageimage

Everything? And no. To make the bots even more complicated, I decided to cross out our picture a couple of times:
 $yd = mt_rand(-30,30); $black = imageColorAllocate($im, mt_rand(0,140),mt_rand(0,140),mt_rand(0,140)); imageline($im,0,$yd,200,30+$yd,$black); imagewave($im); $black = imageColorAllocate($im, mt_rand(0,140),mt_rand(0,140),mt_rand(0,140)); imageline($im,0,30+$yd,200,60+$yd,$black); imagewave($im); 

imageimage

And finally - as I promised at the beginning, let's play a little with the flowers. I will attach only the function itself and the result of its work - the challenge to insert, I think, will be no problem.
 function imagerecolor($im){ $sx = imagesx($im); $sy = imagesy($im); for($x = 0; $x < $sx; $x++){ $rd = mt_rand(80, 120) / 100; $gd = mt_rand(80, 120) / 100; $bd = mt_rand(80, 120) / 100; for($y = 0; $y < $sy; $y++) { $c = imagecolorat($im,$x,$y); $r = (($c >> 16) & 0xFF) * $rd; $g = (($c >> 8) & 0xFF) * $gd; $b = ($c & 0xFF) * $bd; $c = imagecolorallocate($im, $r, $g, $b); imagesetpixel($im,$x,$y,$c); } } } 

imageimage
Well, what kind of happiness without our corporate logo on the captcha? Think about the result yourself, and the code is as simple as it can be:
 $l = imagecreatefrompng('../static/image/icon16.png'); imagecopy($im,$l,184,44,0,0,16,16); imagedestroy($l); 

With the image over - proceed to its convenient conclusion.

Client-server part


The easiest way to store some code (session ID, hash or other information) in cookies. But if the user works in several (at least, two) windows at once, and in both windows suddenly a captcha appeared? Here we come to the aid of such a scheme as base64-encoded images. Since I used JavaScript, the output of this stuff was made quite simply:

 ob_start(); imagepng($im); $b64 = base64_encode(ob_get_contents()); ob_end_clean(); imageDestroy($im); $response = array('key'=>sha512(md5(strtolower($text)).date('DmyH').get_ip().'RANDOM_SALT'),'img'=>'data:image/png;base64,'.$b64); 

And, somewhere further:
 $response = json_encode($response); ?>Api.callback(<?=$cid?>,<?=$response?>); 

The output of the form in this way can also be done - I leave it to you for homework. ;)
Please note that in the hash we use some salt (for the difficulty of busting), and the captcha is valid only in the current hour (if the user is completely out of luck, and he received a captcha, for example, at 15:59, and entered it at 16:00, then it will not work). The output of such a picture is carried out simply by substituting the $ response ['img'] field in the src field of the desired picture.
By the way, the sha512 function:
 function sha512($s){ return hash('sha512',$s); } 


For homework, I also leave you a replacement for similar characters in the hash ('0' and 'O', 'l' and 'I', and so on).

That's all for today. Good luck. ;)

Source: https://habr.com/ru/post/132719/


All Articles