📜 ⬆️ ⬇️

How to make friends with the Yandex API and AJAX captcha

Dear colleagues. Disputes about whether the so-called “captcha” is needed, whether it brings real benefits in the fight against evil robots, or just harms the project’s usability has long subsided, and everyone who in one way or another was interested in this issue drew the appropriate conclusions for themselves.

However, faced with the need to install a captcha in the authorization form for the next project, and after a few hours of fussing with the reCaptcha service, which generates tons of garbage code on the page, I still could not find a ready solution that would suit me one hundred percent . Well, if you want to do something - do it yourself.

This article focuses on the transformation of a simple and convenient API Yandex - Clean Web into a full-fledged, modern and functional captcha. And since we are talking about the authorization module, I think it would be appropriate to show how our new captcha works in conjunction with the module.

So, we will need:
API Yandex - Net Web .
')
I think that lovers of native js will not need anything else, I used the jQuery library

First we turn to the Yandex API, because first we need to get the desired captcha. After reading the documentation we write the class, which actually gives it to:

class_yandex_capcha.php

class yandexCaptcha { static function get() { $lang = $_SESSION['lang']; if ($lang == "ru") { $type = "std"; } else { $type = "estd"; } $key = " API "; $xmlResponse = file_get_contents("http://cleanweb-api.yandex.ru/1.0/get-captcha?key=".$key."&type=".$type); $xml = simplexml_load_string($xmlResponse); return $xml->url; } } 


Everything is extremely simple - send a GET request with parameters:
$ key - Yandex API Key, You can get it here.
$ type is the type of captcha that we want to receive, the values ​​taken by the variable are fully described in the Yandex documentation.

In my case, the site supports several languages ​​- therefore, depending on the language, we select: std - numbers and the Yandex logo in Russian, or estd - also numbers, but with a logo in English.

The yandexCaptcha :: get () method now returns the image address - this is our captcha.
In addition to the "url image captcha" xml method request returns another parameter - captcha , but there is a little trick thanks to which
the captcha parameter can be not stored, say in a session. It can not be stored at all, why - I will explain a little further.

When our class is ready - we build it into the model of the authorization page on the site:

model_closed.php

 class model_closed extends model { function get_data() { $root = $_SERVER['DOCUMENT_ROOT']; $dataArray['language'] = parse_ini_file($root."/app/languages/".Route::$lang."_closed.ini"); $dataArray['base_href'] = $_SERVER['HTTP_HOST']; require_once($root."/app/core/class/class_yandex_capcha.php"); $dataArray['capcha_url'] = yandexCaptcha::get(); return $dataArray; } } 


The last three lines of the get_data () method give the captcha picture to the view:

But actually the presentation - closed_view.php

 <div class="closedLoginForm"> <input type="text" class="loginInput" value="<?php echo $data['language']['login']; ?>"><br> <input type="password" class="passworldInput" value="<?php echo $data['language']['passworld']; ?>"> <div id="capcha" title="<?php echo $data['language']['reload_image']; ?>"> <img class="yandexCapchaImage" src="" alt="<?php echo $data['language']['capcha']; ?>"> </div> <input type="text" class="capchaInput" value="<?php echo $data['language']['capcha']; ?>"> <div class="loginButton"><div class="loginBottonInner"><?php echo $data['language']['loginBotton']; ?></div></div> <div class="loginError"></div> </div> <script type="text/javascript" src="/js/jquery.js"></script> <script type="text/javascript" src="/js/closed.js"></script> <script type="text/javascript" src="/js/login.js"></script> 


As you can see from the presentation, there are no form tags in the html code , which means that we will most likely use AJAX for authorization .

In the view, we have 3 fields: a login, password, and a captcha field, a captcha picture itself, and a send button.

Send the form, as mentioned earlier, we will use the AJAX request in login.js

A lot of code, but it is here - the whole point
 $(document).ready(function(){ var capchaStartText = $(".capchaInput").val(); var passworldStartText = $(".passworldInput").val(); function login() { var login = $(".loginInput"), passworld = $(".passworldInput"), capcha = $(".capchaInput"), buttontext = $(".loginBottonInner").html(), captchasrc = $(".yandexCapchaImage").attr("src"); var pos = captchasrc.indexOf("="); var key = captchasrc.substr(pos+1); $.ajax({ type: "POST", url: "/app/modules/module_login.php", dataType: "json", data: {login:login.val(), passworld:passworld.val(), captchaCode:key, captchaValue:capcha.val()}, beforeSend: function(){ $(".loginBottonInner").html("..."); } }).done(function(data){ if (data.captcha == 1 && data.login == 1) { location.reload(); } if (data.login == 0) { capchaRenew(); $(".loginBottonInner").html(buttontext); login.focus(); login.select(); passworld.val(passworldStartText); capcha.val(capchaStartText); $(".loginError").html(data.login_error); } if (data.captcha == 0 && data.login == 1) { capchaRenew(); $(".loginBottonInner").html(buttontext); capcha.val(capchaStartText); capcha.focus(); $(".loginError").html(data.captcha_error); } }); } function capchaRenew() { $.ajax({ type: "POST", data: {check:"ok"}, url: "/app/modules/module_capcha_renew.php" }).done(function(html) { $(".yandexCapchaImage").attr("src",html); console.log(html); }); } $("#capcha").click(function(){ capchaRenew(); $(".capchaInput").focus(); }); $(".loginBottonInner").click(function() { login(); }); $(window).keydown(function(eventObject){ if ($(".closedLoginForm input").is(":focus") == true) { if (eventObject.which == 13) { login(); } } }); }); 



Now we need to check the captcha.
To do this, from the fields of our form by clicking on the “send” button or on the Enter button (we love users), we grab all the data - the login, password, character set entered by the user in the captcha field, and one more parameter I wrote about above - the same parameter captcha (see the description of the method yandexCaptcha :: get () ).
The fact is that this parameter is the key for checking the correctness of captcha input by the user initially present on the page as part of the captcha image URL. Yandex gives her all in the same method yandexCaptcha :: get () . We can only “isolate” the captcha parameter from the url of the image address, which we do.

The module_login.php file to which we transfer data using an AJAX request, creates an instance of the Login class — the main class we use to authorize users on the site; calls the siteLogin () method that returns data on the result of the authorization, checking the correctness of the login-password combination and, What interests us most is the correctness of captcha input.

  echo Login::siteLogin($login, $passworld, $captchaCode, $captchaValue); 


So that my article, which already turned out to be much longer than I had planned, did not grow to a completely enormous size, I will give only that part of the Login :: siteLogin method that is responsible for checking the captcha:

class_login.php

Actually code
 class Login { static function siteLogin($login, $password, $captchaCode, $captchaValue) { $path = $_SERVER['DOCUMENT_ROOT']; session_start(); $langArray = parse_ini_file($path."/app/languages/".$_SESSION['lang']."_login.ini"); $login = htmlspecialchars($login); $password = htmlspecialchars($password); $json = Array(); $json['captcha'] = 0; $json['login'] = 0; $json['captcha_error'] = $langArray['captcha_error']; $json['login_error'] = $langArray['login_error']; $key = " API "; $response = file_get_contents("http://cleanweb-api.yandex.ru/1.0/check-captcha?key=".$key."&captcha=".$captchaCode."&value=".$captchaValue); if (strpos($response,"<ok")){ $json['captcha'] = 1; unset($json['captcha_error']); } require_once $path."/app/core/class/class_dbconnect.php"; $mysqli = dbconnect::connect(); $sql = "SELECT `id`,`login`,`passworld`,`rights` FROM `users` WHERE `login` = '".$login."'"; $qr = $mysqli->query($sql); $quant = $qr->num_rows; if ($quant <> 0) { $row = $qr->fetch_assoc(); if ($row['passworld'] == hash("whirlpool","super".$password."orgy")) { $json['login'] = 1; unset($json['login_error']); if ($json['login'] == 1 and $json['captcha'] == 1) { //!    } } } return json_encode($json); } } 



* I thought about it and decided to bring all the code so that the reader could understand where the legs grow from.
The essence of captcha checking is again a GET request according to the Yandex API;

Accordingly, they led the right combination of login / password + captcha passed the test - Hooray! We are authorized.

Further, I think there will be the most important moment of the article . I draw the attention of those who are still reading - it all comes down to the architecture of the application.

Suppose the user made a mistake when entering a captcha, or simply cannot parse the text in the picture and wants to update it.

Warning - he does not need to make any extra gestures

In case of an error, the username and password entered by the user earlier are saved, and the captcha image is automatically updated. And if the user decides to update the captcha - it is enough for him to simply click on it.

Let's go back to the contents of the login.js file, and look at the capchaRenew () function .

  function capchaRenew() { $.ajax({ type: "POST", data: {check:"ok"}, url: "/app/modules/module_capcha_renew.php" }).done(function(html) { $(".yandexCapchaImage").attr("src",html); }); } 


So - we just make a request to the module module_capcha_renew.php the whole function of which comes down to
to reshape the captcha using the same method yandexCaptcha :: get () that we addressed when loading the page and give the address of the new image to the user.

As a result, we have a fully functional captcha, user friendly enough. And most importantly, unlike the same reCapcha, there are no iframes, all the code is rather laconic, and is completely under our control.

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


All Articles