Проверка ключа API: варианты реализации на Python
Основные подходы к верификации API ключей
Проверка API ключа – обязательный этап для обеспечения безопасности запросов к внешнему или собственному сервису. В зависимости от контекста (публичный API, внутренний микросервис, JWT-аутентификация) используются различные методы. Ниже представлены наиболее популярные техники с примерами кода, а также разбор типичных проблем.
Какое комбинированное решение обеспечивает надёжную проверку без лишних затрат?
Эффективный подход: сначала проверить формат ключа (длину, наличие разрешённых символов), затем выполнить тестовый запрос к целевому API или верифицировать JWT-подпись. Это позволяет быстро отсеять заведомо неверные ключи и избежать лишних сетевых вызовов.
import re
import requests
from typing import Optional
def validate_api_key(api_key: str, service_url: str) -> bool:
"""
Комбинированная проверка: формат + запрос.
"""
# 1. Проверка синтаксиса: ключ длиной 32 символа, только hex
if not re.fullmatch(r'[a-fA-F0-9]{32}', api_key):
return False
# 2. Проверка действительности через HTTP-запрос
headers = {'X-API-Key': api_key}
try:
response = requests.get(service_url + '/verify', headers=headers, timeout=5)
return response.status_code == 200
except requests.RequestException:
return False
Telegram file python (работа с файлами telegram в python)
Такой метод сочетает быстроту локальной проверки с точностью удалённой валидации.
- Регулярное выражение должно строго соответствовать формату конкретного API – при его изменении код перестанет работать.
- HTTP-запрос добавляет задержку; при массовой проверке ключей рекомендуется кэширование (см. examples_adv).
- Некоторые API не предоставляют отдельного эндпоинта для проверки ключа – приходится использовать конечную точку с минимальными правами.
Как проверить, что ключ соответствует ожидаемому шаблону?
Самый простой способ – регулярное выражение или проверка длины. Часто ключи генерируются по определённой схеме (например, 40 символов, буквы и цифры).
import re
def check_format(api_key: str) -> bool:
pattern = r'^[A-Za-z0-9]{40}$'
return bool(re.match(pattern, api_key))
# Примеры
print(check_format('a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0')) # True
print(check_format('short_key')) # False
компас python api (api компас 3d для python)
- Использование неэкранированных символов в регулярке (например, дефис внутри набора символов стоит указывать последним).
- Разные API используют разные алфавиты: некоторые допускают только hex, другие – base64.
- Проверка только формата не гарантирует, что ключ действителен – он может быть отозван или истёк.
Как проверить действительность ключа обращением к API?
Для внешнего сервиса (например, Google Maps, OpenAI) нужно отправить запрос с ключом и проанализировать код ответа. Часто используется GET-запрос с параметром key или заголовком Authorization.
import requests
def test_key_with_api(api_key: str) -> bool:
url = 'https://api.example.com/v1/usage'
headers = {'Authorization': f'Bearer {api_key}'}
resp = requests.get(url, headers=headers)
if resp.status_code == 200:
return True
elif resp.status_code == 401:
return False # ключ неверный или недостаточно прав
else:
# другая ошибка (500, 429) – нельзя однозначно сказать
raise ValueError(f'API вернул {resp.status_code}')
Open api python (openapi (swagger) в python)
- Если ключ правильный, но превышен лимит запросов (429), функция вернёт исключение. Нужно различать ошибки аутентификации и временные сбои.
- Некоторые API не возвращают 401 для неверного ключа, а возвращают 403. Рекомендуется документировать коды ответов.
- Сетевые таймауты – добавлять параметр timeout и обрабатывать исключения ConnectionError.
Как верифицировать JWT токен без обращения к серверу?
Если в качестве ключа используется JWT (JSON Web Token), его можно проверить локально, зная секрет или публичный ключ. Библиотека PyJWT упрощает задачу.
import jwt
def verify_jwt(token: str, secret: str) -> bool:
try:
decoded = jwt.decode(token, secret, algorithms=['HS256'])
# можно проверить срок действия, issuer и т.д.
return True
except jwt.ExpiredSignatureError:
return False # срок истёк
except jwt.InvalidTokenError:
return False # неверная подпись
проверка ключа python (проверка api ключа в python)
- Необходимо знать и правильно указать алгоритм (HS256, RS256) – ошибка приведёт к провалу проверки.
- Секретный ключ нельзя хранить в коде – используйте переменные окружения или хранилище.
- JWT может содержать множество полей (claims) – проверка только подписи недостаточна, нужно валидировать и содержимое.
Как организовать проверку ключа в собственном сервисе (локальная БД)?
Для внутреннего API логично сохранять ключи в базе данных (например, SQLite, PostgreSQL) и проверять при каждом запросе.
import sqlite3
from flask import request, jsonify
def check_key_in_db(api_key: str) -> bool:
conn = sqlite3.connect('keys.db')
cursor = conn.cursor()
cursor.execute('SELECT active FROM api_keys WHERE key_hash = ?', (hash(api_key),))
row = cursor.fetchone()
conn.close()
return row is not None and row[0] == 1
- Хеширование ключей (лучше использовать SHA256) – просто hash() не подходит, так как нестабилен.
- Необходимо синхронизировать БД с процессом выдачи и отзыва ключей.
- При большом числе запросов требуется кэширование (Redis) для быстродействия.
Расширенные примеры проверки ключей
Асинхронная проверка нескольких ключей
При необходимости проверить множество ключей (например, из списка) лучше использовать асинхронные запросы, чтобы не блокировать выполнение. Пример с asyncio и aiohttp:
import asyncio
import aiohttp
async def check_one(session, key, url):
async with session.get(url, headers={'X-Key': key}) as resp:
return key, resp.status
async def check_many(keys, url):
async with aiohttp.ClientSession() as session:
tasks = [check_one(session, k, url) for k in keys]
results = await asyncio.gather(*tasks)
return results
# Пример вызова
keys = ['key1', 'key2', 'key3']
results = asyncio.run(check_many(keys, 'https://httpbin.org/get'))
print(results)
[('key1', 200), ('key2', 200), ('key3', 401)]
Асинхронный подход сокращает общее время ожидания почти до одного максимального запроса.
Кэширование результатов проверки в Redis
Чтобы избежать повторных обращений к API или БД, можно сохранять временный статус ключа в Redis с TTL.
import redis
import requests
r = redis.Redis(host='localhost', port=6379, db=0)
def check_with_cache(api_key):
cached = r.get(api_key)
if cached is not None:
return cached.decode() == 'valid'
# Реальный запрос
resp = requests.get('https://api.example.com/verify', headers={'Key': api_key})
is_valid = resp.status_code == 200
# Кэшируем на 5 минут
r.setex(api_key, 300, 'valid' if is_valid else 'invalid')
return is_valid
# При первом вызове – запрос к API, при втором (в течение 5 минут) – ответ из кэша.
print(check_with_cache('abc')) # возможно, True
print(check_with_cache('abc')) # сразу True (кэш)
Кэширование особенно полезно для ключей, которые используются многократно в короткий промежуток времени.
Кастомный валидатор с поддержкой разных типов ключей
Если приложение работает с несколькими внешними сервисами, можно создать единый валидатор, который выбирает стратегию проверки на основе префикса или формата ключа.
import re
def validate(api_key):
# Определяем тип
if api_key.startswith('sk-'):
# Stripe-подобный ключ: длина 24 символа, буквы и цифры
return bool(re.fullmatch(r'sk-[A-Za-z0-9]{22}', api_key))
elif api_key.startswith('ghp_'):
# GitHub Personal Access Token
return bool(re.fullmatch(r'ghp_[A-Za-z0-9]{36}', api_key))
elif len(api_key) == 64 and all(c in '0123456789abcdef' for c in api_key):
# Шестнадцатеричный ключ (например, для self-hosted API)
return True
else:
return False
print(validate('sk_test_4eC39HqLyjWDarjtT1zdp7dc')) # True
print(validate('ghp_abc123def456ghi789jklmno')) # True
print(validate('unknown_key')) # False
True True False
Такой подход позволяет централизованно управлять правилами проверки и легко добавлять новые типы ключей.