Для чего нужна данная статья? :
- Реализовать аутентификацию пользователя по IP-адресу, без внешних ML-крэйтов.
- Полная реализация Extended-like Isolation Forest с нуля (только rand и num-traits).
Зачем Вам это уметь? :
- Создать систему которая учится на нормальных данных, а потом проверяет каждый новый запрос. Если запрос слишком необычный — доступ запрещён.
- Cделать систему интероперабельной.
Методы аутентификации на основе IP-адреса в зависимости от требований.
1. Фиксированный список разрешенных IP-адресов (Whitelist)
Самый простой способ — хранение списка разрешенных IP-адресов (Whitelist). При каждом запросе проверяется, входит ли IP клиента в этот список.
Пример с использованием actix-web:
let allowed_ips = vec![
"127.0.0.1".parse::<IpAddr>().unwrap(),
"192.168.1.100".parse::<IpAddr>().unwrap(),
];
Плюсы:
- Простота реализации.
- Хорошо подходит для небольшого списка IP-адресов.
Минусы:
- Статичность, требуется перезапуск сервера при изменении списка.
- Не подходит для большого количества динамических IP.
2. Черный список IP-адресов (Blacklist)
Противоположный подход — использовать черный список (Blacklist) для блокировки определенных IP-адресов, например, известных атакующих.
Пример:
let blocked_ips = vec![
"192.168.1.200".parse::<IpAddr>().unwrap(),
"203.0.113.5".parse::<IpAddr>().unwrap(),
];
Плюсы:
- Блокировка нежелательных пользователей.
- Можно использовать в сочетании с Whitelist для улучшенной безопасности.
Минусы:
- Требуется регулярное обновление черного списка.
- Не решает проблему злоумышленников с постоянно меняющимися IP.
3. Аутентификация на основе диапазонов IP (CIDR)
Иногда необходимо разрешать доступ не отдельным IP-адресам, а целым подсетям (например, все IP внутри диапазона 192.168.1.0/24). Для этого используется CIDR (Classless Inter-Domain Routing).
Пример:
let allowed_ranges = vec![
"192.168.1.0/24".parse::<IpNet>().unwrap(),
"10.0.0.0/8".parse::<IpNet>().unwrap(),
];
Плюсы:
- Удобно для работы с большими сетями.
- Масштабируемость при изменении сети.
Минусы:
- Может быть сложнее управлять большим количеством диапазонов.
- Возможна утечка безопасности при ошибочной конфигурации.
4. Динамическое управление списками IP (с базой данных или конфигурацией)
Если требуется часто обновлять списки IP-адресов, можно хранить их в базе данных или в файле конфигурации. Приложение будет динамически проверять IP при каждом запросе.
Пример с загрузкой из файла конфигурации:
fn load_allowed_ips() -> Vec<IpAddr> {
let contents = std::fs::read_to_string("allowed_ips.txt").unwrap();
contents.lines().map(|line| line.parse::<IpAddr>().unwrap()).collect()
}
Плюсы:
- Динамическое управление без необходимости перезапуска сервера.
- Удобно для администрирования.
Минусы:
- Более сложная реализация.
- Зависимость от внешних систем, таких как БД или файловые системы.
5. Аутентификация на основе геолокации IP
Использование базы данных GeoIP (например, geoip2) для проверки географического положения IP-адреса и принятия решения на основе страны, региона и т. д.
Пример:
let city = lookup_city(ip).unwrap();
if city.country == "RU" {
HttpResponse::Ok().body("Доступ разрешен для России")
} else {
HttpResponse::Forbidden().body("Доступ запрещен")
}
Плюсы:
- Полезно для географического ограничения доступа.
- Можно блокировать доступ из подозрительных стран.
Минусы:
- Требует использования сторонних сервисов или библиотек.
- Менее точное решение, так как геолокация IP не всегда точна.
6. Rate Limiting по IP
Ограничение количества запросов с одного IP-адреса (Rate Limiting) может использоваться для предотвращения DDoS-атак. Реализуется через специальный middleware, который отслеживает количество запросов от каждого IP.
Пример с actix-web и использованием middleware:
use actix_web::middleware::Logger;
use actix_rate_limit::RateLimiter;
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.wrap(RateLimiter::default())
.route("/", web::get().to(ip_auth))
})
Плюсы:
- Защита от атак типа brute force или DDoS.
- Может быть комбинировано с другими методами.
Минусы:
- Не предотвращает доступ на основе IP напрямую.
- Требует настройки и мониторинга.
7. API токены с привязкой к IP
Еще один подход — использование API-токенов, привязанных к IP-адресу. Это может быть полезно в системах с авторизованными пользователями или клиентами API. Проверка токена производится совместно с проверкой IP.
Пример:
let allowed_tokens = HashMap::new();
allowed_tokens.insert("12345", "192.168.1.100");
fn validate_token(token: &str, ip: &IpAddr) -> bool {
allowed_tokens.get(token) == Some(ip)
}
Плюсы:
- Более гибкое решение для API, где нужна строгая проверка.
- Подходит для работы с клиентами API.
Минусы:
- Необходимость хранения и управления токенами.
Алгоритм работы кода
Запуск сервера (#[launch]): Генерируются синтетические "нормальные" данные (10 000 образцов).
Пример:
Представьте, что у вас есть сайт, и вы хотите научить систему распознавать нормальные запросы. Вы генерируете 10 000 "правильных" запросов, чтобы система понимала, как выглядит обычная активность.
Строится CustomIsolationForest (300 деревьев, подвыборка 512).
Это алгоритм машинного обучения, который ищет аномалии (необычные запросы). Он работает как лес из 300 деревьев, каждое из которых обучается на случайной выборке из 512 запросов.
Пример:
Представьте, что каждое дерево — это детектив, который учится распознавать подозрительное поведение. Чем больше детективов (деревьев), тем точнее система.
Создаётся цепочка стратегий аутентификации, включая ML.
Это набор правил, которые проверяют каждый запрос по очереди. Если хотя бы одно правило говорит "нет" — доступ запрещён.
Пример:
Представьте, что у вас есть охранник, который проверяет: сначала паспорт, потом пропуск, потом отпечаток пальца. Если хоть одна проверка не пройдена — вход запрещён.
Обработка запроса: Rocket guards собирают IP, токен, timestamp → AuthContext.
Это система, которая собирает данные о каждом запросе: IP-адрес, токен (уникальный ключ пользователя), время запроса.
Пример:
Как если бы на входе в здание вас спрашивали: "Кто вы? (токен), откуда пришли? (IP), во сколько? (время)".
AuthenticationEngine::authenticate последовательно вызывает каждую стратегию.
AuthContext — это "пакет" с данными о запросе. AuthenticationEngine — это "движок", который проверяет этот пакет по всем стратегиям.
Пример:
Представьте, что AuthContext — это папка с документами, а AuthenticationEngine — это начальник, который проверяет каждый документ по списку правил.
Если любая стратегия возвращает Deny — доступ запрещён.
Работа ML: Обучение: Для каждого дерева берётся случайная подвыборка → рекурсивно строятся узлы с случайным признаком и случайной точкой разделения.
Инференс: Для нового запроса извлекаются признаки → для каждого дерева вычисляется длина пути → усредняется → нормализуется в score [0,1].
Когда приходит новый запрос, система извлекает его признаки, пропускает через каждое дерево, вычисляет "длину пути" (насколько запрос необычный), усредняет и нормализует в score от 0 до 1.
Пример:
Представьте, что вы бросаете мяч в корзину. Система смотрит, насколько далеко вы от корзины, и ставит оценку: 0 — попал, 1 — очень далеко.
Решение: score > threshold → аномалия → отказ.
Если оценка аномальности (score) выше порога (threshold), система считает запрос подозрительным и запрещает доступ.
Пример:
Как если бы у вас был металлоискатель на входе: если писк слишком громкий (score > threshold), охранник не пускает вас дальше.