kremle-detect

Защита сайта от Яндекса. Капча с вопросами ЕГЭ.

что это

Python-библиотека для Flask, Django и FastAPI. Автоматически определяет пользователей Яндекс Браузера и переходы с поисковика Яндекс — и показывает им капчу с вопросами из ЕГЭ.

Детекция ведётся по нескольким независимым сигналам:

User-Agent
YaBrowser, YaApp, YandexSearch, YandexStation
Яндекс-боты
YandexBot, YandexMetrika, YandexImages и 20+ ботов
Referer
yandex.ru/com/by/kz, ya.ru, dzen.ru, market.yandex и др.
Client Hints
Заголовок Sec-CH-UA с маркером YaBrowser
JS Fingerprint
navigator.userAgentData, window.yandex, window.Ya

установка

pip install git+https://github.com/RUdolf1517/KremleXYZ-Detect.git
pip install "git+https://github.com/RUdolf1517/KremleXYZ-Detect.git#egg=kremle-detect[flask]"
pip install "git+https://github.com/RUdolf1517/KremleXYZ-Detect.git#egg=kremle-detect[django]"
pip install "git+https://github.com/RUdolf1517/KremleXYZ-Detect.git#egg=kremle-detect[fastapi]"

Обновление до последней версии:

pip install --force-reinstall git+https://github.com/RUdolf1517/KremleXYZ-Detect.git

быстрый старт

from flask import Flask
from kremle_detect.integrations.flask_ext import KremleFlask

app = Flask(__name__)
app.secret_key = 'your-secret'

kremle = KremleFlask(
    app,
    categories=['math', 'physics', 'russian', 'literature'],
    question_count=15,
    max_errors=3,
)

# Redis для продакшена (несколько воркеров)
# from kremle_detect import RedisStorage
# kremle = KremleFlask(app, storage=RedisStorage(url='redis://localhost:6379/0'))
# settings.py
MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ...
    'kremle_detect.integrations.django_ext.KremleDjangoMiddleware',
]

KREMLE_CATEGORIES = ['math', 'physics', 'russian', 'literature']
KREMLE_QUESTION_COUNT = 15
KREMLE_MAX_ERRORS = 3

# urls.py
from kremle_detect.integrations.django_ext import kremle_urls
urlpatterns = [
    path('kremle/', kremle_urls()),
]
from fastapi import FastAPI
from kremle_detect.integrations.fastapi_ext import KremleFastAPI

app = FastAPI()
kremle = KremleFastAPI(
    app,
    categories=['math', 'physics'],
    question_count=10,
    secret='your-secret',
)

React SPA + FastAPI: включи headless=True на бэке — middleware вернёт JSON 403 вместо редиректа.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from kremle_detect.integrations.fastapi_ext import KremleFastAPI

app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"],
                   allow_credentials=True, allow_methods=["*"], allow_headers=["*"])

kremle = KremleFastAPI(app, secret='your-secret', headless=True)

API роуты

GET  /kremle/challenge HTML-страница капчи (или JSON в headless-режиме)
POST /kremle/verify Проверка ответов (JSON)
GET  /kremle/status Статус верификации сессии (JSON)

react-компонент

Пакет kremle-react — готовый React-компонент и хук для SPA. Работает в паре с FastAPI в режиме headless=True.

Установка

# Из репозитория:
npm install /path/to/KremleXYZ-Detect/kremle-react

# Собрать пакет самому:
cd kremle-react && npm install && npm run build

Готовый компонент

import { KremleChallenge } from 'kremle-react'

// Показать капчу при 403 от бэкенда
function App() {
  return (
    <KremleChallenge
      challengeUrl="/kremle/challenge"
      verifyUrl="/kremle/verify"
      onPass={() => window.location.reload()}
      onFail={(r) => console.log(`ошибок: ${r.errors}/${r.total}`)}
    />
  )
}

Хук для кастомного UI

import { useKremle } from 'kremle-react'

function MyCaptcha() {
  const { questions, answers, setAnswer, submit, loading, result } = useKremle()

  if (loading) return <div>Загрузка...</div>

  return (
    <form onSubmit={(e) => { e.preventDefault(); submit() }}>
      {questions.map((q, qi) => (
        <div key={qi}>
          <p>{q.q}</p>
          {q.opts.map((opt, oi) => (
            <label key={oi}>
              <input type="radio" onChange={() => setAnswer(qi, oi)} /> {opt}
            </label>
          ))}
        </div>
      ))}
      <button type="submit">Отправить</button>
    </form>
  )
}

Что возвращает middleware в headless-режиме

error "captcha_required" — нужно показать капчу
reason yandex_browser / yandex_referrer / blacklisted / ...
challenge_url "/kremle/challenge" — куда запросить вопросы (GET → JSON)

cli

После установки доступна команда kremle-detect. Для блоклиста, вайтлиста и логов нужен Redis.

kremle-detect help
диагностика
kremle-detect check-ua "Mozilla/5.0 YaBrowser/24" Проверить User-Agent
kremle-detect check-ref "https://yandex.ru/..." Проверить Referer
kremle-detect questions -c math -n 5 Показать вопросы из банка
блоклист
kremle-detect blacklist list --redis redis://... Все заблокированные IP
kremle-detect blacklist add 1.2.3.4 Заблокировать IP (1 день по умолч.)
kremle-detect blacklist remove 1.2.3.4 Разблокировать IP
вайтлист
kremle-detect whitelist list Все разрешённые IP
kremle-detect whitelist add 1.2.3.4 Добавить в вайтлист
kremle-detect whitelist remove 1.2.3.4 Убрать из вайтлиста
логи
kremle-detect logs -n 100 Последние 100 событий (detect / pass / fail)
kremle-detect logs --no-color Без цвета (для grep / pipe)

Redis URL можно задать через переменную окружения: KREMLE_REDIS_URL=redis://localhost:6379/0

свои вопросы

Встроенный банк — 60 вопросов ЕГЭ по математике, физике, русскому и литературе. Можно добавить свои или полностью заменить.

Шаг 1 — создать файл questions.py

MY_QUESTIONS = [
    {
        "q": "Столица России?",
        "opts": ["Москва", "Санкт-Петербург", "Новосибирск", "Казань"],
        "ans": 0,          # индекс правильного варианта (с нуля)
        "category": "geo", # необязательно
    },
]

Шаг 2 — подключить

from questions import MY_QUESTIONS

# Добавить к встроенным
kremle = KremleFlask(app, extra_questions=MY_QUESTIONS)

# Только свои (без ЕГЭ)
kremle = KremleFlask(app, extra_questions=MY_QUESTIONS, only_extra=True)

Требования к формату

q str — непустая строка, текст вопроса
opts list — минимум 2 варианта ответа
ans int — индекс правильного варианта в opts (с нуля)
category str — необязательно, метка на карточке вопроса

Ошибки формата выбрасываются при старте приложения — неверный вопрос не дойдёт до пользователя.