当前位置:   article > 正文

【验证码逆向专栏】某验全家桶细节避坑总结_空间推理验证码识别

空间推理验证码识别

00

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!

前言

某验的验证码总体来说还是很简单的,但是也有一些细节可能要注意一下,如果你扣完算法发现验证报各种各样的错误,或者在官网的 demo 能验证通过,在其他网站却验证失败,那么就可以看看本文总结的细节你有没有注意到。

除此之外,本文还分享了一些验证码的识别方案、轨迹的处理,这些方法大多来自网络上其他大佬的分享,直接百度就能搜到,本文只是做了一个归纳总结。

关于 w 值

三代里面,有几个接口请求都有 w,但除了最后一个校验接口 ajax.php 以外,其他接口的 w 可以置空,但也不完全都是这样,比如三代的一键通过模式(无感验证),在请求 get.php 接口获取 c 和 s 值的时候,同样校验了 w 值,因此需要获取两次 w 值,而这两次 w 值的生成方式还不太一样,需要自己细心分两次扣一下。如果你第一次不带 w,或者 w 生成错误,就会报以下错误:

{'status': 'error', 'error': 'param decrypt error', 'user_error': '网络不给力', 'error_code': 'error_03'}

    关于时间间隔

    三代里面,整个流程走得太快了也是不行的,需要在生成 w 值之后,随机停留个 2 秒左右,以三代的点选(文字点选、图标点选、语序点选、空间推理)为例,如果整得太快了验证失败会报以下错误:

    {'status': 'success', 'data': {'result': 'fail', 'msg': ['duration short']}}

      关于 challenge

      三代里面,有个 challenge 参与了很多接口的请求,三代滑块比较特殊,第一次获取到了一个 challenge,后面的第二个 get.php 请求返回数据里会有一个新的 challenge,新的 challenge 比第一次的 challenge 多了两位数,后续的请求要用这个新的 challenge 才行,不然的话会报以下错误:

      {'success': 0, 'message': 'fail'}

        关于 c 和 s

        三代里面,有个 c 和 s 的值参与了 w 的计算,点选系列和滑块,第一次 get.php 请求会返回一个 c 和 s,第二次 get.php 请求也会返回一个 c 和 s,两次的 c 一般是不变的,但 s 会变,生成 w 要用第二次 get.php 返回的 s 才行,不然的话会报以下错误:

        {'success': 0, 'message': 'forbidden'}

          关于两次 get.php 和 ajax.php 请求

          同样还是三代里面,点选系列和滑块,会有两次以 get.phpajax.php 结尾的请求,第一次的 get.php 返回的是一些主题、域名、提示文字等信息,第一次的 ajax.php 返回的是验证码的类型,这两次请求返回的数据虽然对我们没太大用处,但是我们还是得发起请求,不然后续的请求就不对,必须得按照他这个顺序来才行。

          关于智能组合验证

          智能组合验证说白了就是事先不知道是什么类型,四代在很多网站都是选择智能模式,处理方法也很简单,事先把所有类型都准备好,然后通过接口返回的验证码类型来接入不同的逻辑。

          三代判断逻辑:第一次的 ajax.php 接口,返回值会告诉你是点选 (click) 还是滑块 (slide),其中点选又分为文字点选、图标点选、语序点选和空间推理,它们的类型都为 click,这个时候就要进行第二次判断,第二次 get.php 返回的 pic_type 字段,会告诉你是文字点选 (word)、图标点选 (icon)、语序点选 (phrase) 还是空间推理 (space)。

          四代判断逻辑:四代更简洁,load 接口会有一个 captcha_type 字段,会直接告诉你是滑块、点选(以及哪种类型的点选)、五子棋还是九宫格等。

          关于扣 w 的算法

          扣 w 的算法,里面也有一些细节,某些参数也值得注意。

          passtime

          不管是二代、三代还是四代,生成 w 的时候经常有个 passtime 参与了计算,这个值分为两种情况,如果是滑块,这个值应该是滑动花费的时间,因为滑块的轨迹里包含了时间,所以应该直接取轨迹的最后一个时间值即可,即 track[track.length - 1][2],以三代为例,如果这个值和你轨迹里的时间不一致,就会报以下错误:

          {'success': 0, 'message': 'forbidden'}

            01

            除了滑块,其他情况下,这个值写死就行,不过还是建议写个随机值:Math.floor((Math.random()*500) + 4000)

            pow_sign 和 pow_msg

            这两个参数是四代里独有的,如果你是在 gt4.geetest.com 进行调试,你会发现 pow_msg 的组成格式如下:

            1|0|md5|datetime|captcha_id|lot_number||随机字符串

              pow_sign 则是 pow_msg 经过 MD5 加密后的值,如下图所示:

              02

              这里你可能不注意的话,直接按照这个格式写死了,特别是最后一个随机值,真的随机其实是不行的,真随机就会导致你在某些网站里能通过,某些网站不能通过。搜索 pow_sign 或者 pow_msg 的 Unicode 值,总共就三个地方,都下个断点,刷新一下网页,断下之后仔细分析,其实是有三种算法的,如下图所示:

              03

              上图中第 6819 行的 h 就是随机值,后续会根据不同算法进行计算,判断这个随机值是否满足一些条件,满足才是正确的,可以在 load 接口返回的 pow_detail 字段判断是 MD5、SHA1 还是 SHA256,如下图所示:

              04

              这一段的处理逻辑扣出来就是这样的:

              var CryptoJS = require("crypto-js");
              
              
              function getRandomString(){
                  function e(){
                      return (65536 * (1 + Math.random()) | 0).toString(16).substring(1);
                  }
                  return e() + e() + e() + e();
              }
              
              function get_pow(pow_detail, captcha_id, lot_number) {
                  var n = pow_detail.hashfunc;
                  var i = pow_detail.version;
                  var r = pow_detail.bits;
                  var s = pow_detail.datetime;
                  var o = "";
              
                  var a = r % 4;
                  var u = parseInt(r / 4, 10);
                  var c = function g(e, t) {
                      return new Array(t + 1).join(e);
                  }("0", u);
                  var _ = i + "|" + r + "|" + n + "|" + s + "|" + captcha_id + "|" + lot_number + "|" + o + "|";
              
                  while (1) {
                      var h = getRandomString()
                        , l = _ + h
                        , p = void 0;
                      switch (n) {
                          case "md5":
                          p = CryptoJS.MD5(l).toString();
                          break;
                      case "sha1":
                          p = CryptoJS.SHA1(l).toString();
                          break;
                      case "sha256":
                          p = CryptoJS.SHA256(l).toString();
                      }
                      if (0 == a) {
                          if (0 === p.indexOf(c))
                              return {
                                  "pow_msg": _ + h,
                                  "pow_sign": p
                              };
                      } else if (0 === p.indexOf(c)) {
                          var f = void 0
                            , d = p[u];
                          switch (a) {
                          case 1:
                              f = 7;
                              break;
                          case 2:
                              f = 3;
                              break;
                          case 3:
                              f = 1;
                          }
                          if (d <= f)
                              return {
                                  "pow_msg": _ + h,
                                  "pow_sign": p
                              };
                      }
                  }
              }
              
              // 测试用例
              // var pow_detail = {
              //     bits: 0,
              //     datetime: "2023-02-09T11:04:17.687400+08:00",
              //     hashfunc: "md5",
              //     version: "1"
              // }
              // var captcha_id = "08c16c99330a5a1d6b7f4371bbd5a978"
              // var lot_number = "1417b7e362b748429003c412b3aa300c"
              // console.log(get_pow(pow_detail, captcha_id, lot_number))
              • 1
              • 2
              • 3
              • 4
              • 5
              • 6
              • 7
              • 8
              • 9
              • 10
              • 11
              • 12
              • 13
              • 14
              • 15
              • 16
              • 17
              • 18
              • 19
              • 20
              • 21
              • 22
              • 23
              • 24
              • 25
              • 26
              • 27
              • 28
              • 29
              • 30
              • 31
              • 32
              • 33
              • 34
              • 35
              • 36
              • 37
              • 38
              • 39
              • 40
              • 41
              • 42
              • 43
              • 44
              • 45
              • 46
              • 47
              • 48
              • 49
              • 50
              • 51
              • 52
              • 53
              • 54
              • 55
              • 56
              • 57
              • 58
              • 59
              • 60
              • 61
              • 62
              • 63
              • 64
              • 65
              • 66
              • 67
              • 68
              • 69
              • 70
              • 71
              • 72
              • 73
              • 74
              • 75

              只有经过这样处理,才能保证 pow_signpow_msg 是正确的,才能适配不同网站、不同算法的验证。

              随机变化的字符串

              不管是哪一代,都会有一个 16 位随机字符串参与了 w 的加密计算,这个随机字符串一般都会用到两次,这两次要保证是一样的才行。

              05

              如果这个字符串两次不一样,二、三代验证会报错如下:

              {'status': 'error', 'error': 'param decrypt error', 'user_error': '网络不给力', 'error_code': 'error_03'}

                四代验证会报错如下:

                {'status': 'error', 'code': '-50002', 'msg': 'param decrypt error', 'desc': {'type': 'defined error'}}

                  随机变化的键值对

                  三四代生成 w 的过程中会有一个随机键值对,每隔一段时间就会变化,类似于 {h9s9: '1803797734'},这个键值对写死也可以,貌似不影响,但如果非要和网页一样随机起来应该怎么做呢?

                  以三代滑块为例,断点到 o 参数生成的地方,后续有个 langep 组成的 s 参数,经过 window[$_CAHJd(744)](s) 处理后,s 里就新增了一个键值对(不同类型略有差别,但生成的位置一定离 o 不远,仔细跟即可),如下图所示:

                  06

                  跟进去,会来到 gct.xxx.js 里,也是经过了一个方法后,就多了这个键值对:

                  07

                  这个 gct 的 js 具体地址可以在前面的 get.php 之类的请求里拿到,由于里面是不断变化的,所以可以采取动态请求这个 js,动态导出获取这个值,一个简单的逻辑如下:

                  import re
                  import execjs
                  import requests
                  
                  
                  headers = {
                      "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
                  }
                  
                  # gct js 路径
                  gct_path = "https://static.geetest.com/static/js/gct.b71a9027509bc6bcfef9fc6a196424f5.js"
                  gct_js = requests.get(gct_path, headers=headers).text
                  # 正则匹配需要调用的方法名称
                  function_name = re.findall(r"\)\)\{return (.*?)\(", gct_js)[0]
                  # 查找需要插入全局导出代码的位置
                  break_position = gct_js.find("return function(t){")
                  # window.gct 全局导出方法
                  gct_js_new = gct_js[:break_position] + "window.gct=" + function_name + ";" + gct_js[break_position:]
                  # 添加自定义方法调用 window.gct 获取键值对
                  gct_js_new = "window = global;" + gct_js_new + """
                  function getGct(){
                      var e = {"lang": "zh", "ep": "test data"};
                      window.gct(e);
                      delete e["lang"];
                      delete e["ep"];
                      return e;
                  }"""
                  gct = execjs.compile(gct_js_new).call("getGct")
                  print(gct)
                  
                  # {'h9s9': '1803797734'}
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8
                  • 9
                  • 10
                  • 11
                  • 12
                  • 13
                  • 14
                  • 15
                  • 16
                  • 17
                  • 18
                  • 19
                  • 20
                  • 21
                  • 22
                  • 23
                  • 24
                  • 25
                  • 26
                  • 27
                  • 28
                  • 29
                  • 30

                  补环境中可能用到的方法

                  补环境可能会遇到 window.crypto.getRandomValues() 方法,例如三代滑块的位置如下:

                  08

                  可以用以下代码来实现:

                  window = global;
                  window.crypto = {
                      getRandomValues: getRandomValues_
                  }
                  
                  function randoms(min, max) {
                      return Math.floor(Math.random() * (max - min + 1) + min)
                  }
                  
                  function getRandomValues_(buf) {
                      var min = 0,
                      max = 255;
                      if (buf.length > 65536) {
                          var e = new Error();
                          e.code = 22;
                          e.message = 'Failed to execute \'getRandomValues\' : The ' + 'ArrayBufferView\'s byte length (' + buf.length + ') exceeds the ' + 'number of bytes of entropy available via this API (65536).';
                          e.name = 'QuotaExceededError';
                          throw e;
                      }
                      if (buf instanceof Uint16Array) {
                          max = 65535;
                      } else if (buf instanceof Uint32Array) {
                          max = 4294967295;
                      }
                      for (var element in buf) {
                          buf[element] = randoms(min, max);
                      }
                      return buf;
                  }
                  
                  // 测试
                  // var a = new Uint32Array(256);
                  // console.log(window.crypto.getRandomValues(a))
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8
                  • 9
                  • 10
                  • 11
                  • 12
                  • 13
                  • 14
                  • 15
                  • 16
                  • 17
                  • 18
                  • 19
                  • 20
                  • 21
                  • 22
                  • 23
                  • 24
                  • 25
                  • 26
                  • 27
                  • 28
                  • 29
                  • 30
                  • 31
                  • 32

                  另外,还有个用到 window.performance.timing 的地方,如下图所示:

                  09

                  这个主要是一些性能指标,直接搞个时间戳随机加值就行了:

                  function timing() {
                      var now = Date.now()
                      var tim = {
                          "navigationStart": now,
                          "unloadEventStart": now + 200,
                          "unloadEventEnd": now + 200,
                          "redirectStart": 0,
                          "redirectEnd": 0,
                          "fetchStart": now + 100,
                          "domainLookupStart": now + 150,
                          "domainLookupEnd": now + 250,
                          "connectStart": now + 30,
                          "connectEnd": now + 50,
                          "secureConnectionStart": now + 52,
                          "requestStart": now + 72,
                          "responseStart": now + 91,
                          "responseEnd": now + 92,
                          "domLoading": now + 99,
                          "domInteractive": now + 105,
                          "domContentLoadedEventStart": now + 105,
                          "domContentLoadedEventEnd": now + 111,
                          "domComplete": now + 111,
                          "loadEventStart": now + 111,
                          "loadEventEnd": now + 111,
                      }
                      return tim
                  }
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8
                  • 9
                  • 10
                  • 11
                  • 12
                  • 13
                  • 14
                  • 15
                  • 16
                  • 17
                  • 18
                  • 19
                  • 20
                  • 21
                  • 22
                  • 23
                  • 24
                  • 25
                  • 26

                  关于验证码的识别

                  识别主要有三种方法,第一个是会深度学习的话,自己用 OpenCV 之类的去识别,第二个当然是非常牛逼的 ddddocr(https://github.com/sml2h3/ddddocr),还支持自己训练,是不错的选择,当然也有一些其他开源库,这里就不一一举例了,第三个就是打码平台,这里推荐云码打码,可通过我的链接注册:https://www.jfbym.com/register/TG17764 ,自己去官网看,支持非常多的类型,甚至谷歌验证码都可以,价格也不贵,实测成功率 99%,还是不错的。这里贴一个 OpenCV 识别滑块的源码(来源于互联网收集),效果还不错:

                  # CV2 识别滑块缺口距离
                  
                  import cv2
                  import PIL
                  import numpy as np
                  from PIL import Image
                  from pathlib import Path
                  
                  
                  def imshow(img, winname='test', delay=0):
                      """cv2展示图片"""
                      cv2.imshow(winname, img)
                      cv2.waitKey(delay)
                      cv2.destroyAllWindows()
                  
                  
                  def pil_to_cv2(img):
                      """
                      pil转cv2图片
                      :param img: pil图像, <type 'PIL.JpegImagePlugin.JpegImageFile'>
                      :return: cv2图像, <type 'numpy.ndarray'>
                      """
                      img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
                      return img
                  
                  
                  def bytes_to_cv2(img):
                      """
                      二进制图片转cv2
                      :param img: 二进制图片数据, <type 'bytes'>
                      :return: cv2图像, <type 'numpy.ndarray'>
                      """
                      # 将图片字节码bytes, 转换成一维的numpy数组到缓存中
                      img_buffer_np = np.frombuffer(img, dtype=np.uint8)
                      # 从指定的内存缓存中读取一维numpy数据, 并把数据转换(解码)成图像矩阵格式
                      img_np = cv2.imdecode(img_buffer_np, 1)
                      return img_np
                  
                  
                  def cv2_open(img, flag=None):
                      """
                      统一输出图片格式为cv2图像, <type 'numpy.ndarray'>
                      :param img: <type 'bytes'/'numpy.ndarray'/'str'/'Path'/'PIL.JpegImagePlugin.JpegImageFile'>
                      :param flag: 颜色空间转换类型, default: None
                          eg: cv2.COLOR_BGR2GRAY(灰度图)
                      :return: cv2图像, <numpy.ndarray>
                      """
                      if isinstance(img, bytes):
                          img = bytes_to_cv2(img)
                      elif isinstance(img, (str, Path)):
                          img = cv2.imread(str(img))
                      elif isinstance(img, np.ndarray):
                          img = img
                      elif isinstance(img, PIL.Image.Image):
                          img = pil_to_cv2(img)
                      else:
                          raise ValueError(f'输入的图片类型无法解析: {type(img)}')
                      if flag is not None:
                          img = cv2.cvtColor(img, flag)
                      return img
                  
                  
                  def get_distance(bg, tp, im_show=False, save_path=None):
                      """
                      :param bg: 背景图路径或 Path 对象或图片二进制
                                 eg: 'assets/bg.jpg'、Path('assets/bg.jpg')
                      :param tp: 缺口图路径或 Path 对象或图片二进制
                                 eg: 'assets/tp.jpg'、Path('assets/tp.jpg')
                      :param im_show: 是否显示结果, <type 'bool'>; default: False
                      :param save_path: 保存路径, <type 'str'/'Path'>; default: None
                      :return: 缺口位置
                      """
                      # 读取图片
                      bg_img = cv2_open(bg)
                      tp_gray = cv2_open(tp, flag=cv2.COLOR_BGR2GRAY)
                  
                      # 金字塔均值漂移
                      bg_shift = cv2.pyrMeanShiftFiltering(bg_img, 5, 50)
                  
                      # 边缘检测
                      tp_gray = cv2.Canny(tp_gray, 255, 255)
                      bg_gray = cv2.Canny(bg_shift, 255, 255)
                  
                      # 目标匹配
                      result = cv2.matchTemplate(bg_gray, tp_gray, cv2.TM_CCOEFF_NORMED)
                      # 解析匹配结果
                      min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
                  
                      distance = max_loc[0]
                      if save_path or im_show:
                          # 需要绘制的方框高度和宽度
                          tp_height, tp_width = tp_gray.shape[:2]
                          # 矩形左上角点位置
                          x, y = max_loc
                          # 矩形右下角点位置
                          _x, _y = x + tp_width, y + tp_height
                          # 绘制矩形
                          bg_img = cv2_open(bg)
                          cv2.rectangle(bg_img, (x, y), (_x, _y), (0, 0, 255), 2)
                          # 保存缺口识别结果到背景图
                          if save_path:
                              save_path = Path(save_path).resolve()
                              save_path = save_path.parent / f"{save_path.stem}{save_path.suffix}"
                              save_path = save_path.__str__()
                              cv2.imwrite(save_path, bg_img)
                          # 显示缺口识别结果
                          if im_show:
                              imshow(bg_img)
                      return distance
                  
                  
                  # with open("./img/slide_bg.jpg", "rb") as f:
                  #     bg_img = f.read()
                  # with open("./img/slide_slice.png", "rb") as f:
                  #     slice_img = f.read()
                  # distance = get_distance(bg_img, slice_img)
                  # print(distance)
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8
                  • 9
                  • 10
                  • 11
                  • 12
                  • 13
                  • 14
                  • 15
                  • 16
                  • 17
                  • 18
                  • 19
                  • 20
                  • 21
                  • 22
                  • 23
                  • 24
                  • 25
                  • 26
                  • 27
                  • 28
                  • 29
                  • 30
                  • 31
                  • 32
                  • 33
                  • 34
                  • 35
                  • 36
                  • 37
                  • 38
                  • 39
                  • 40
                  • 41
                  • 42
                  • 43
                  • 44
                  • 45
                  • 46
                  • 47
                  • 48
                  • 49
                  • 50
                  • 51
                  • 52
                  • 53
                  • 54
                  • 55
                  • 56
                  • 57
                  • 58
                  • 59
                  • 60
                  • 61
                  • 62
                  • 63
                  • 64
                  • 65
                  • 66
                  • 67
                  • 68
                  • 69
                  • 70
                  • 71
                  • 72
                  • 73
                  • 74
                  • 75
                  • 76
                  • 77
                  • 78
                  • 79
                  • 80
                  • 81
                  • 82
                  • 83
                  • 84
                  • 85
                  • 86
                  • 87
                  • 88
                  • 89
                  • 90
                  • 91
                  • 92
                  • 93
                  • 94
                  • 95
                  • 96
                  • 97
                  • 98
                  • 99
                  • 100
                  • 101
                  • 102
                  • 103
                  • 104
                  • 105
                  • 106
                  • 107
                  • 108
                  • 109
                  • 110
                  • 111
                  • 112
                  • 113
                  • 114
                  • 115
                  • 116

                  关于轨迹的生成

                  轨迹主要是针对滑块的,可以利用贝塞尔曲线、缓动函数等,来生成正确的轨迹,基于贝塞尔曲线的可以参考:https://github.com/2833844911/gurs ,吾爱上也有个大佬利用 tanharctan 函数整合生成轨迹的:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1162979

                  基于缓动函数的可以参考以下代码(来源于互联网收集):

                  import random
                  
                  
                  def __ease_out_expo(sep):
                      """
                      缓动函数 easeOutExpo
                      参考:https://easings.net/zh-cn#easeOutExpo
                      """
                      if sep == 1:
                          return 1
                      else:
                          return 1 - pow(2, -10 * sep)
                  
                  
                  def get_slide_track(distance):
                      """
                      根据滑动距离生成滑动轨迹
                      :param distance: 需要滑动的距离
                      :return: 滑动轨迹<type 'list'>: [[x,y,t], ...]
                          x: 已滑动的横向距离
                          y: 已滑动的纵向距离, 除起点外, 均为0
                          t: 滑动过程消耗的时间, 单位: 毫秒
                      """
                  
                      if not isinstance(distance, int) or distance < 0:
                          raise ValueError(f"distance类型必须是大于等于0的整数: distance: {distance}, type: {type(distance)}")
                      # 初始化轨迹列表
                      slide_track = [
                          [random.randint(-50, -10), random.randint(-50, -10), 0],
                          [0, 0, 0],
                      ]
                      # 共记录count次滑块位置信息
                      count = 30 + int(distance / 2)
                      # 初始化滑动时间
                      t = random.randint(50, 100)
                      # 记录上一次滑动的距离
                      _x = 0
                      _y = 0
                      for i in range(count):
                          # 已滑动的横向距离
                          x = round(__ease_out_expo(i / count) * distance)
                          # 滑动过程消耗的时间
                          t += random.randint(10, 20)
                          if x == _x:
                              continue
                          slide_track.append([x, _y, t])
                          _x = x
                      slide_track.append(slide_track[-1])
                      return slide_track
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8
                  • 9
                  • 10
                  • 11
                  • 12
                  • 13
                  • 14
                  • 15
                  • 16
                  • 17
                  • 18
                  • 19
                  • 20
                  • 21
                  • 22
                  • 23
                  • 24
                  • 25
                  • 26
                  • 27
                  • 28
                  • 29
                  • 30
                  • 31
                  • 32
                  • 33
                  • 34
                  • 35
                  • 36
                  • 37
                  • 38
                  • 39
                  • 40
                  • 41
                  • 42
                  • 43
                  • 44
                  • 45
                  • 46
                  • 47
                  • 48

                  其他可能的报错

                  // challenge 不对
                  geetest_xxxxxxxxxxxxx({"status": "error", "error": "illegal challenge", "user_error": "网络不给力", "error_code": "error_23"})
                  // w 生成不对
                  geetest_xxxxxxxxxxxxx({"status": "error", "error": "param decrypt error", "user_error": "网络不给力", "error_code": "error_03"})
                  // 滑动验证没有轨迹
                  geetest_xxxxxxxxxxxxx({"status": "error", "error": "not proof", "user_error": "网络不给力", "error_code": "error_21"})
                  // 轨迹、缺口距离、参数问题
                  geetest_xxxxxxxxxxxxx({"success": 0, "message": "fail"})
                  geetest_xxxxxxxxxxxxx({"success": 0, "message": "forbidden"})
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8

                  声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/90145
                  推荐阅读
                  相关标签
                    

                  闽ICP备14008679号