【源码分析】极验验证官方SDK源码分析和实现思路
前言
2016年就这么来了,新的一年,继续努力~
最近,除了12306的验证码火起来以后,还有一个在界面上拖拽的验证码,也火了起来,就是这次要说的极验验证
,在这个万众创新的时代,工具类产品能做到这样,也是很不错的~
源码来源
来自于官网提供的PHP SDK
https://github.com/GeeTeam/gt-php-sdk
官方在http://www.geetest.com/install/sections/idx-basic-introduction.html 页面对整个通讯流程进行了简要说明,所以我这次的侧重点则是实现部分
本次下载的提交版本为 commit fd4b1d8cc6aa30f9c2dc5671ebfddc18e39e892e
源码分析
demo页面
基本的展示界面,文件位置 /static/login.html
下面是截取的验证码对于div和内嵌的js代码
<div class="box" id="div_geetest_lib"><div id="div_id_embed"></div><script type="text/javascript">//这里就是宕机回滚机制var gtFailbackFrontInitial = function(result) {//动态创建出js引用var s = document.createElement('script');s.id = 'gt_lib'; //设置ids.src = 'http://static.geetest.com/static/js/geetest.0.0.0.js'; //设置路径,这里引用的是官网下的jss.charset = 'UTF-8'; //utf8s.type = 'text/javascript'; //设置typedocument.getElementsByTagName('head')[0].appendChild(s); //把引用的js放到header中//初始化loaded变量名称,用来标记是不是已经下载了var loaded = false;//当页面加载状态改变,或者,页面或图像加载完成,执行下面的匿名函数s.onload = s.onreadystatechange = function() {console.log(this);//判断当前的状态if (!loaded && (!this.readyState|| this.readyState === 'loaded' || this.readyState === 'complete')) {//如果没有载入完成,就执行下面的方式进行载入loadGeetest(result);loaded = true;//执行之后,把loaded的状态变成已经读取}};}//get geetest server status, use the failback solution//执行载入操作var loadGeetest = function(config) {//1. use geetest capthcawindow.gt_captcha_obj = new window.Geetest({//载入对应的配置】gt : config.gt, //3386e03c620a4067f18fa92c370f1594challenge : config.challenge, //f1ccacfa56ca8085a59fd493cd4305aaproduct : 'embed',offline : !config.success //表示是不是离线模式});//创建对象,验证码放到div中gt_captcha_obj.appendTo("#div_id_embed");}//创建一个引入js的对象s = document.createElement('script');s.src = 'http://api.geetest.com/get.php?callback=gtcallback';$("#div_geetest_lib").append(s); //放到验证码div的内容//变量赋值给匿名函数var gtcallback =( function() {var status = 0, result, apiFail;//返回一个匿名函数return function(r) {status += 1; //状态+1 ,外层定义变量,供给内部反复赋值使用if (r) {// r Object {success: 1, gt: "3386e03c620a4067f18fa92c370f1594", challenge: "f1ccacfa56ca8085a59fd493cd4305aa"}//如果返回的结果失败.下面进行一秒后的再次重试result = r;setTimeout(function() {if (!window.Geetest) {apiFail = true;gtFailbackFrontInitial(result)}}, 1000)}else if(apiFail) {return}//如果成功 , 也就是执行两次// 当前返回函数if (status == 2) {//载入页面loadGeetest(result);}}})()//ajax访问本地连接库,返回供页面展示的参数$.ajax({url : "../web/StartCaptchaServlet.php?rand="+Math.round(Math.random()*100),type : "get",dataType : 'JSON',success : function(result) {// console.log(result);gtcallback(result)}})</script></div>
js思路比较简单
- 引入js
- ajax获取展示验证码的对象参数
- 如果没有载入完成就再次载入
- 通过ajax返回的参数,再用js创建验证码对象
- 展示在页面上
ajax本地库
url : “../web/StartCaptchaServlet.php?rand=”+Math.round(Math.random()*100)
这里对应的地址,是ajax本地的地址,后面接了一个随机的地址
文件位置 /web/StartCaptchaServlet.php
/*** 使用Get的方式返回:challenge和capthca_id 此方式以实现前后端完全分离的开发模式 专门实现failback*@author Tanxu*/
error_reporting(0);
//吐槽一下,还没有使用命名空间,并且放在项目的vendor中的配置文件还需要修改而不是单拿出来,真像一个没有完成的sdk
require_once dirname(dirname(__FILE__)) . '/lib/class.geetestlib.php';
$GtSdk = new GeetestLib();
session_start();
$return = $GtSdk->register();
//返回的结果是0或者1,session里面也没有保存其他结果
if ($return) {$_SESSION['gtserver'] = 1;$result = array('success' => 1,'gt' => CAPTCHA_ID,'challenge' => $GtSdk->challenge //所以返回展示的界面都在这个参数内部);echo json_encode($result);
}else{$_SESSION['gtserver'] = 0;$rnd1 = md5(rand(0,100));$rnd2 = md5(rand(0,100));$challenge = $rnd1 . substr($rnd2,0,2);$result = array('success' => 0,'gt' => CAPTCHA_ID,'challenge' => $challenge);$_SESSION['challenge'] = $result['challenge'];echo json_encode($result);
}
和服务器通讯的类
js进行ajax,到最后和服务器进行通讯的类
文件位置 lib/class.geetestlib.php
从文件名和引用配置文件来看,做SDK并没有考虑到命名空间 = =
/*** 极验行为式验证安全平台,php 网站主后台包含的库文件*@author Tanxu*/
//引入配置文件
require_once dirname(dirname(__FILE__)) . '/config/config.php';
class GeetestLib{const GT_SDK_VERSION = 'php_2.15.7.6.1';//初始化返回值public function __construct() {$this->challenge = "";}/***判断极验服务器是否down机**@return*/public function register() {$url = "http://api.geetest.com/register.php?gt=" . CAPTCHA_ID;$this->challenge = $this->send_request($url);//判断返回值是不是32位,来界定是不是服务器能用if (strlen($this->challenge) != 32) {return 0;}return 1;}//进行验证public function validate($challenge, $validate, $seccode) {if ( ! $this->check_validate($challenge, $validate)) {return FALSE;}$data = array("seccode"=>$seccode,"sdk"=>self::GT_SDK_VERSION,);$url = "http://api.geetest.com/validate.php";$codevalidate = $this->post_request($url, $data);if (strlen($codevalidate) > 0 && $codevalidate == md5($seccode)) {return TRUE;} else if ($codevalidate == "false"){return FALSE;} else {return $codevalidate;}}private function check_validate($challenge, $validate) {if (strlen($validate) != 32) {return FALSE;}if (md5(PRIVATE_KEY.'geetest'.$challenge) != $validate) {return FALSE;}return TRUE;}//通过curl和远程服务器进行通信private function send_request($url){if(function_exists('curl_exec')){$ch = curl_init();curl_setopt ($ch, CURLOPT_URL, $url);curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);$data = curl_exec($ch);curl_close($ch);}else{$opts = array('http'=>array('method'=>"GET",'timeout'=>2,) );$context = stream_context_create($opts);$data = file_get_contents($url, false, $context);}return $data;}/***解码随机参数**@param $challenge*@param $string*@return*/private function decode_response($challenge,$string) {if (strlen($string) > 100) {return 0;}$key = array();$chongfu = array(); //重复 = =$shuzi = array("0"=>1,"1"=>2,"2"=>5,"3"=>10,"4"=>50); //数字 = =$count = 0;$res = 0;$array_challenge = str_split($challenge);$array_value = str_split($string);for ($i=0; $i < strlen($challenge); $i++) { $item = $array_challenge[$i];if (in_array($item, $chongfu)) {continue;}else{$value = $shuzi[$count % 5];array_push($chongfu, $item);$count++;$key[$item] = $value;}}for ($j=0; $j < strlen($string); $j++) { $res += $key[$array_value[$j]];}$res = $res - $this->decodeRandBase($challenge);return $res; }/****@param $x_str*@return*/private function get_x_pos_from_str($x_str) {if (strlen($x_str) != 5) {return 0;}$sum_val = 0;$x_pos_sup = 200;$sum_val = base_convert($x_str,16,10);$result = $sum_val % $x_pos_sup;$result = ($result < 40) ? 40 : $result;return $result;}/****@param full_bg_index*@param img_grp_index*@return*/private function get_failback_pic_ans($full_bg_index,$img_grp_index) {$full_bg_name = substr(md5($full_bg_index),0,9);$bg_name = substr(md5($img_grp_index),10,9);$answer_decode = "";// 通过两个字符串奇数和偶数位拼接产生答案位for ($i=0; $i < 9; $i++) { if ($i % 2 == 0) {$answer_decode = $answer_decode . $full_bg_name[$i];}elseif ($i % 2 == 1) {$answer_decode = $answer_decode . $bg_name[$i];}}$x_decode = substr($answer_decode, 4 , 5);$x_pos = $this->get_x_pos_from_str($x_decode);return $x_pos;}/*** 输入的两位的随机数字,解码出偏移量* *@param challenge*@return*/private function decodeRandBase($challenge) {$base = substr($challenge, 32, 2);$tempArray = array();for ($i=0; $i < strlen($base); $i++) { $tempAscii = ord($base[$i]);$result = ($tempAscii > 57) ? ($tempAscii - 87) : ($tempAscii -48);array_push($tempArray,$result);}$decodeRes = $tempArray['0'] * 36 + $tempArray['1'];return $decodeRes;}/*** 得到答案* *@param validate*@return*/public function get_answer($validate) {if ($validate) {$value = explode("_",$validate);$challenge = $_SESSION['challenge'];$ans = $this->decode_response($challenge,$value['0']);$bg_idx = $this->decode_response($challenge,$value['1']);$grp_idx = $this->decode_response($challenge,$value['2']);$x_pos = $this->get_failback_pic_ans($bg_idx ,$grp_idx);$answer = abs($ans - $x_pos);if ($answer < 4) {return 1;}else{return 0;}}else{return 0;}}public function post_request($url, $postdata = null){$data = http_build_query($postdata);if(function_exists('curl_exec')){$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);if(!$postdata){curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);}else{curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, $data);}$data = curl_exec($ch);curl_close($ch);}else{if($postdata){$url = $url.'?'.$data;$opts = array('http' => array('method' => 'POST','header'=> "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n",'content' => $data));$context = stream_context_create($opts);$data = file_get_contents($url, false, $context);}}return $data;}
}
直接访问地址 "http://api.geetest.com/register.php?gt=" . CAPTCHA_ID
,可以得到每次不一样的32位字符串,所以这个加密字符串就是每次验证码显示的内容,经过js解析之后,进行展示
验证
每次展示验证码,都会从服务器获取验证码对于的参数,经过动态加载的js文件,展示出对应的验证码。
对应的验证操作,它会根据表单的提交方式,提交用户滑动后的结果,从服务器端进行校验。
表单提交的结果
[geetest_challenge] => aec462f7abc1edf69048b1057c5d2ac7l7
[geetest_validate] => 2abebf70f08b839e3037f6417459a65f
[geetest_seccode] => 2abebf70f08b839e3037f6417459a65f|jordan
再通过后台进行校验
服务器校验
本地服务器进行对用户拖拽验证码进行校验。
文件对应位置 /web/VerifyLoginServlet.php
/*** 本文件示例只是简单的输出 Yes or No*/
// error_reporting(0);
require_once dirname(dirname(__FILE__)) . '/lib/class.geetestlib.php';
//通过session进行判断,是不是需要采用本地算法校验
session_start();
$GtSdk = new GeetestLib();
if ($_SESSION['gtserver'] == 1) {//在线判断,传递参数过去,返回拖拽是否成功的结果$result = $GtSdk->validate($_POST['geetest_challenge'], $_POST['geetest_validate'], $_POST['geetest_seccode']);if ($result == TRUE) {echo 'Yes!';} else if ($result == FALSE) {echo 'No';} else {echo 'FORBIDDEN';}
}else{//本地进行检验,使用类库内部的算法进行匹配,返回结果if ($GtSdk->get_answer($_POST['geetest_validate'])) {echo "yes";}else{echo "no";}
}
就这样,完成了下图的流程
项目核心
- 前端展示机制,通过js动态生成拖拽页面元素进行本地拖拽展示
- 后端检验,要求和前端的算法进行解密,匹配是否验证准确
不足
- 本地验证只是能进行本地校验结果,如果没有对官方服务器的通信,还是不能展示出验证码的,同时也将网站的所有验证信息暴露给了极验官方
引用资料
- http://www.geetest.com/install/sections/idx-basic-introduction.html#id7
- https://github.com/GeeTeam/gt-php-sdk
【源码分析】极验验证官方SDK源码分析和实现思路相关推荐
- Spring Boot 极验验证滑动验证码
概要 基于极验验证官网 java版gt3-java-sdk改编,使用Spring Boot 整合的极验滑动验证,包含form表单登录和ajax登录两种情况. 目录 注册账户获取ID和KEY Demo源 ...
- 极验验证--滑块验证
极验验证–滑块验证 本文通过通过模拟登录极验网站,完成滑块验证 所用语言和相关模块 python3.6 selenium requests` PIL 极验验证码特点分析 极验验证是一种在计算机领域用于 ...
- java项目极验验证_有关极验验证SDK的使用过程-Java版本
在这里我会一步一步的实现极验验证配置到自己的项目上的详细过程(Java版本)! 首先,我们看一下我们要实现的预期效果: (1),打开服务器,进入到登陆页面 (2),点击提交按钮,进入验证界面 (3), ...
- 更安全的验证方式-极验验证
简介 极验验证是一种在计算机领域区分自然人和机器人的,通过简单集成的方式,为开发者提供安全.便捷的云端验证服务,与以往传统验证不同的是,极验通过分析用户完成拼图过程中的行为特征,通过数据分析来判断是人 ...
- 极验验证(滑动验证)的使用
极验验证目录 一.样例 二.注册账号 三.获取ID 四.极验官方文档(参考) 五.SpringBoot集成极验 5.1.maven依赖(可能有些需要自己去导,个人的包依赖太多不好全部放上来,核心就这两 ...
- Android极验验证集成【滑块+文字验证】
前言: 先复制一段官网介绍 产品介绍 极验「行为验证」是一项可以帮助你的网站与APP识别与拦截机器程序批量自动化操作的SaaS应用.它是由极验开发的新一代人机验证产品,它不基于传统"问题-答 ...
- 乱序图片 极验_极验验证吴渊:传统图片验证方式已经无效了!
吴渊,极意网络CEO 黑五月频发的宕机门告诉我们:数据安全,所有创业者都应该关注! 让我们来听听IDG资本的两位投资人大佬的深刻分析,以及5家创业公司CEO/CTO大拿的深切呼吁吧!--这里不止有干货 ...
- 【JavaScript 逆向】极验三代无感验证码逆向分析
相关文章 [JavaScript 逆向]极验三代滑块验证码逆向分析 [JavaScript 逆向]极验四代无感验证码逆向分析 [JavaScript 逆向]极验四代滑块验证码逆向分析 声明 本文章中所 ...
- 极验验证简介(待续)
百度百科介绍: 极验验证是一种在计算机领域用于区分自然人和机器人的,通过简单集成的方式,为开发者提供安全.便捷的云端验证服务. 与以往传统验证码不同的是,极验通过分析用户完成拼图过程中的行为特征,通过 ...
最新文章
- 几个受益终身的英文缩写
- 20165224 陆艺杰 网络攻防 实验1
- 每日一皮:互联网人去银行面试...
- 动态链接库编写与使用(VC6)
- 三丰三坐标编程基本步骤_数控车床编程,经典实例教程
- P4149-[IOI2011]Race【点分治】
- flex 学习篇 ---- 导航类容器
- ubuntu系统debootstrap的再三实验
- MySQL中的事务及读写锁实现并发访问控制
- NOWCODER暑期多校第四场F:Beautiful Garden(签到题)题解
- android7.1获取存储权限,Android外部存储
- pano2vr输出的HTML手机可以看吗,Pano2VR怎么导出手机可看, Pano2VR导出手机查看教程...
- 使用phpStudy搭建74cms(详)
- Unity - Timeline 之Creating a Timeline Asset and Timeline instance(创建Timeline Asset和Timeline 实例)
- 21cn邮箱服务器,21cn邮箱客户端
- 美国人眼中的大数据法律问题
- Freeswitch智能语音开发之TTS
- 首席数据官:从哪里来?到哪里去?
- 【计算机基础】Macbook安装Anaconda和使用Jupyter notebook
- MySQL 生成累计乘积