DevOps và Mở Rộng

AWS Lambda + CaptchaAI: Giải mã CAPTCHA không cần máy chủ

Chạy một EC2 instance 24/7 chỉ để chờ vài trăm request giải CAPTCHA mỗi ngày là lãng phí — server rảnh phần lớn thời gian nhưng vẫn tính tiền theo giờ. AWS Lambda giải quyết đúng vấn đề này: hàm chỉ chạy khi có request gọi tới, tự động scale khi traffic tăng đột biến, và tích hợp thẳng với API Gateway, SQS hoặc Step Functions mà không cần quản lý server nào. Bài này đi qua toàn bộ luồng triển khai: viết Lambda handler gọi CaptchaAI, bảo mật API key bằng Secrets Manager, định nghĩa hạ tầng bằng SAM template, deploy, và xử lý hàng loạt qua SQS.


Viết Lambda handler gọi CaptchaAI

Handler dưới đây nhận request qua API Gateway (hoặc gọi trực tiếp), gửi task tới CaptchaAI theo method truyền vào (mặc định userrecaptcha), rồi polling res.php đến khi có token hoặc hết timeout 90 giây. Toàn bộ chỉ dùng urllib — không cần cài thêm dependency nào:

# lambda_function.py
import json
import os
import time
import urllib.request
import urllib.parse

def lambda_handler(event, context):
    """AWS Lambda handler for CaptchaAI solving."""
    api_key = os.environ["CAPTCHAAI_KEY"]

    # Parse input
    body = json.loads(event.get("body", "{}")) if isinstance(event.get("body"), str) else event

    method = body.get("method", "userrecaptcha")
    params = body.get("params", {})

    try:
        token = solve_captcha(api_key, method, params)
        return {
            "statusCode": 200,
            "body": json.dumps({"token": token}),
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "body": json.dumps({"error": str(e)}),
        }

def solve_captcha(api_key, method, params, timeout=90):
    """Solve CAPTCHA using CaptchaAI API."""
    # Submit task
    submit_data = urllib.parse.urlencode({
        "key": api_key,
        "method": method,
        "json": 1,
        **params,
    }).encode()

    req = urllib.request.Request(
        "https://ocr.captchaai.com/in.php",
        data=submit_data,
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        result = json.loads(resp.read())

    if result.get("status") != 1:
        raise RuntimeError(f"Submit error: {result.get('request')}")

    task_id = result["request"]

    # Poll for result
    start = time.time()
    while time.time() - start < timeout:
        time.sleep(5)
        poll_url = (
            f"https://ocr.captchaai.com/res.php"
            f"?key={api_key}&action=get&id={task_id}&json=1"
        )
        with urllib.request.urlopen(poll_url, timeout=15) as resp:
            data = json.loads(resp.read())

        if data["request"] != "CAPCHA_NOT_READY":
            if data.get("status") == 1:
                return data["request"]
            raise RuntimeError(f"Solve error: {data['request']}")

    raise TimeoutError("Solve timeout")

Hàm solve_captcha() gói gọn cả hai bước: gửi task tới in.php, rồi polling res.php mỗi 5 giây — đúng luồng gửi → nhận task ID → polling → dùng token mà CaptchaAI dùng cho mọi loại CAPTCHA.


Bảo mật API key bằng Secrets Manager

Đừng hardcode API key thẳng vào code hay để plaintext trong biến môi trường của template. Hàm dưới đây lấy key trực tiếp từ AWS Secrets Manager tại runtime:

import json
import boto3

def get_api_key():
    """Retrieve CaptchaAI key from AWS Secrets Manager."""
    client = boto3.client("secretsmanager")
    response = client.get_secret_value(SecretId="captchaai/api-key")
    secret = json.loads(response["SecretString"])
    return secret["api_key"]

Tạo secret trước khi deploy:

aws secretsmanager create-secret \
  --name captchaai/api-key \
  --secret-string '{"api_key":"YOUR_API_KEY"}'

Định nghĩa hạ tầng bằng SAM template

SAM template khai báo function, timeout, memory và endpoint API Gateway trong một file duy nhất. Biến CAPTCHAAI_KEY được resolve trực tiếp từ Secrets Manager lúc deploy — key không bao giờ nằm trong template hay lộ ra trong log CloudFormation:

# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 120
    MemorySize: 256
    Runtime: python3.11

Resources:
  CaptchaSolverFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: lambda_function.lambda_handler
      Environment:
        Variables:
          CAPTCHAAI_KEY: !Sub "{{resolve:secretsmanager:captchaai/api-key:SecretString:api_key}}"
      Events:
        SolveApi:
          Type: Api
          Properties:
            Path: /solve
            Method: post
      Policies:

        - AWSSecretsManagerGetSecretValuePolicy:
            SecretArn: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:captchaai/api-key-*"

Outputs:
  SolveApiUrl:
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/solve"

Build và deploy

Build package, deploy có hướng dẫn tương tác (--guided hỏi tên stack, region, và có tạo IAM role hay không), rồi test bằng curl:

# Build and deploy
sam build
sam deploy --guided

# Test
curl -X POST https://YOUR_API_ID.execute-api.us-east-1.amazonaws.com/Prod/solve \
  -H "Content-Type: application/json" \
  -d '{
    "method": "userrecaptcha",
    "params": {
      "googlekey": "SITE_KEY",
      "pageurl": "https://example.com"
    }
  }'

Xử lý hàng loạt CAPTCHA qua SQS

Khi cần giải hàng trăm CAPTCHA cùng lúc — ví dụ một agency ở TP.HCM chạy pipeline theo dõi giá sản phẩm công khai trên Shopee hoặc Tiki mỗi vài phút cho mục đích nghiên cứu thị trường — gọi API Gateway theo từng request một là không hiệu quả. Đẩy task vào hàng đợi SQS rồi để Lambda xử lý theo batch tận dụng được concurrency mặc định (1.000) mà không cần tự dựng worker pool:

import json
import os
import time
import urllib.request
import urllib.parse

def sqs_handler(event, context):
    """Process CAPTCHA tasks from SQS queue."""
    api_key = os.environ["CAPTCHAAI_KEY"]
    results = []

    for record in event["Records"]:
        task = json.loads(record["body"])
        try:
            token = solve_captcha(
                api_key,
                task["method"],
                task["params"],
            )
            results.append({
                "task_id": task.get("id"),
                "status": "success",
                "token": token[:50],
            })
        except Exception as e:
            results.append({
                "task_id": task.get("id"),
                "status": "error",
                "error": str(e),
            })

    return {"results": results}

Những điều cần biết khi chạy CaptchaAI trên Lambda

Vài con số thực tế khi vận hành pattern này trong production:

Yếu tố Giá trị
Timeout tối đa 15 phút (đặt 2 phút là đủ cho hầu hết CAPTCHA)
Memory 256 MB là đủ (không xử lý nặng)
Concurrency Mặc định 1.000 đồng thời (xin tăng nếu cần)
Cold start ~500ms với Python (không đáng kể so với thời gian giải)
Chi phí ~$0,0001 mỗi lần gọi (chỉ tính phí compute)
Dependency Dùng urllib (built-in) để tránh Lambda layer

Khắc phục lỗi thường gặp

Bốn lỗi hay gặp nhất khi mới chuyển pattern này sang Lambda:

Vấn đề Nguyên nhân Cách xử lý
Hàm bị timeout Timeout Lambda ngắn hơn thời gian giải CAPTCHA Đặt timeout 120 giây trở lên
Bị từ chối quyền đọc secret Thiếu IAM policy Thêm policy đọc SecretsManager
Cold start làm tăng độ trễ Invocation không thường xuyên Bật provisioned concurrency
Lỗi import requests Thư viện không được đóng gói sẵn trong Lambda Dùng urllib.request (built-in) hoặc thêm layer

Câu hỏi thường gặp

AWS Lambda có rẻ hơn chạy một server 24/7 để gọi CaptchaAI không?

Có, với khối lượng dưới vài nghìn request mỗi ngày. Ở mức ~$0,0001 mỗi lần gọi (256 MB, 60 giây), chi phí Lambda gần như không đáng kể so với phí API CaptchaAI, và bạn không trả tiền cho thời gian server rảnh — khác hẳn EC2 hay VPS chạy cố định cả tháng.

Timeout 15 phút của Lambda có đủ cho reCAPTCHA Enterprise không?

Thừa. Phần lớn CAPTCHA giải xong trong 10–60 giây, nên đặt timeout Lambda 120 giây là đủ cho hầu hết trường hợp. Với reCAPTCHA Enterprise hoặc các loại phức tạp hơn, nâng lên 180 giây để có biên an toàn.

Nên chọn region AWS nào để giảm độ trễ khi gọi CaptchaAI từ Việt Nam?

Độ trễ mạng tới ocr.captchaai.com thường chỉ chiếm phần nhỏ so với thời gian giải CAPTCHA (10–60 giây), nên khác biệt giữa ap-southeast-1 (Singapore) và các region khác thường không đáng kể trong thực tế. Chọn region gần các service AWS khác mà pipeline của bạn dùng (SQS, API Gateway) quan trọng hơn việc chọn region gần người dùng cuối.

Có thể dùng Lambda + SQS để giải CAPTCHA hàng loạt khi theo dõi giá trên Shopee hoặc Tiki không?

Có. Đẩy từng task cần giải vào hàng đợi SQS, để Lambda xử lý theo batch như ví dụ sqs_handler ở trên. Cách này scale tốt hơn gọi API Gateway tuần tự và tận dụng được concurrency mặc định của Lambda — phù hợp cho pipeline theo dõi giá công khai chạy định kỳ.

Provisioned concurrency có đáng bật cho pipeline giải CAPTCHA không?

Chỉ khi traffic của bạn ổn định và nhạy cảm với độ trễ. Provisioned concurrency giữ sẵn một số instance "ấm" nên tránh được cold start ~500ms, nhưng tính phí ngay cả khi không có request. Với workload chạy theo lịch (batch định kỳ) hoặc traffic thấp, cold start mặc định thường chấp nhận được vì không đáng kể so với 10–60 giây giải CAPTCHA.


Bài viết liên quan


Bỏ hẳn việc quản lý server — lấy API key CaptchaAI và deploy hàm Lambda giải CAPTCHA đầu tiên ngay hôm nay.

Os comentários estão desativados para este artigo.