SIEM
SIEM — Security Information and Event Management (управление информацией и событиями безопасности). SIEM собирает журналы с многих устройств в одно хранилище. Вы ищете в нём инциденты, пробелы политики и инвентарь.
NVM for Windows пишет активность Node.js в NVM for Windows/Operational.
Отправьте этот канал в SIEM, чтобы видеть, какие версии Node.js работают в организации, кто их ставил или удалял, и когда правило firewall или malware блокировало пакет.
Advanced Logging добавляет EventName и JSON Payload.
Используйте эти поля, когда запрос должен назвать событие, а не разбирать свободный текст.
Это руководство показывает, как собрать канал, затем даёт стартовые запросы.
См. Коды ошибок для каждого значения NVM####.
NVM4312 — это EventData.Code == 4312. Уберите префикс NVM.
Сбор этого журнала
| Элемент | Значение |
|---|---|
| Канал | NVM for Windows/Operational |
| Provider | NVM for Windows |
| GUID provider | {4c0f8d8e-2d6b-4f93-9f0f-3f0d7b4e2d11} |
Certified MSI регистрирует этот provider и включает канал. Направьте сборщик на этот канал. Не собирайте только журнал Windows Application. Предупреждения Application, например NVM4101, — отдельный поток.
Большинство событий EventName / Payload требуют дополнение Advanced Logging. Без этой лицензии классические события всё равно содержат Code. В них нет EventName и Payload.
Исключение: nodejs.executed (Event ID 120) пишется при включённом log_executions. Community Builds и Certified Builds. Лицензия Advanced Logging не нужна. Certified Builds так же пишут package_manager.executed.
Как запрашивать
| Нужно | Фильтр |
|---|---|
| Структурированный info, warning или error | Event ID 120, 121 или 122 |
| Код NVM | Code (только число) |
| Именованное событие жизненного цикла или аудита | EventName, затем разберите Payload как JSON |
Каждый запуск node.exe | Event ID 120 nodejs.executed, когда включён log_executions. Event 200 не используется |
Payload — строка JSON.
Code — отдельный UInt32.
Не ищите номер NVM внутри Payload.
Code равен 0, когда событие не является условием NVM####.
Сборщики по-разному разворачивают EventData. Если поле из примера отсутствует, используйте имена, которые создала ваша платформа.
| Microsoft Sentinel | Splunk | Elastic | Event Viewer | |
|---|---|---|---|---|
| Канал | EventLog | Channel | winlog.channel | имя журнала |
| Event ID | EventID | EventCode | winlog.event_id | EventID |
| Имя события | EventData.EventName | EventData.EventName | winlog.event_data.EventName | Data[@Name='EventName'] |
| Payload | EventData.Payload | EventData.Payload | winlog.event_data.Payload | Data[@Name='Payload'] |
| Code | EventData.Code | EventData.Code | winlog.event_data.Code | Data[@Name='Code'] |
| Computer | Computer | Computer | winlog.computer_name | — |
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code == 4312
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventData.Code=4312
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_data.Code == 4312
wevtutil qe "NVM for Windows/Operational" /q:"*[EventData[Data[@Name='Code']='4312']]" /f:text /c:20
Версии Node.js в работе
Event ID 120 nodejs.executed пишет каждый запуск shim, когда включён log_executions.
Payload.NodeVersion — версия. Event ID 200 не используется.
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nodejs.executed"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Launches = count() by tostring(Payload.NodeVersion), Computer
| order by Launches desc
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nodejs.executed"
| eval Payload=json_decode(EventData.Payload)
| stats count as Launches by Payload.NodeVersion, Computer
| sort -Launches
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nodejs.executed"
| EVAL Payload = TO_JSON(winlog.event_data.Payload)
| STATS Launches = COUNT(*) BY version = Payload.NodeVersion, host = winlog.computer_name
| SORT Launches DESC
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nodejs.executed']]" /f:text /c:20
Число узлов для того же события:
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nodejs.executed"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Launches = dcount(Computer) by tostring(EventData.EventName)
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nodejs.executed"
| stats dc(Computer) as Hosts
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nodejs.executed"
| STATS Hosts = COUNT_DISTINCT(winlog.computer_name)
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nodejs.executed']]" /f:text /c:20
Кто установил версию Node.js
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.system.changed"
| extend Payload = parse_json(tostring(EventData.Payload))
| where Payload.Action == "install"
| where Payload.Outcome == "Succeeded"
| summarize Installs = count() by tostring(Payload.NodeVersion), tostring(Payload.User), tostring(Payload.hostname)
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nvm.system.changed"
| eval action=json_extract('EventData.Payload',"Action"), outcome=json_extract('EventData.Payload',"Outcome"), node_version=json_extract('EventData.Payload',"NodeVersion"), user=json_extract('EventData.Payload',"User"), hostname=json_extract('EventData.Payload',"hostname")
| search action=install outcome=Succeeded
| stats count as Installs by node_version, user, hostname
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nvm.system.changed" AND winlog.event_data.Payload LIKE "*\"Action\":\"install\"*" AND winlog.event_data.Payload LIKE "*\"Outcome\":\"Succeeded\"*"
| STATS Installs = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nvm.system.changed']]" /f:text /c:20
Кто установил Node.js 20
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| extend Payload = parse_json(tostring(EventData.Payload))
| where EventData.EventName == "nvm.system.changed"
| where Payload.Action == "install"
| where Payload.Outcome == "Succeeded"
| where tostring(Payload.NodeVersion) startswith "20."
| summarize People = dcount(tostring(Payload.User))
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nvm.system.changed"
| eval action=json_extract('EventData.Payload',"Action"), outcome=json_extract('EventData.Payload',"Outcome"), node_version=json_extract('EventData.Payload',"NodeVersion"), user=json_extract('EventData.Payload',"User")
| search action=install outcome=Succeeded
| search node_version="20.*"
| stats dc(user) as People
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nvm.system.changed" AND winlog.event_data.Payload LIKE "*\"Action\":\"install\"*" AND winlog.event_data.Payload LIKE "*\"Outcome\":\"Succeeded\"*" AND winlog.event_data.Payload LIKE "*\"NodeVersion\":\"20.*"
| STATS People = COUNT_DISTINCT(winlog.computer_name)
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nvm.system.changed']]" /f:text /c:20
Смените 20. на другую major-версию или на EOL-линейку из вашей политики.
Событие само EOL не помечает.
Кто удалил версию Node.js
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.system.changed"
| extend Payload = parse_json(tostring(EventData.Payload))
| where Payload.Action == "uninstall"
| where Payload.Outcome == "Succeeded"
| summarize Uninstalls = count() by tostring(Payload.NodeVersion), tostring(Payload.User), tostring(Payload.hostname)
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nvm.system.changed"
| eval action=json_extract('EventData.Payload',"Action"), outcome=json_extract('EventData.Payload',"Outcome"), node_version=json_extract('EventData.Payload',"NodeVersion"), user=json_extract('EventData.Payload',"User"), hostname=json_extract('EventData.Payload',"hostname")
| search action=uninstall outcome=Succeeded
| stats count as Uninstalls by node_version, user, hostname
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nvm.system.changed" AND winlog.event_data.Payload LIKE "*\"Action\":\"uninstall\"*" AND winlog.event_data.Payload LIKE "*\"Outcome\":\"Succeeded\"*"
| STATS Uninstalls = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nvm.system.changed']]" /f:text /c:20
Какая версия стала по умолчанию
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.version.activated"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Activations = count() by tostring(Payload.version), tostring(Payload.user), tostring(Payload.hostname)
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nvm.version.activated"
| eval version=json_extract('EventData.Payload',"version"), user=json_extract('EventData.Payload',"user"), hostname=json_extract('EventData.Payload',"hostname")
| stats count as Activations by version, user, hostname
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nvm.version.activated"
| STATS Activations = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nvm.version.activated']]" /f:text /c:20
Установки пакетного менеджера: разрешено или запрещено
Certified Builds пишут package_manager.install, когда межсетевой экран модулей разрешает или запрещает установку.
NVM4408 — разрешение.
NVM4403 — запрет.
Блок malware это событие не пишет. Используйте NVM4602.
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID in (120, 122)
| where EventData.EventName == "package_manager.install"
| summarize Events = count() by Computer, EventData.Code
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode IN (120,122) EventData.EventName="package_manager.install"
| stats count as Events by Computer, EventData.Code
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id IN (120, 122) AND winlog.event_data.EventName == "package_manager.install"
| STATS Events = COUNT(*) BY host = winlog.computer_name, code = winlog.event_data.Code
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120 or EventID=122)] and EventData[Data[@Name='EventName']='package_manager.install']]" /f:text /c:20
Блоки malware
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.Code == 4602
| where EventData.EventName == "malware.package_blocked"
| summarize Blocks = count() by Computer
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.Code=4602 EventData.EventName="malware.package_blocked"
| stats count as Blocks by Computer
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.Code == 4602 AND winlog.event_data.EventName == "malware.package_blocked"
| STATS Blocks = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='Code']='4602'] and EventData[Data[@Name='EventName']='malware.package_blocked']]" /f:text /c:20
Смотрите Payload для имени пакета, версии если известна, команды, source и reason.
Сбои проверки целостности
Отказ Authenticode / trust — NVM4301. Несовпадение FileVersion node.exe с папкой — NVM4314.
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code in (4301, 4314)
| summarize Failures = count() by Computer, EventID, EventData.EventName, EventData.Code
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventData.Code IN (4301,4314)
| stats count as Failures by Computer, EventCode, EventData.EventName, EventData.Code
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_data.Code IN (4301, 4314)
| STATS Failures = COUNT(*) BY host = winlog.computer_name, event_id = winlog.event_id, name = winlog.event_data.EventName, code = winlog.event_data.Code
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[EventData[Data[@Name='Code']='4301' or Data[@Name='Code']='4314']]" /f:text /c:20
Блоки version firewall
Отказ Governance VersionAllowList / VersionBlockList — NVM4411.
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code == 4411
| where EventData.EventName == "node.security.version_firewall_blocked"
| summarize Blocks = count() by Computer
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventData.Code=4411 EventData.EventName="node.security.version_firewall_blocked"
| stats count as Blocks by Computer
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_data.Code == 4411 AND winlog.event_data.EventName == "node.security.version_firewall_blocked"
| STATS Blocks = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[EventData[Data[@Name='Code']='4411'] and EventData[Data[@Name='EventName']='node.security.version_firewall_blocked']]" /f:text /c:20
Блоки lifecycle-скриптов
HKLM BlockPackageManagerLifecycleScripts пишет NVM4412 (package_manager.lifecycle_scripts_blocked) на Certified Builds с Advanced Logging, когда proxy переписывает install-команду (install, ci, add, update, rebuild и похожие; npx с пакетом; голый yarn). npm --version и npm run не пишут. Lock на эти команды всё равно действует. Это аудит, не сбой install. Community не пишет это событие. Смотрите Payload.command, Payload.verb и Payload.action.
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code == 4412
| where EventData.EventName == "package_manager.lifecycle_scripts_blocked"
| summarize Locks = count() by Computer, tostring(EventData.EventName)
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventData.Code=4412 EventData.EventName="package_manager.lifecycle_scripts_blocked"
| stats count as Locks by Computer
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_data.Code == 4412 AND winlog.event_data.EventName == "package_manager.lifecycle_scripts_blocked"
| STATS Locks = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[EventData[Data[@Name='Code']='4412'] and EventData[Data[@Name='EventName']='package_manager.lifecycle_scripts_blocked']]" /f:text /c:20
Изменения конфигурации
- Microsoft Sentinel
- Splunk
- Elastic
- Event Viewer
Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.configuration.changed"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Changes = count() by tostring(Payload.Configuration), tostring(Payload.User), tostring(Payload.hostname)
Смените `index=wineventlog` на свой индекс Splunk.
index=wineventlog Channel="NVM for Windows/Operational" EventCode=120 EventData.EventName="nvm.configuration.changed"
| eval configuration=json_extract('EventData.Payload',"Configuration"), user=json_extract('EventData.Payload',"User"), hostname=json_extract('EventData.Payload',"hostname")
| stats count as Changes by configuration, user, hostname
Смените `FROM logs-*` на свой поток Elastic.
FROM logs-*
| WHERE winlog.channel == "NVM for Windows/Operational" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nvm.configuration.changed"
| STATS Changes = COUNT(*) BY host = winlog.computer_name
Event Viewer показывает совпавшие события. Он не считает и не группирует их.
wevtutil qe "NVM for Windows/Operational" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nvm.configuration.changed']]" /f:text /c:20
Шаблоны Event ID
Windows Event ID — шаблон. Это не номер NVM.
| Event ID | Источник | Поля |
|---|---|---|
| 100 | nvm.exe info | Source, Message, Code |
| 101 | nvm.exe warning | Source, Message, Code |
| 102 | nvm.exe error | Source, Message, Code |
| 120 | структурированный info | Source, EventName, Payload, Code |
| 121 | структурированный warning | Source, EventName, Payload, Code |
| 122 | структурированный error | Source, EventName, Payload, Code |
| 200 | не используется | Остаток манифеста. Журнал запусков — Event ID 120 nodejs.executed. |
| 210 | shim info | Source, Message, Code |
| 211 | shim warning | Source, Message, Code |
| 212 | shim error | Source, Message, Code |
Event ID 200 не используется. Журнал запусков — Event ID 120.
События безопасности от shim содержат user, sid, hostname, parent_process, parent_pid, project_name и project_path, когда процесс может их получить.
Пустое значение — unknown.
Имена событий с Code 0
Запрашивайте их по EventName.
| EventName | Что записывает |
|---|---|
nvm.system.changed | Установка, удаление и похожие результаты жизненного цикла. Payload: Action, NodeVersion, Outcome, User, sid, hostname, correlation_id. |
nvm.configuration.changed | Изменение настройки. Payload: Configuration, Value, Old, Outcome. |
nvm.version.activated | Сменилась активная версия Node.js. Payload: version, previous_version, user, hostname. |
nodejs.executed | Запуск node, когда LogExecutions = 1. Источник node-shim. Если allow-флаги были переданы, добавлены или удалены, payload содержит permission_allow.supplied, permission_allow.applied и permission_allow.removed. |
package_manager.executed | Запуск npm, yarn, pnpm или похожего на Certified Build, когда LogExecutions = 1. Источник proxy. |
Имена событий с кодом NVM
Сопоставьте Code с номером в Кодах ошибок.
| EventName | Code | Источник |
|---|---|---|
verify_key.open_failed | 4312 | nvm.exe на Certified Build. Community Build пишет Event ID 102 с тем же кодом. |
node.security.verification_failed | 4301 | node-shim или proxy |
node.security.activation_blocked | 4302 или 4305 | nvm.exe использует 4302. Proxy использует 4305. |
node.security.cache_state_changed | 4303 | node-shim или proxy |
node.security.full_verification_recovered | 4304 | node-shim или proxy |
node.security.version_mismatch | 4314 | node-shim или proxy. PE FileVersion не совпадает с папкой установки. |
node.security.version_firewall_blocked | 4411 | node-shim или proxy. Governance VersionAllowList / VersionBlockList отклонил версию. |
package_manager.lifecycle_scripts_blocked | 4412 | proxy. Только Advanced Logging. HKLM lock lifecycle-скриптов на install-команде. |
network.deadline_exceeded | 4501 | nvm.exe или sync.exe |
malware.definitions_updated | 4601 | sync.exe. Только Advanced Logging. |
malware.package_blocked | 4602 | Shim. Только Advanced Logging. |
firewall.elevation_required | 4404 | nvm.exe |
firewall.invalid_rule | 4405 | nvm.exe |
firewall.untrusted_module_changed | 4406 | nvm.exe или proxy |
firewall.policy_mutated | 4410 | nvm.exe |
firewall.policy_mutate_failed | 4410 | nvm.exe |
package_manager.install | 4403 или 4408 | proxy на Certified Build. 4403 — запрет. 4408 — разрешение. |