【CAPTCHA】手把手教你应用&绕过音频验证码

目录

概念

实现

方式一:开源库

方式二:商用api

绕过

干扰过大:失败

无干扰下:成功 


概念

音频验证码是一种用于验证用户身份的安全机制,通常在无法使用或不方便使用图形验证码的情况下使用。与传统的视觉验证码不同,音频验证码通过生成并播放一段语音,用户需要听取这段语音并输入相应的数字或字母序列以完成验证。

音频验证码的主要作用是帮助那些视力有障碍的用户或在环境光线较差时使用设备的用户完成验证。它通过合成语音将验证码内容读出来,例如,系统可能会读出“3, B, 7, Z”等字符,用户需要在输入框中正确输入这些字符以通过验证。

特点与优势:

  1. 无障碍性:为视障人士提供了友好的用户体验,使得他们能够顺利通过验证。
  2. 防止恶意攻击:音频验证码增加了恶意软件自动破解的难度,因为它需要理解和处理语音内容。
  3. 多语言支持:可以根据用户的语言习惯生成相应的语音验证码。

缺点:

  1. 环境影响:在嘈杂的环境中,用户可能难以听清验证码内容。
  2. 语音识别:对某些用户而言,尤其是非母语用户,理解语音内容可能存在困难。
  3. 耗时较长:相比图形验证码,用户需要花更多时间来听取和输入验证码。

音频验证码常用于增强安全性的网站、应用程序或设备,特别是当用户需要选择一种比视觉验证码更适合的验证方式时。

实现

方式一:开源库

github开源项目

https://github.com/lepture/captcha

示例,基于flask

from io import BytesIO
from flask import Flask, Response
from captcha.audio import AudioCaptcha

audio = AudioCaptcha()
app = Flask(__name__)


@app.route("/captcha")
def captcha_view():
    # add your own logic to generate the code
    code = "4321"
    data = audio.generate(code)
    return Response(BytesIO(data), mimetype="audio/wav")

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=1337)

只能生成英文语音,噪音较多,人耳都需要较仔细去识别

 

方式二:商用api

调用商用api

比如阿里云、腾讯云,但这些要企业认证,比较麻烦

短信宝

👆这里推荐一个个人用户即可调用的api

代码里的验证码长度要求为6位

import urllib
import urllib.request
import hashlib

def md5(str):
    import hashlib
    m = hashlib.md5()
    m.update(str.encode("utf8"))
    return m.hexdigest()

statusStr = {
    '0': '语音发送成功',
    '-1': '参数不全',
    '-2': '服务器空间不支持,请确认支持curl或者fsocket,联系您的空间商解决或者更换空间',
    '30': '密码错误',
    '40': '账号不存在',
    '41': '余额不足',
    '42': '账户已过期',
    '43': 'IP地址限制',
    '50': '内容含有敏感词'
}

smsapi = "http://api.smsbao.com/voice?"
# 短信平台账号
user = 'Z3r4yuu'
# 短信平台密码
password = md5('mypassword')
content="123456"
# 要发送短信的手机号码
phone='13141314520'

data = urllib.parse.urlencode({'u': user, 'p': password, 'm': phone, 'c': content})
send_url = smsapi + data
print(send_url)
response = urllib.request.urlopen(send_url)
the_page = response.read().decode('utf-8')
print(statusStr[the_page])

合成语音为中文,语音清晰,没有识别难度

但有比较严重的次数限制

绕过

经过调研你会发现基本上有两种语音验证码,一种是非常简单的,没有加入很多噪音,语音也很清晰。另外一种是非常复杂的,故意加了很多噪音,连真人很难听出来。这种验证码里面估计加了很多嘶嘶的噪声,并且用很多人声作为干扰。

我主要写了如何解决前一种验证码,虽然我为了破解后一种复杂的验证码也做了很多努力,但是实在是太困难了,即使是人类对于它的识别率也很低。

要用魔法战胜魔法,调用2captcha提供的api接口(语音识别)去识别语音对应的文字

https://2captcha.com/api-docs/audio

https://2captcha.com/blog/audio-captcha-solver

注意其只能识别mp3格式的外文音频 

干扰过大:失败

这里用python去实现,先起一个语音合成的Flask服务器,再写一个调用2captcha api的绕过脚本

用上面的第一种方式生成音频验证码,加了个wav格式转mp3格式的预处理

Flask服务

from io import BytesIO
from flask import Flask, Response, send_file
from captcha.audio import AudioCaptcha
from pydub import AudioSegment

audio = AudioCaptcha()
app = Flask(__name__)

@app.route("/captcha")
def captcha_view():
    # 生成验证码
    code = "4321"
    data = audio.generate(code)
    
    # 将生成的 WAV 文件转成 MP3
    wav_audio = AudioSegment.from_file(BytesIO(data), format="wav")
    mp3_audio = BytesIO()
    wav_audio.export(mp3_audio, format="mp3")
    mp3_audio.seek(0)
    
    return send_file(mp3_audio, mimetype="audio/mp3")

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=1337)

破解音频验证码的脚本(本质上是语音识别)

import base64
import requests
import sys
import time

# 定义 2Captcha API 密钥
API_KEY = '1df933fcb705f16ff6aa295d687dc1a6'

# 获取生成的语音验证码文件
response = requests.get('http://124.222.136.33:1337/captcha')
if response.status_code != 200:
    sys.exit("Failed to retrieve the captcha audio.")

# 将音频文件保存到本地
audio_file_path = 'captcha.mp3'
with open(audio_file_path, 'wb') as f:
    f.write(response.content)

# 将音频文件进行 Base64 编码
with open(audio_file_path, 'rb') as audio_file:
    audio_base64 = base64.b64encode(audio_file.read()).decode('utf-8')

# 构建 POST 请求的数据
post_data = {
    "key": API_KEY,
    "method": "audio",
    "body": audio_base64,
    "lang": "en",
    "json": 1
}

# 发送 POST 请求到 2Captcha 的 API
post_response = requests.post('https://2captcha.com/in.php', data=post_data)
if post_response.status_code != 200:
    sys.exit("Failed to send audio captcha for solving.")

# 解析 POST 请求的响应
try:
    post_result = post_response.json()
    if post_result['status'] != 1:
        sys.exit(f"Error in response: {post_result['request']}")
except ValueError:
    sys.exit(f"Error decoding POST response as JSON: {post_response.text}")

# 提取请求 ID
request_id = post_result['request']

# 等待几秒以确保验证码已经被处理
time.sleep(20)  # 可根据实际情况调整等待时间

# 构建 GET 请求的 URL
get_url = f'https://2captcha.com/res.php?key={API_KEY}&action=get&id={request_id}'

# 发送 GET 请求获取验证码结果
while True:
    get_response = requests.get(get_url)
    
    # 打印响应内容以进行调试
    print(f"Response status code: {get_response.status_code}")
    print(f"Response content: {get_response.text}")
    
    # 手动解析返回的内容
    response_text = get_response.text
    if "OK" in response_text:
        answer = response_text.split('|')[1].strip()
        print(f'Result: {answer}')
        sys.exit(0)
    elif "CAPCHA_NOT_READY" in response_text:
        # 如果验证码尚未准备好,等待几秒再重试
        time.sleep(5)
    else:
        sys.exit(f"Error in response: {response_text}")

可以看到因为生成的音频验证码干扰过大,最终识别效果较差(无意义的文字)😭

无干扰下:成功 

既然干扰大的验证码没法识别,那么就换极致简单的音源

我们用科大讯飞的语音合成api来实现

语音合成(流式版)WebAPI 文档 | 讯飞开放平台文档中心

为方便测试,这里写死了生成的语音为'1 2 3 4'的英文,经测试,人声十分清晰且无干扰

from flask import Flask, jsonify, send_file
import websocket
import datetime
import hashlib
import hmac
import json
from urllib.parse import urlencode
from time import mktime
import _thread as thread
import ssl
import logging
import io
import wave
from pydub import AudioSegment
import base64
from wsgiref.handlers import format_date_time

app = Flask(__name__)

STATUS_FIRST_FRAME = 0
STATUS_CONTINUE_FRAME = 1
STATUS_LAST_FRAME = 2

# 设置日志
logging.basicConfig(level=logging.INFO)

# 用于存储音频数据的全局变量
audio_data = b''

def pcm2mp3(pcm_data, channels=1, bits=16, sample_rate=16000):
    # 先将PCM数据转换为WAV格式
    wav_io = io.BytesIO()
    with wave.open(wav_io, 'wb') as wavfile:
        wavfile.setnchannels(channels)
        wavfile.setsampwidth(bits // 8)
        wavfile.setframerate(sample_rate)
        wavfile.writeframes(pcm_data)
    wav_io.seek(0)  # 确保文件指针指向文件的开头

    # 使用pydub将WAV转换为MP3
    audio = AudioSegment.from_wav(wav_io)
    mp3_io = io.BytesIO()
    audio.export(mp3_io, format="mp3")
    mp3_io.seek(0)  # 确保文件指针指向文件的开头
    return mp3_io

class Ws_Param:
    def __init__(self, APPID, APIKey, APISecret, Text):
        self.APPID = APPID
        self.APIKey = APIKey
        self.APISecret = APISecret
        self.Text = Text

        self.CommonArgs = {"app_id": self.APPID}
        self.BusinessArgs = {
            "aue": "raw",
            "auf": "audio/L16;rate=16000",
            "vcn": "xiaoyan",
            "tte": "utf8"
        }
        self.Data = {
            "status": 2,
            "text": str(base64.b64encode(self.Text.encode('utf-8')), "UTF8")
        }

    def create_url(self):
        url = 'wss://tts-api.xfyun.cn/v2/tts'
        now = datetime.datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        signature_origin = f"host: tts-api.xfyun.cn\ndate: {date}\nGET /v2/tts HTTP/1.1"
        signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'), hashlib.sha256).digest()
        signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')

        authorization_origin = f'api_key="{self.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha}"'
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode('utf-8')

        v = {
            "authorization": authorization,
            "date": date,
            "host": "tts-api.xfyun.cn"
        }
        return f"{url}?{urlencode(v)}"

def on_message(ws, message):
    global audio_data
    data = json.loads(message)
    if 'data' in data and 'audio' in data['data']:
        audio_chunk = base64.b64decode(data['data']['audio'])
        logging.info(f"Received audio chunk of length: {len(audio_chunk)}")
        audio_data += audio_chunk
    if data.get('data', {}).get('status') == 2:
        logging.info("Last audio chunk received, closing WebSocket")
        ws.close()

def on_error(ws, error):
    logging.error(f"WebSocket Error: {error}")

def on_close(ws, close_status_code, close_msg):
    logging.info(f"WebSocket closed with status: {close_status_code} and message: {close_msg}")

def on_open(ws):
    def run(*args):
        try:
            d = {
                "common": wsParam.CommonArgs,
                "business": wsParam.BusinessArgs,
                "data": wsParam.Data,
            }
            ws.send(json.dumps(d))
        except Exception as e:
            logging.error(f"Error in WebSocket run: {e}")

    thread.start_new_thread(run, ())


@app.route('/captcha', methods=['GET'])
def synthesize():
    try:
        global audio_data
        audio_data = b''  # 重置音频数据缓冲区

        # 固定文本
        text = "one two three four"

        # 初始化 WebSocket 参数
        global wsParam
        wsParam = Ws_Param(
            APPID='5ba18639',
            APISecret='YzY3YjNjZWM2MzY0MWU5OGMxYWE2M2Ri',
            APIKey='7d8b9c713a0d43796e5ed8b304073ac0',
            Text=text
        )
        
        wsUrl = wsParam.create_url()
        ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
        ws.on_open = on_open

        try:
            ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}, ping_timeout=10, ping_interval=11)
        except Exception as e:
            logging.error(f"WebSocket error: {e}")
            response_data = {'code': 500, 'msg': "Failed to generate audio data due to WebSocket error"}
            return jsonify(response_data), 500

        if not audio_data:
            logging.info("No audio data was generated.")
            response_data = {'code': 500, 'msg': "Failed to generate audio data"}
            return jsonify(response_data), 500

        # Convert PCM to MP3 format and return the file
        mp3_data = pcm2mp3(audio_data)
        return send_file(mp3_data, mimetype="audio/mp3", as_attachment=True, download_name="synthesized_audio.mp3")

    except Exception as e:
        logging.error(f"Error occurred: {e}")
        return jsonify({'code': 500, 'msg': "Internal server error"}), 500


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=1337)

bypass的脚本还是一样不变

最终成功绕过

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Captcha验证码是一种用于验证用户身份的技术。它通常以图像形式出现,要求用户在输入框中输入由图像中显示的文字或数字。这种验证码的目的是区分机器和人类用户,以防止自动化程序或恶意软件的攻击。 在您提供的引用内容中,描述了一种实现常规输入验证码的方式,其中右边显示验证码图片,用户可以点击刷新验证码,左边的输入框用于用户输入验证码。这种方式是一种常见的验证码实现方式。 提到了使用深度学习来识别captcha验证码的项目实践。该项目使用Keras库构建了一个深度卷积神经网络,以识别captcha验证码。这种方法利用神经网络的模式识别能力,通过训练来学习captcha验证码的特征,并能够在实际应用中自动识别验证码。 提到了登录验证中常见的一种方式,即输入式验证码。这种验证码要求用户在输入框中手动输入由图像中显示的文字或数字,以验证用户身份。 综上所述,captcha验证码是一种用于验证用户身份的技术,通常以图像形式出现,并要求用户在输入框中输入由图像中显示的文字或数字。常规的输入验证码方式是在界面中显示验证码图片,用户点击刷新验证码,然后在相应输入框中输入验证码。深度学习可以用于识别captcha验证码,通过训练神经网络来自动识别验证码。输入式验证码是登录验证中常见的一种方式,要求用户手动输入验证码以完成验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值