通过js完成滑动验证码的校验

本文详细解析了58同城网站滑动验证码的工作原理,包括如何获取验证码图片、伪造滑动轨迹、加密数据及服务器端校验流程。并提供了使用Node.js和Python实现的代码示例。
部署运行你感兴趣的模型镜像

背景

对于滑动验证码校验的原理,如果我理解的没错的话,大致如下:我们在浏览器调出验证码,拖动滑块,同时页面开始记录鼠标的坐标,当松开鼠标的时候,页面完成对坐标数据的加密,携带该数据发送请求,在服务器端进行校验。要想用js伪造该过程,关键是获取滑块的坐标和对坐标进行加密的算法,下面仍然以58同城二手车为例,学习一下该过程。

大致过程

经过反复操作,发现要想获取验证码图片,需要先对https://callback.58.com/antibot/codev2/getsession.do发送post请求,获取sessionid,然后携带sessionid访问https://verifycode.58.com/captcha/getV3?&showType=win&sessionId={session_id},从而获取验证码图片的连接,同时返回一个responseid;之后伪造滑动轨迹,经过算法加密后,携带加密数据,sessionid和responseid访问https://verifycode.58.com/captcha/checkV3?callback={callback}&responseId={response_id}&sessionId={session_id}&data={data}完成校验。另外,仍然用上篇博客模板匹配的方法获取滑块坐标,不过查找的子图改为了下图(黑色周围是有一圈白色的),改了之后成功率提高很多。
在这里插入图片描述

代码

滑块轨迹的加密实际上就是一个aes加密,经过验证,58并未对aes加密算法进行修改,直接使用node.js中的crypto-js就可以,代码如下:

// import CryptoJS from 'crypto-js'
const CryptoJS = require('crypto-js')
function random_num(){
    return Math.floor(Math.random()*25)
}
// 获取滑块的x坐标
var offset = parseInt(process.argv[2])
// 获取sessionid
var responseId = process.argv[3]
// var responseId = "dc7b4e746cc442319944e061e7023f6d"
// var offset = 200
var stmap = 140

var track = [{p: "43,22", t: 1}]
var arg = 22
for(var s=44; s < 43 + offset; s++, stmap=stmap+random_num()){
    if(s%20 == 0){
        arg = arg - 1
        track.push({p:String(s)+','+String(arg), t:stmap})
    }else{
        track.push({p:String(s)+','+String(arg), t:stmap})
    }
};
track.push({p:String(s-1)+','+String(arg), t:stmap+random_num()})
for (var r = track.slice(0, 260), o = "", l = 0; l < r.length; l++)
                    o += r[l].p + "," + r[l].t + "|";


AESEncryption = function(e) {
                var t = CryptoJS.enc.Utf8.parse(responseId.substr(0, 16));
                return CryptoJS.AES.encrypt(e, t, {
                    iv: t,
                    mode: CryptoJS.mode.CBC,
                    padding: CryptoJS.pad.Pkcs7
                }).ciphertext.toString().toUpperCase()
            };
var d = AESEncryption('{"x":"' + (offset) + '","track":"' + o + '","p":"' + [0,0] + '","finger":"' + ("") + '"}');

console.log(d)

callback.js

var now = (new Date).getTime()
var jq = "jQuery" + ("1.10.1" + Math.random()).replace(/\D/g, "");
var claaback = jq + "_" + now++;
console.log(claaback)

python校验代码car_verify.py:

import requests
import time
from PIL import Image
import aircv as ac
import os
import re
import json


def save_captcha():
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
    'accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'cookie': 'id58=c5/nfF8oqage5rsmEt8aAg==; 58home=tj; city=tj; 58tj_uuid=f0606c8f-bfe0-4918-a05c-54792979825d; als=0; wmda_new_uuid=1; wmda_uuid=8d04e2a5433c9b87989e8dd97f0910d7; xxzl_deviceid=hk4tMTLXOoZDMq1OWS%2FbYdQsr1%2F6tJGVp6JnDKghAebk%2BWYpclsNs7e5ckbFgJ6m; wmda_visited_projects=%3B11187958619315%3B1732038237441; myLat=""; myLon=""; mcity=tj; new_uv=5; xxzl_cid=279a85402ac4446f9b35cada3a75d9e8; xzuid=32ea41f7-b931-49f6-bda8-2b622227d207',
    'referer': 'https://callback.58.com/antibot/verifycode?serialId=52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764&code=22&sign=235448960cb8f4d6cd710b06c61dc57a&namespace=usdt_infolist_car&url=https%3A%2F%2Finfo5.58.com%3A443%2Ftj%2Fershouche%2F%3FPGTID%3D0d100000-0001-2e0e-cd43-870ea87150b4%26ClickID%3D8',
    'sec-fetch-dest': 'image'
    }
    headers_f = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
    }

    data = {
        'serialId': '52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764',
        'code': '22',
        'sign': '235448960cb8f4d6cd710b06c61dc57a',
        'url': 'https://info5.58.com:443/tj/ershouche/?PGTID=0d100000-0001-2e0e-cd43-870ea87150b4&ClickID=8',
        'namespace': 'usdt_infolist_car'
    }

    s = requests.session()

    callbacked = os.popen('node callback.js')
    global callback, _
    callback = callbacked.readlines()[0].strip()
    _ = callback.split('_')[1]

    # url_main = 'https://tj.58.com//ershouche/?utm_source=market&spm=u-2d2yxv86y3v43nkddh1.BDPCPZ_BT&PGTID=0d100000-0001-2cb9-a60b-cca97abb456d&ClickID=4'
    # url_f = 'https://callback.58.com/antibot/verifycode?serialId=52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764&code=22&sign=235448960cb8f4d6cd710b06c61dc57a&namespace=usdt_infolist_car&url=https%3A%2F%2Finfo5.58.com%3A443%2Ftj%2Fershouche%2F%3FPGTID%3D0d100000-0001-2e0e-cd43-870ea87150b4%26ClickID%3D2'
    # resp_f = s.get(url_f,headers=headers_f)
    # print(resp_f)

    # 获取sessionid
    url_session = f'https://callback.58.com/antibot/codev2/getsession.do?{time.time()}'
    resp_session = s.post(url_session, headers=headers_f, data=data).json()

    session_id = resp_session['data']['sessionId']
    url_pic = f'https://verifycode.58.com/captcha/getV3?callback={callback}&_={_}&showType=win&sessionId={session_id}'
    resp = s.get(url_pic, headers=headers).text
    print(resp)
    # 获取responseid和验证码图片
    resp = json.loads(re.findall(r'jQuery\d+.\d+\((.*?)\)', resp, re.S)[0])
    response_id = resp['data']['responseId']
    url_bgImg = 'https://verifycode.58.com' + str(resp['data']['bgImgUrl'])
    pic = s.get(url_bgImg).content
    with open('./imgs/1.png', 'wb') as f:
        f.write(pic)
    return s, session_id, response_id
    
# 处理图片,进行灰度转换和二值化处理
def handle_img(threshold=140):
    img = Image.open('./imgs/1.png')
    img = img.resize((300,169),Image.ANTIALIAS)
    img = img.convert('L')
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    img = img.point(table, '1')
    img.save('./imgs/handled.png')

def crop_img():
    img = Image.open('./imgs/small.png')
    box = (2,2,8,14)
    small = img.crop(box)
    small.save('./imgs/croped.png')

# 模板匹配,找到滑块坐标
def match_template(confidencevalue=0.6):
    croped = ac.imread('./imgs/croped.png')
    big = ac.imread('./imgs/handled.png')
    match_result = ac.find_template(big, croped, confidencevalue)
    if match_result is not None:
            match_result['shape'] = (big.shape[1], big.shape[0])  
    return match_result['result'][0]

# 获取滑动轨迹加密结果
def get_data(loc, response_id):
    ret = os.popen(f'node car_verify.js {loc} {response_id}')
    data = ret.readlines()[0].strip()
    return data

# 发送验证码校验请求
def check(s, response_id, session_id, data):
    # proxy = {'http':'http://127.0.0.1:8888','https':'http://127.0.0.1:8888'}
    url = f'https://verifycode.58.com/captcha/checkV3?callback={callback}&responseId={response_id}&sessionId={session_id}&data={data}&_={_}'
    headers_check = {'referer': 'https://callback.58.com/antibot/verifycode?serialId=52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764&code=22&sign=235448960cb8f4d6cd710b06c61dc57a&namespace=usdt_infolist_car&url=https%3A%2F%2Finfo5.58.com%3A443%2Ftj%2Fershouche%2F%3FPGTID%3D0d100000-0001-2e0e-cd43-870ea87150b4%26ClickID%3D2',
                    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
                    'Cookie': 'id58=c5/nfF8oqage5rsmEt8aAg==; 58home=tj; city=tj; 58tj_uuid=f0606c8f-bfe0-4918-a05c-54792979825d; als=0; wmda_new_uuid=1; wmda_uuid=8d04e2a5433c9b87989e8dd97f0910d7; xxzl_deviceid=hk4tMTLXOoZDMq1OWS%2FbYdQsr1%2F6tJGVp6JnDKghAebk%2BWYpclsNs7e5ckbFgJ6m; wmda_visited_projects=%3B11187958619315%3B1732038237441; myLat=""; myLon=""; mcity=tj; new_uv=9; init_refer=https%253A%252F%252Fcallback.58.com%252Fantibot%252Fverifycode%253FserialId%253D52e2ca88845f157e29a6d26349ef0344_6a059b1477814bb8baf7ee04e2b61764%2526code%253D22%2526sign%253D235448960cb8f4d6cd710b06c61dc57a%2526namespace%253Dusdt_infolist_car%2526url%253Dhttps%25253A%25252F%25252Finfo5.58.com%25253A443%25252Ftj%25252Fershouche%25252F%25253FPGTID%25253D0d100000-0001-2e0e-cd43-870ea87150b4%252526ClickID%25253D2; wmda_session_id_11187958619315=1597305356023-87e5e97d-4b43-b080; new_session=0; xxzl_cid=279a85402ac4446f9b35cada3a75d9e8; xzuid=32ea41f7-b931-49f6-bda8-2b622227d207; spm=u-2d2yxv86y3v43nkddh1.BDPCPZ_BT; utm_source=link',
                    'Accept': '*/*',
                    'Sec-Fetch-Site': 'same-site',
                    'Sec-Fetch-Mode': 'no-cors',
                    'Sec-Fetch-Dest': 'script'
                        }
    resp = s.get(url, headers=headers_check)
    print(resp.text)

# 程序运行的主要逻辑
def verify():
    s, session_id, response_id = save_captcha()
    handle_img()
    loc = match_template() - 10
    print(loc)
    data = get_data(loc, response_id)
    check(s, response_id, session_id, data)

if __name__ == "__main__":
    # crop_img()
    verify()
    

总结

还有一个要注意的地方,通过模板匹配获得的滑块x坐标,要比真正的坐标大10,所以在传递给加密算法的时候要减去10,一开始在这个地方花费了很多时间。此外,58又新增了一种验证码,图片如下,还有待探索。
在这里插入图片描述

结语

以上只做学习交流使用,欢迎各位提出宝贵意见,多多指正交流,另转载请注明出处。

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值