Разбираем, как усилить безопасность Kad-DHT против Eclipse-атак: IP-разнообразие подсетей, борьба с флаки-тестами и практические выводы для вашего кода.
Вы когда-нибудь задумывались, как децентрализованные сети защищаются от атак, когда злоумышленник окружает ваш узел своими подконтрольными нодами? Это называется Eclipse-атака, и она может полностью отрезать вас от сети. В этой статье мы разберём, как мы укрепляем Kademlia DHT в py-libp2p, внедряя IP-разнообразие подсетей, и как боремся с флаки-тестами, которые мешают разработке. Вы получите конкретные практические приёмы для повышения безопасности и стабильности ваших P2P-систем.
Kademlia — это распределённая хеш-таблица (DHT), используемая во многих P2P-сетях. Она хранит информацию о других узлах в k-корзинах (k-buckets). Если атакующий контролирует множество узлов в одной подсети, он может заполнить ваши k-корзины своими узлами, изолировав вас от честной сети. Это называется Eclipse-атакой. Защита от неё критически важна для целостности сети.
Представьте, что вы общаетесь только с соседями. Если все соседи — друзья злоумышленника, они могут передавать вам ложную информацию или блокировать доступ к реальным узлам. В DHT это означает, что ваши запросы на поиск данных будут обрабатываться только подконтрольными узлами, которые могут вернуть поддельные данные.
Мы реализовали функцию, которая ограничивает количество узлов из одной IP-подсети в k-корзинах. Это делает атаку дороже: злоумышленнику придётся арендовать множество разных подсетей, что значительно увеличивает стоимость атаки.
В PR feat(kad_dht): enforce IP subnet diversity in k-buckets (#1383) мы добавили 226 строк логики. Основная идея — при добавлении нового узла в k-корзину проверять, сколько уже узлов из той же подсети. Если лимит превышен, узел не добавляется.
# Пример: проверка IP-разнообразия
from ipaddress import ip_network, ip_address
MAX_PER_SUBNET = 2 # максимум узлов из одной подсети
def is_allowed(peer_ip, subnet_count):
"""Проверяем, можно ли добавить узел"""
return subnet_count.get(peer_ip, 0) < MAX_PER_SUBNET
# Использование
subnet_counts = {} # словарь: подсеть -> количество узлов
peer_ip = '192.168.1.5'
subnet = ip_network(peer_ip + '/24', strict=False)
if is_allowed(str(subnet), subnet_counts):
subnet_counts[str(subnet)] = subnet_counts.get(str(subnet), 0) + 1
# добавляем узел в k-корзину
else:
# отклоняем узел
pass
Мы также отказались от рандомизированного выбора узлов при поиске, так как это не давало достаточной защиты. Более надёжный подход — строгое управление k-корзинами.
Асинхронное программирование приносит не только скорость, но и проблемы с тестами. Флаки-тесты — это тесты, которые иногда проходят, иногда нет, из-за гонок (race conditions). Мы разобрали два таких случая.
Тест проверял удаление записей по истечении времени. Ошибка была в том, что фоновый поток очистки не успевал завершиться до проверки. Решение — использовать более точные ожидания и моки.
# Пример исправления: ожидание завершения фоновой задачи
import trio
async def test_expiry():
cache = TimedCache(expiry=0.1)
cache.add('key', 'value')
await trio.sleep(0.2) # ждём больше, чем таймер
assert 'key' not in cache
Вместо trio.sleep с фиксированной задержкой лучше использовать trio.wait_all_tasks_blocked или явные события.
Второй тест касался Gossipsub — подписки распространяются асинхронно. Если тест проверяет состояние слишком рано, он падает. Решение — добавить ожидание распространения подписки.
async def test_gossip_gate():
# ... настройка узлов
await node1.subscribe('topic')
# Ждём, пока подписка дойдёт до node2
await trio.sleep(0.5) # эвристика, лучше использовать события
assert node2.has_subscription('topic')
Эти исправления не самые захватывающие, но они делают CI стабильным, что экономит часы времени.
Параллельно я поддерживал свой Neovim конфиг: 6 коммитов, в основном автоматические обновления плагинов. Это позволяет всегда иметь актуальный редактор без лишних усилий.
Если вы работаете с P2P-сетями, обязательно внедряйте IP-разнообразие подсетей, чтобы защититься от Eclipse-атак. Для асинхронных тестов используйте явные ожидания вместо sleep. Начните с проверки своих k-корзин на предмет концентрации узлов из одной подсети — это простой шаг к безопасности.
В следующей неделе я планирую довести до конца PR по IP-разнообразию и заняться новыми фичами. Следите за обновлениями!
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →