Skip to content

Security: diagonalciso/wazuh-attackmap

Security

SECURITY.md

Security

⚠️ BETA / PROOF-OF-CONCEPT

This is a read-only visualization toy, not a hardened or audited product. Run it on trusted internal networks only. No warranty.

What it is / isn't (threat model)

  • It is a read-only viewer. It issues only _search (read) calls to the local wazuh-indexer and never writes anything back to Wazuh.
  • It holds no persistent state — an in-memory ring buffer of recent events, lost on restart. No database, no user accounts, no uploads.
  • It takes no untrusted input that reaches a sink: the only request inputs are a clamped integer (recent?limit=N) and the static routes. There is no user-controlled file path, command, or outbound URL.
  • Its data source (wazuh-alerts-*) is trusted Wazuh output. Alert string fields (IP, country, agent, type) are HTML-escaped before display.

Hardening built in

  • Read-only indexer account. Use an account scoped to read wazuh-alerts-* only — never admin. The service refuses to start without INDEXER_PASS.
  • HTTPS. Set MAP_TLS_CERT + MAP_TLS_KEY to serve TLS 1.2+; plain HTTP is then refused. Setting only one fails safe (won't start).
  • Security headers on every response: Content-Security-Policy (locked to same-origin; frame-ancestors configurable), X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: no-referrer, and HSTS when served over TLS.
  • Output encoding. All alert-derived strings are HTML-escaped (incl. quotes); colours are validated against a hex pattern before use in a style attribute.
  • Bind control. MAP_BIND (default 0.0.0.0) — set 127.0.0.1 to restrict to loopback and front with a reverse proxy.
  • No dependencies. Pure Python 3 stdlib — no third-party packages, so no supply-chain surface.
  • Least privilege. Shipped systemd unit runs as a dedicated unprivileged account with NoNewPrivileges=true.

Test lab vs production posture

Defaults are tuned for a test lab / SOC wall on a trusted internal network: no page auth, MAP_BIND=0.0.0.0, HTTP unless you set a cert. That keeps it one-command to stand up and view from another machine.

For production / any wider exposure, best practice is the opposite end:

Setting Lab default Production best practice
Auth none front with an authenticating reverse proxy (basic-auth / SSO / mTLS)
Bind 0.0.0.0 MAP_BIND=127.0.0.1, proxy in front; or firewall the port
Transport HTTP HTTPS (MAP_TLS_CERT/MAP_TLS_KEY)
Indexer TLS verify off (localhost) INDEXER_CA set

None of the production steps are wired on by default on purpose — they get in the way of a quick lab. Turn them on before the map is reachable by anyone you don't trust. Never expose it to the internet without a proxy + auth.

Known limitations / accepted trade-offs (it's a POC)

  • No authentication on the map page. TLS encrypts transport but does not authenticate viewers. Bind to loopback / an internal interface and/or front it with an authenticating reverse proxy. Do not expose it to the internet.
  • CSP allows 'unsafe-inline'. The page is a single self-contained file with inline JS/CSS and no CDN. Removing 'unsafe-inline' would require hashing every inline block — deferred for the POC.
  • Indexer TLS verification is off by default (when INDEXER_CA is unset), appropriate for a 127.0.0.1 self-signed indexer. Set INDEXER_CA to verify. The service logs a warning at boot when verification is disabled.
  • Cert is a copy. Reusing the Wazuh dashboard cert means copying the cert+key to a service-readable path; re-copy on rotation.
  • No rate limiting / connection cap. Many concurrent SSE clients = one thread each (thread-per-connection server). Fine behind a proxy on an internal network; not intended for hostile exposure.

Reporting a vulnerability

This is an internal proof-of-concept. If you find a security issue, open a private issue / contact the maintainer directly — please do not post exploit details in a public channel. There is no formal SLA.

Provenance / release hygiene

This repository ships no real hostnames, IPs, or credentials. Verified before the first public release: working tree + full git log --all -p history scanned clean — only <password> / CHANGE_ME placeholders and generic RFC1918 / doc examples are present, the demo sample IPs are public generic examples (not customer data), and the sensor "home" coordinates are a neutral placeholder. Licensed under MIT.

There aren't any published security advisories