""" Проверка геолокации сервера. Согласно 152-ФЗ (ст. 18), сбор персональных данных граждан РФ должен производиться с использованием баз данных, находящихся на территории РФ. """ from __future__ import annotations from playwright.async_api import Page from scanner.checks.base import BaseCheck from scanner.models import CheckResult, RiskLevel class ServerLocationCheck(BaseCheck): """Проверяет, находится ли сервер сайта в России.""" name = "server_location" title = "Геолокация сервера" description = "Проверяет, находится ли сервер сайта на территории РФ" risk_on_fail = RiskLevel.CRITICAL default_weight = 25 # Популярные иностранные провайдеры (можно расширять) RUSSIAN_HOSTERS = [ "yandex", "mail.ru", "vk", "selectel", "timeweb", "beget", "reg.ru", "nic.ru", "rtcdn", "astra", "sbercloud", "cloud4russia", "гостех", "ростелеком", "мтс", "билайн", "мегафон", "вымпелком", "ростелеком", "netology", ] async def check(self, page: Page, url: str) -> CheckResult: try: # Получаем IP-адрес сервера через JavaScript ip_info = await page.evaluate("""() => { return new Promise((resolve) => { // Пробуем определить IP через несколько методов const methods = [ () => fetch('https://api.ipify.org?format=json') .then(r => r.json()).then(d => ({ip: d.ip, method: 'ipify'})), () => fetch('https://api.my-ip.io/v2/json') .then(r => r.json()).then(d => ({ip: d.ip, method: 'my-ip-io'})), ]; // Пробуем каждый метод (async () => { for (const method of methods) { try { const result = await method(); resolve(result); return; } catch (e) {} } resolve(null); })(); }); }""") if not ip_info: return self._skip_result( message="Не удалось определить IP-адрес сервера.", ) ip = ip_info.get("ip", "") method = ip_info.get("method", "") # Определяем страну по IP (используем публичный API) country = await self._get_country(ip) if not country: return self._skip_result( message=f"Не удалось определить страну для IP {ip}.", ) # Проверяем, Россия ли это if country.lower() == "ru" or "Россия" in country: return self._pass_result( message=f"Сервер находится в России (IP: {ip}, Страна: {country})", details={ "ip": ip, "country": country, "method": method, }, ) # Сервер за рубежом — критическое нарушение return self._fail_result( message=( f"Сервер находится ЗА ПРЕДЕЛАМИ РОССИИ ({country}, IP: {ip}). " f"Это нарушение ст. 18 152-ФЗ: базы данных граждан РФ должны быть в РФ." ), details={ "ip": ip, "country": country, "method": method, }, ) except Exception as e: return self._error_result(message=f"Ошибка при проверке геолокации: {str(e)}") async def _get_country(self, ip: str) -> str | None: """Получает страну по IP через публичный API.""" try: import urllib.request import json # Пробуем несколько бесплатных API apis = [ f"https://ipapi.co/{ip}/json/", f"https://ip-api.com/json/{ip}?fields=country", f"https://api.ipgeolocation.io/ipgeo?ip={ip}", ] for api_url in apis: try: req = urllib.request.Request( api_url, headers={"User-Agent": "Mozilla/5.0"} ) with urllib.request.urlopen(req, timeout=5) as response: data = json.loads(response.read().decode()) # ipapi.co формат if "country_name" in data: return data["country_name"] # ip-api.com формат if "country" in data: return data["country"] # ipgeolocation формат if "country_name" in data: return data["country_name"] except Exception: continue return None except Exception: return None