# -*- coding: utf-8 -*- """DokoGen — автоматический подбор угроз безопасности информации (236 приказ). Логика программы: по характеристикам объекта КИИ (тип, сеть, нарушитель) подбирает актуальные угрозы из перечня БДУ ФСТЭК (threats_fstec.json). Если перечень не скачан — использует базовый threats.json. Правила эвристические; при наличии методического документа (например, «Методика оценки угроз безопасности информации» ФСТЭК) — адаптируются под него. """ import os import json def load_threats(dict_dir=None): """Список угроз: сначала скачанный с ФСТЭК (с кодами УБИ), иначе базовый.""" if dict_dir is None: dict_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries') fstec = os.path.join(dict_dir, 'threats_fstec.json') if os.path.exists(fstec): try: with open(fstec, encoding='utf-8') as f: data = json.load(f) if data: return data except Exception: pass base = os.path.join(dict_dir, 'threats.json') try: with open(base, encoding='utf-8') as f: return json.load(f) except Exception: return [] # --------------------------------------------------------------- # Ключевые слова (в названии угрозы) по признакам объекта # --------------------------------------------------------------- # Базовый набор — актуален для любого объекта КИИ BASE_KEYWORDS = [ 'вредоносн', 'несанкционированн', 'утечк', 'перехват', 'модификац', 'уничтожен', 'отказ в обслуживании', 'нсд', 'биос', 'пароль', 'криптографическ', 'шифрован', 'аутентификац', ] # Объект имеет подключение к сети Интернет / сеть электросвязи INTERNET_KEYWORDS = [ 'удалённ', 'удаленн', 'сетев', 'фишинг', 'ddos', 'отказ в обслуживании', 'веб', 'электронн', 'почт', 'беспроводн', 'wi-fi', 'wifi', 'vpn', 'маршрутизац', 'протокол', 'интернет', ] # Объект изолирован (нет подключения к Интернету) ISOLATED_KEYWORDS = [ 'съёмн', 'съемн', 'физическ', 'локальн', 'инсайд', 'носител', 'биос', 'желез', 'аппаратн', ] # АСУ / технологический процесс ASU_KEYWORDS = [ 'технологическ', 'асутп', 'промышленн', 'scada', 'датчик', 'исполнительн', 'технологическ', 'производств', 'процесс', ] # ИТКС / сети связи NETWORK_KEYWORDS = [ 'сеть', 'канал', 'протокол', 'маршрутизац', 'связ', 'передач', 'коммутац', 'трафик', ] # Внутренний нарушитель INSIDER_KEYWORDS = [ 'инсайд', 'сотрудник', 'привилегированн', 'администратор', 'внутренн', 'персонал', ] # Внешний нарушитель EXTERNAL_KEYWORDS = [ 'внешн', 'удалённ', 'удаленн', 'дистанционн', ] def _is_asu(isys) -> bool: t = (getattr(isys, 'object_type', '') or '').lower() return 'асу' in t or 'автоматизированн' in t or 'технологическ' in t def _is_network(isys) -> bool: t = (getattr(isys, 'object_type', '') or '').lower() return 'телекоммуникац' in t or 'сеть' in t def _has_internet(isys) -> bool: """Есть ли подключение к сети Интернет / внешним сетям.""" if getattr(isys, 'is_internet', False): return True desc = ((getattr(isys, 'description', '') or '') + ' ').lower() operator = (getattr(isys, 'net_operator', '') or '').lower() net_category = (getattr(isys, 'net_category', '') or '').lower() # Явные признаки ОТСУТСТВИЯ внешней сети if 'отсутств' in operator: # оператор «Отсутствует» return False if 'отсутств' in desc and ('интернет' in desc or 'подключ' in desc): return False if 'не требуется' in desc and ('доменн' in desc or 'интернет' in desc): return False if 'изолирован' in desc: return False # Сеть общего пользования — это и есть внешняя сеть (Интернет) if 'общего пользован' in net_category: return True # Указан реальный оператор/провайдер — внешняя связь есть if operator and operator not in ('нет', 'отсутствует', 'не требуется', '-'): return True # Консервативно: без явных признаков внешней сети считаем, что её нет return False def _is_insider(isys) -> bool: a = (getattr(isys, 'attacker_category', '') or '').lower() return 'внутрен' in a def _score(threat_lower, keywords): """Сколько ключевых слов встретилось в названии угрозы.""" return sum(1 for kw in keywords if kw in threat_lower) def suggest_threats_program(isys, threats=None, limit=None): """Подбор угроз по характеристикам объекта КИИ (инвертированная логика). По умолчанию актуальны ВСЕ угрозы из перечня (БДУ ФСТЭК). Исключаются только заведомо неактуальные для данного объекта: - нет сети Интернет → убираем угрозы удалённого доступа, веб, почты, беспроводных сетей, DDoS, облачных и т.п.; - объект типа АСУ → убираем угрозы BIOS, грид-систем, виртуализации (у технологического оборудования их, как правило, нет). Такой подход даёт полный список, а не «15 самых похожих». Возвращает список строк (с кодами УБИ, если они есть в перечне). """ if threats is None: threats = load_threats() if not threats: return [] has_internet = _has_internet(isys) is_asu = _is_asu(isys) is_network = _is_network(isys) # --- Слова-исключения (если встретились в названии угрозы — она неактуальна) --- exclude = [] if not has_internet: # Объект изолирован: угрозы, реализуемые только через внешние сети exclude += [ 'удалённ', 'удаленн', 'дистанционн', 'веб', 'электронн', 'почт', 'фишинг', 'беспроводн', 'wi-fi', 'wifi', 'интернет', 'ddos', 'vpn', 'облачн', 'грид', 'внешн', ] if is_asu: # АСУ: нет BIOS/виртуализации/браузера в привычном понимании exclude += ['биос', 'грид', 'гипервизор', 'виртуальн', 'браузер'] if not is_network and not is_asu: # Обычная ИС: неактуальны чисто сетевые/телеком-угрозы без указания # — оставляем всё, кроме специфики каналов связи, если их нет pass result = [] for t in threats: tl = str(t).lower() if any(kw in tl for kw in exclude): continue result.append(t) if limit and len(result) > limit: result = result[:limit] return result