Новая кнопка «⚡ Автозаполнить» в панели действий — заполняет пустые поля
логически, без выдумок:
- категория нарушителя: есть интернет → внешний, нет → внутренний
- уровень возможностей: Н1 (внутренний) / Н2 (внешний)
- виды нарушителей: по категории из словаря attacker_types.json
- угрозы: подбор программой (скоринг по характеристикам объекта)
- инциденты: выводятся из подобранных угроз по ключевым словам
- орг. меры: базовый набор (контролируемая зона, доступ, документы,
ответственные, обучение, инциденты, перечень лиц)
- тех. меры: базовые + доп. по средствам защиты объекта (СЗИ:
межсетевой экран → МЭ, антивирус, СОВ, СКЗИ, виртуализация)
Незаполненное пользователем не перезаписывает.
345 lines
18 KiB
Python
345 lines
18 KiB
Python
# -*- 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
|
||
|
||
# --- Положительные ключевые слова: чем больше совпало — тем угроза актуальнее ---
|
||
pos = list(BASE_KEYWORDS)
|
||
if has_internet:
|
||
pos += INTERNET_KEYWORDS
|
||
else:
|
||
pos += ISOLATED_KEYWORDS
|
||
if is_asu:
|
||
pos += ASU_KEYWORDS
|
||
if is_network:
|
||
pos += NETWORK_KEYWORDS
|
||
if _is_insider(isys):
|
||
pos += INSIDER_KEYWORDS
|
||
else:
|
||
pos += EXTERNAL_KEYWORDS
|
||
# Убираем дубликаты
|
||
pos = list(dict.fromkeys(pos))
|
||
|
||
scored = []
|
||
for t in threats:
|
||
tl = str(t).lower()
|
||
if any(kw in tl for kw in exclude):
|
||
continue
|
||
sc = sum(1 for kw in pos if kw in tl)
|
||
scored.append((sc, t))
|
||
|
||
# Сортируем по релевантности (сначала — с наибольшим совпадением)
|
||
scored.sort(key=lambda x: -x[0])
|
||
result = [t for sc, t in scored if sc > 0]
|
||
|
||
if limit and len(result) > limit:
|
||
result = result[:limit]
|
||
return result
|
||
|
||
|
||
# ---------------------------------------------------------------
|
||
# Автозаполнение вкладки «Безопасность» по данным объекта (логическое)
|
||
# ---------------------------------------------------------------
|
||
|
||
# Ключевые слова: угроза → тип инцидента (словарь incidents.json)
|
||
INCIDENT_RULES = [
|
||
('отказ в обслуживании', 'отказ в обслуживании'),
|
||
('несанкционированн', 'несанкционированный доступ'),
|
||
('утечк', 'утечка данных (нарушение конфиденциальности)'),
|
||
('модификац', 'модификация (подмена) данных'),
|
||
('подмен', 'модификация (подмена) данных'),
|
||
('функционирован', 'нарушение функционирования технических средств'),
|
||
('техническ', 'нарушение функционирования технических средств'),
|
||
('использован', 'несанкционированное использование вычислительных ресурсов объекта'),
|
||
]
|
||
|
||
# Базовый набор орг. мер (всегда актуальны для значимого объекта)
|
||
BASE_ORG_MEASURES = [
|
||
'Установление контролируемой зоны',
|
||
'Контроль физического доступа к объекту',
|
||
'Разработка документов (регламентов, инструкций, руководств) по обеспечению безопасности объекта',
|
||
'Назначение ответственных за обеспечение безопасности объекта',
|
||
'Обучение и инструктаж персонала',
|
||
'Регламентация действий при компьютерных инцидентах',
|
||
'Утверждение перечня лиц, имеющих доступ к объекту',
|
||
]
|
||
|
||
# Базовый набор тех. мер
|
||
BASE_TECH_MEASURES = [
|
||
'Идентификация и аутентификация пользователей',
|
||
'Управление доступом',
|
||
'Ограничение программной среды',
|
||
'Антивирусная защита',
|
||
'Защита информации от несанкционированного доступа (СЗИ от НСД)',
|
||
'Регистрация и мониторинг событий безопасности',
|
||
'Резервное копирование и восстановление',
|
||
'Защита технических средств и линий связи',
|
||
]
|
||
|
||
# Средства защиты → доп. тех. меры
|
||
DEFENSE_TOOL_MEASURES = [
|
||
('межсетев', 'Межсетевое экранирование'),
|
||
('firewall', 'Межсетевое экранирование'),
|
||
('сетев', 'Межсетевое экранирование'),
|
||
('обнаруж', 'Обнаружение вторжений (СОВ)'),
|
||
('ids', 'Обнаружение вторжений (СОВ)'),
|
||
('ips', 'Обнаружение вторжений (СОВ)'),
|
||
('криптограф', 'Криптографическая защита информации (СКЗИ)'),
|
||
('vipnet', 'Криптографическая защита информации (СКЗИ)'),
|
||
('континент', 'Криптографическая защита информации (СКЗИ)'),
|
||
('виртуализ', 'Защита среды виртуализации'),
|
||
('vmware', 'Защита среды виртуализации'),
|
||
('hyper-v', 'Защита среды виртуализации'),
|
||
]
|
||
|
||
|
||
def auto_fill_security(isys, threats=None):
|
||
"""Логическое автозаполнение вкладки «Безопасность» по данным объекта КИИ.
|
||
|
||
Заполняет (только если поле пустое):
|
||
attacker_category — по наличию интернета (внешний/внутренний нарушитель);
|
||
attacker_level — Н1/Н2 (внутренний — базовые, внешний — базовые повышенные);
|
||
attacker_types — виды нарушителей по категории/интернету;
|
||
threats — подбор программой (скоринг по характеристикам);
|
||
incidents — типы инцидентов, выводимые из подобранных угроз;
|
||
org_measures / tech_measures — базовый набор + по СЗИ объекта.
|
||
|
||
Возвращает dict {поле: новое_значение} только для пустых полей.
|
||
"""
|
||
from .models import InformationSystem
|
||
res = {}
|
||
|
||
has_internet = _has_internet(isys)
|
||
|
||
# 1. Категория нарушителя
|
||
if not (getattr(isys, 'attacker_category', '') or '').strip():
|
||
res['attacker_category'] = 'Внешний нарушитель' if has_internet else 'Внутренний нарушитель'
|
||
|
||
# 2. Уровень возможностей
|
||
if not (getattr(isys, 'attacker_level', '') or '').strip():
|
||
res['attacker_level'] = 'Н2 — Базовые повышенные возможности' if has_internet else 'Н1 — Базовые возможности'
|
||
|
||
# 3. Виды нарушителей
|
||
if not (getattr(isys, 'attacker_types', '') or '').strip():
|
||
if has_internet:
|
||
types = [
|
||
'Отдельные физические лица (хакеры)',
|
||
'Преступные группы (криминальные структуры)',
|
||
'Конкурирующие организации',
|
||
'Авторизованные пользователи систем и сетей',
|
||
'Системные администраторы и администраторы безопасности',
|
||
'Бывшие (уволенные) работники (пользователи)',
|
||
]
|
||
else:
|
||
types = [
|
||
'Авторизованные пользователи систем и сетей',
|
||
'Системные администраторы и администраторы безопасности',
|
||
'Бывшие (уволенные) работники (пользователи)',
|
||
'Лица, обеспечивающие функционирование систем и сетей или обеспечивающих систем оператора (администрация, охрана, уборщики и др.)',
|
||
]
|
||
res['attacker_types'] = '\n'.join(types)
|
||
|
||
# 4. Угрозы — подбор программой
|
||
if not (getattr(isys, 'threats', '') or '').strip():
|
||
prog = suggest_threats_program(isys, threats=threats)
|
||
if prog:
|
||
res['threats'] = '\n'.join(prog)
|
||
|
||
# 5. Инциденты — из подобранных угроз
|
||
if not (getattr(isys, 'incidents', '') or '').strip():
|
||
threats_text = res.get('threats') or (getattr(isys, 'threats', '') or '')
|
||
if threats_text:
|
||
incidents = []
|
||
low = threats_text.lower()
|
||
for kw, incident in INCIDENT_RULES:
|
||
if kw in low and incident not in incidents:
|
||
incidents.append(incident)
|
||
if incidents:
|
||
res['incidents'] = '\n'.join(incidents)
|
||
|
||
# 6. Орг. меры — базовый набор
|
||
if not (getattr(isys, 'org_measures', '') or '').strip():
|
||
res['org_measures'] = '\n'.join(BASE_ORG_MEASURES)
|
||
|
||
# 7. Тех. меры — базовые + по СЗИ объекта
|
||
if not (getattr(isys, 'tech_measures', '') or '').strip():
|
||
tech = list(BASE_TECH_MEASURES)
|
||
defense = ' '.join(getattr(isys, 'defense_tools_list', []) or [])
|
||
dlow = defense.lower()
|
||
for kw, measure in DEFENSE_TOOL_MEASURES:
|
||
if kw in dlow and measure not in tech:
|
||
tech.append(measure)
|
||
res['tech_measures'] = '\n'.join(tech)
|
||
|
||
return res
|