Sprint completo — prodotto presentation-ready:
Services API (read-only, API Key + scope):
- GET /api/services/status|compliance-summary|risks-feed|incidents-feed
- GET /api/services/controls-status|assets-critical|suppliers-risk|policies-approved
- GET /api/services/openapi (spec OpenAPI 3.0.3 JSON)
Webhook Outbound (Stripe-like HMAC-SHA256):
- CRUD api_keys + webhook_subscriptions (Settings → 2 nuovi tab)
- WebhookService: retry 3x backoff (0s/5min/30min), delivery log
- Trigger auto in IncidentController, RiskController, PolicyController
- Delivery log, test ping, processRetry
Nuovi moduli:
- WhistleblowingController (Art.32 NIS2): anonimato garantito, timeline, token tracking
- NormativeController: feed NIS2/ACN/DORA con ACK tracciato per audit
Frontend:
- whistleblowing.html: form submit anonimo/firmato + gestione CISO
- normative.html: feed con presa visione documentata + progress bar ACK
- public/docs/api.html: documentazione API dark theme (Swagger-like)
- settings.html: tab API Keys + tab Webhook
- integrations/: guide per lg231, SustainAI, AllRisk, SIEM (widget + codice)
- Sidebar: Segnalazioni + Normative aggiunte a common.js
DB: migration 007 (api_keys, webhook_subscriptions, webhook_deliveries),
008 (whistleblowing_reports + timeline),
009 (normative_updates + normative_ack + seed NIS2/ACN/DORA/ISO)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
9.9 KiB
HTML
180 lines
9.9 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="it">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>SIEM × NIS2 Agile — Integrazione</title>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
:root {
|
||
--primary: #06b6d4; --purple: #8b5cf6;
|
||
--gray-200: #e2e8f0; --gray-500: #64748b; --gray-700: #334155;
|
||
--radius: 8px; --font: -apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
|
||
--mono: 'Cascadia Code','Consolas',monospace;
|
||
}
|
||
body { background: #f8fafc; font-family: var(--font); color: #0f172a; }
|
||
.header { background: linear-gradient(135deg, #2e1065, #3b0764); padding: 40px 48px; color: #fff; }
|
||
.header-badges { display: flex; gap: 10px; margin-bottom: 16px; }
|
||
.badge { padding: 4px 12px; border-radius: 20px; font-size: 0.75rem; font-weight: 700; }
|
||
.badge-nis2 { background: rgba(6,182,212,0.2); color: #67e8f9; border: 1px solid rgba(6,182,212,0.3); }
|
||
.badge-siem { background: rgba(139,92,246,0.2); color: #ddd6fe; border: 1px solid rgba(139,92,246,0.3); }
|
||
h1 { font-size: 1.875rem; font-weight: 800; margin-bottom: 8px; }
|
||
.header p { color: #ddd6fe; font-size: 1rem; }
|
||
.container { max-width: 960px; margin: 0 auto; padding: 40px 24px; }
|
||
h2 { font-size: 1.25rem; font-weight: 700; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 2px solid var(--gray-200); }
|
||
.section { margin-bottom: 48px; }
|
||
p { color: var(--gray-500); font-size: 0.9rem; line-height: 1.7; margin-bottom: 12px; }
|
||
pre { background: #1e293b; color: #e2e8f0; padding: 20px; border-radius: var(--radius); font-family: var(--mono); font-size: 0.8125rem; overflow-x: auto; line-height: 1.7; margin: 12px 0; }
|
||
code { background: #f1f5f9; padding: 2px 6px; border-radius: 4px; font-family: var(--mono); font-size: 0.85em; }
|
||
.siem-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; margin-top: 16px; }
|
||
.siem-card { background: #fff; border: 1px solid var(--gray-200); border-radius: var(--radius); padding: 16px; }
|
||
.siem-card h4 { font-size: 0.875rem; font-weight: 700; margin-bottom: 6px; }
|
||
.siem-card p { font-size: 0.8rem; color: var(--gray-500); margin: 0; }
|
||
.info-box { padding: 14px 16px; border-radius: var(--radius); margin: 16px 0; font-size: 0.875rem; }
|
||
.info-purple { background: #f5f3ff; border-left: 3px solid var(--purple); color: #4c1d95; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<div class="header-badges">
|
||
<span class="badge badge-siem">SIEM / SOC</span>
|
||
<span>×</span>
|
||
<span class="badge badge-nis2">NIS2 Agile</span>
|
||
</div>
|
||
<h1>Integrazione SIEM / SOC ← NIS2 Agile</h1>
|
||
<p>Ricevi eventi NIS2 in tempo reale nel tuo SIEM. Incidenti Art.23, rischi HIGH/CRITICAL, variazioni compliance come alert automatici.</p>
|
||
</div>
|
||
|
||
<div class="container">
|
||
|
||
<div class="section">
|
||
<h2>SIEM supportati</h2>
|
||
<div class="siem-grid">
|
||
<div class="siem-card"><h4>Splunk Enterprise</h4><p>HTTP Event Collector (HEC) + custom TA per NIS2 Agile. Dashboard Art.23 precompilata.</p></div>
|
||
<div class="siem-card"><h4>Elastic SIEM</h4><p>Logstash HTTP input + index template NIS2. Alert rule per incident.significant.</p></div>
|
||
<div class="siem-card"><h4>IBM QRadar</h4><p>Universal DSM + webhook-to-syslog bridge. Evento NIS2 → offense QRadar.</p></div>
|
||
<div class="siem-card"><h4>Microsoft Sentinel</h4><p>Logic App webhook receiver → Azure Monitor. Playbook SOAR per Art.23.</p></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2>Configurazione Webhook NIS2 → SIEM</h2>
|
||
<p>Configura in NIS2 Agile → Settings → Webhook l'endpoint del tuo SIEM:</p>
|
||
<pre><span style="color:#7dd3fc;"># SIEM HEC / HTTP Input endpoint</span>
|
||
URL: https://your-siem.example.com:8088/services/collector <span style="color:#7dd3fc;"># Splunk HEC</span>
|
||
URL: https://your-elk.example.com:9200/_ingest/pipeline/nis2 <span style="color:#7dd3fc;"># Elastic</span>
|
||
Events: incident.created, incident.significant, incident.deadline_warning,
|
||
risk.high_created, compliance.score_changed</pre>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2>Splunk HEC — Bridge PHP</h2>
|
||
<p>Se il SIEM non supporta nativamente la firma HMAC-SHA256, usa questo bridge come proxy tra NIS2 e Splunk HEC:</p>
|
||
<pre><span style="color:#7dd3fc;">// nis2_to_splunk_bridge.php — Deploy su server intermedio</span>
|
||
|
||
<span style="color:#f1fa8c;">$nis2Secret</span> = getenv(<span style="color:#86efac;">'NIS2_WEBHOOK_SECRET'</span>);
|
||
<span style="color:#f1fa8c;">$splunkHec</span> = getenv(<span style="color:#86efac;">'SPLUNK_HEC_URL'</span>); <span style="color:#7dd3fc;">// https://splunk:8088/services/collector</span>
|
||
<span style="color:#f1fa8c;">$splunkToken</span> = getenv(<span style="color:#86efac;">'SPLUNK_HEC_TOKEN'</span>);
|
||
|
||
<span style="color:#7dd3fc;">// 1. Verifica firma NIS2</span>
|
||
<span style="color:#f1fa8c;">$body</span> = file_get_contents(<span style="color:#86efac;">'php://input'</span>);
|
||
<span style="color:#f1fa8c;">$sig</span> = $_SERVER[<span style="color:#86efac;">'HTTP_X_NIS2_SIGNATURE'</span>] ?? <span style="color:#86efac;">''</span>;
|
||
if (!hash_equals(<span style="color:#86efac;">'sha256='</span> . hash_hmac(<span style="color:#86efac;">'sha256'</span>, <span style="color:#f1fa8c;">$body</span>, <span style="color:#f1fa8c;">$nis2Secret</span>), <span style="color:#f1fa8c;">$sig</span>)) {
|
||
http_response_code(401); exit;
|
||
}
|
||
|
||
<span style="color:#f1fa8c;">$payload</span> = json_decode(<span style="color:#f1fa8c;">$body</span>, true);
|
||
|
||
<span style="color:#7dd3fc;">// 2. Trasforma in formato Splunk HEC</span>
|
||
<span style="color:#f1fa8c;">$splunkEvent</span> = [
|
||
<span style="color:#86efac;">'time'</span> => <span style="color:#f1fa8c;">$payload</span>[<span style="color:#86efac;">'created'</span>],
|
||
<span style="color:#86efac;">'host'</span> => <span style="color:#86efac;">'nis2-agile'</span>,
|
||
<span style="color:#86efac;">'source'</span> => <span style="color:#86efac;">'nis2-agile-webhook'</span>,
|
||
<span style="color:#86efac;">'sourcetype'</span> => <span style="color:#86efac;">'nis2:event'</span>,
|
||
<span style="color:#86efac;">'index'</span> => <span style="color:#86efac;">'nis2_compliance'</span>,
|
||
<span style="color:#86efac;">'event'</span> => [
|
||
<span style="color:#86efac;">'event_type'</span> => <span style="color:#f1fa8c;">$payload</span>[<span style="color:#86efac;">'event'</span>],
|
||
<span style="color:#86efac;">'event_id'</span> => <span style="color:#f1fa8c;">$payload</span>[<span style="color:#86efac;">'id'</span>],
|
||
<span style="color:#86efac;">'org_id'</span> => <span style="color:#f1fa8c;">$payload</span>[<span style="color:#86efac;">'org_id'</span>],
|
||
<span style="color:#86efac;">'data'</span> => <span style="color:#f1fa8c;">$payload</span>[<span style="color:#86efac;">'data'</span>],
|
||
<span style="color:#86efac;">'api_version'</span> => <span style="color:#f1fa8c;">$payload</span>[<span style="color:#86efac;">'api_version'</span>],
|
||
],
|
||
];
|
||
|
||
<span style="color:#7dd3fc;">// 3. Forward a Splunk HEC</span>
|
||
<span style="color:#f1fa8c;">$ch</span> = curl_init(<span style="color:#f1fa8c;">$splunkHec</span>);
|
||
curl_setopt_array(<span style="color:#f1fa8c;">$ch</span>, [
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => json_encode(<span style="color:#f1fa8c;">$splunkEvent</span>),
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_HTTPHEADER => [
|
||
<span style="color:#86efac;">'Authorization: Splunk '</span> . <span style="color:#f1fa8c;">$splunkToken</span>,
|
||
<span style="color:#86efac;">'Content-Type: application/json'</span>,
|
||
],
|
||
]);
|
||
<span style="color:#f1fa8c;">$result</span> = curl_exec(<span style="color:#f1fa8c;">$ch</span>);
|
||
curl_close(<span style="color:#f1fa8c;">$ch</span>);
|
||
http_response_code(200);</pre>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2>Elastic — Logstash Pipeline</h2>
|
||
<pre><span style="color:#7dd3fc;"># logstash/pipelines/nis2.conf</span>
|
||
input {
|
||
http {
|
||
port => 8181
|
||
codec => json
|
||
<span style="color:#7dd3fc;"># Aggiungi filtro HMAC-SHA256 con plugin custom</span>
|
||
}
|
||
}
|
||
filter {
|
||
mutate {
|
||
add_field => { "[@metadata][index]" => "nis2-compliance-%{+YYYY.MM}" }
|
||
}
|
||
date {
|
||
match => [ "[created]", "UNIX" ]
|
||
target => "@timestamp"
|
||
}
|
||
}
|
||
output {
|
||
elasticsearch {
|
||
hosts => ["https://elasticsearch:9200"]
|
||
index => "%{[@metadata][index]}"
|
||
}
|
||
}</pre>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2>Microsoft Sentinel — Logic App</h2>
|
||
<pre><span style="color:#7dd3fc;">// Azure Logic App — trigger HTTP + azione Send to Log Analytics</span>
|
||
{
|
||
"triggers": {
|
||
"When_a_HTTP_request_is_received": {
|
||
"type": "Request",
|
||
"kind": "Http",
|
||
"inputs": { "schema": {} }
|
||
}
|
||
},
|
||
"actions": {
|
||
"Send_Data_to_Log_Analytics": {
|
||
"type": "ApiConnection",
|
||
"inputs": {
|
||
"body": "@{triggerBody()}",
|
||
"headers": { "Log-Type": "NIS2AgileEvent" },
|
||
"host": { "connection": { "name": "@parameters('$connections')['azureloganalyticsdatacollector']" } },
|
||
"method": "post",
|
||
"path": "/api/logs"
|
||
}
|
||
}
|
||
}
|
||
}</pre>
|
||
</div>
|
||
|
||
<div class="info-box info-purple">
|
||
<strong>Art.23 NIS2 + SIEM:</strong> Configurare alert SIEM su <code>incident.significant</code> e <code>incident.deadline_warning</code> permette di automatizzare il tracking delle scadenze 24h/72h/30d direttamente nel SOC. Il team può così gestire incidenti NIS2 senza uscire dal workflow SIEM.
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|