Zum Inhalt springen
CASOON

nosecrets unter der Haube: Architektur, Regeln und Entropy-Detection

Wie die Detection Pipeline aufgebaut ist, welche Regeln greifen und warum Entropy-Detection den Unterschied macht

12 Minuten
nosecrets unter der Haube: Architektur, Regeln und Entropy-Detection
#Security #Rust #Open Source #Git
Serienosecrets
Teil 2 von 2

Unter der Haube

Im ersten Teil ging es darum, was nosecrets ist, warum es existiert und wie man es einrichtet. Dieser Artikel geht tiefer: Wie funktioniert die Detection Pipeline? Welche Regeln greifen und warum? Und was hat es mit der Entropy-Detection auf sich?

Architektur: Fünf spezialisierte Crates

nosecrets ist als Rust Workspace mit fünf Crates aufgebaut. Jeder Crate hat eine klare Verantwortung:

1
nosecrets-cli CLI mit clap, Subcommands scan/ignore
2
nosecrets-core Detection Engine, Aho-Corasick + Regex + Entropy
3
nosecrets-rules 629 Zeilen TOML-Regeln, compiled into binary
4
nosecrets-filter Pfad-Ignore, Allowlists, Inline-Comments
5
nosecrets-report Deduplizierung, Fingerprinting, Terminal/JSON Output

nosecrets-core ist das Herzstück. Die Detection Pipeline verarbeitet jede Datei in mehreren Stufen:

  1. Pfad-Filter — passt die Datei zu einem Ignore-Pattern?
  2. Keyword-Prefilter — Aho-Corasick sucht nach Schlüsselwörtern in einem Durchlauf
  3. Regex-Matching — nur Regeln, deren Keywords gefunden wurden, werden angewendet
  4. Struktur-Validierung — Charset, Länge, Prefix-Constraints prüfen
  5. Allowlist-Check — Pattern- und Value-Allowlists auf Config- und Regel-Ebene
  6. Inline-Comments@nosecrets-ignore oder @nsi im Code
  7. Fingerprint-Check — Content-Hash in .nosecretsignore
  8. Entropy-Detection — Shannon-Entropie für unbekannte Tokens

Der Aho-Corasick-Prefilter ist entscheidend für die Performance. Statt jede Regex gegen jede Zeile zu testen, prüft er in einem einzigen Durchlauf, welche Keywords überhaupt vorkommen. Nur die zugehörigen Regeln werden dann mit der teuren Regex ausgewertet.

Was erkannt wird

nosecrets hat über 60 Regeln in sieben Kategorien, alle als TOML spezifiziert und zur Build-Zeit in das Binary kompiliert:

Cloud Provider — AWS Access Keys (AKIA/ABIA/ACCA/ASIA Prefix mit exakt 20 Zeichen), GCP API Keys (AIza-Prefix), Azure Storage Keys, Cloudflare API Tokens, DigitalOcean Tokens.

Code-Plattformen — GitHub PATs (ghp_), Fine-Grained PATs (github_pat_), GitLab PATs (glpat-), npm Tokens, PyPI Tokens, Slack Tokens (xoxp/xoxb/xoxo/xoxa/xoxr/xoxs), Discord Tokens.

Deployment — Netlify, Fly.io (fo1_, fm1_, fm2_), Heroku, Vercel, Railway, Render, Supabase.

Payment — Stripe Secret Keys (sk_live_), Restricted Keys (rk_live_), Webhook Secrets (whsec_), PayPal, Square.

Datenbanken — PostgreSQL, MySQL, MongoDB, Redis und SQL Server Connection URIs mit Passwort-Extraktion. JDBC Connection Strings.

Kommunikation — SendGrid (SG.-Prefix), Twilio, Mailchimp, Mailgun.

Generisch — Private Keys (PEM: RSA, EC, DSA, OpenSSH, PGP), generische API-Key und Secret-Patterns, Password Assignments, Basic Auth Header.

Entropy-Detection: Das Unbekannte finden

Regelbasierte Erkennung hat eine Grenze: Sie findet nur, was sie kennt. Ein API-Key eines obskuren SaaS-Dienstes, der kein bekanntes Prefix hat, rutscht durch.

Dafür gibt es die Entropy-Detection. Sie berechnet die Shannon-Entropie eines Tokens — ein Maß dafür, wie zufällig ein String ist. Ein echtes Secret hat typischerweise hohe Entropie (viele verschiedene Zeichen, kein erkennbares Muster). Ein normaler Variablenname oder ein Platzhalter hat niedrige Entropie.

[entropy]
enabled = true
min_length = 20
threshold = 4.2
require_context = true

require_context ist der wichtigste Parameter. Wenn aktiviert, werden High-Entropy-Strings nur gemeldet, wenn sie in der Nähe von secret-relevanten Variablennamen stehen: secret, token, key, auth, password, credential, bearer, api_key, private, access_token, refresh_token.

Das reduziert False Positives drastisch. Ohne Context-Requirement würde jeder Base64-String, jeder lange Hash und jede UUID gemeldet. Mit Context werden nur Strings geflaggt, die wie Secrets zugewiesen werden.

Zusätzlich filtert die Entropy-Detection bekannte Platzhalter (example, changeme, your_token, todo, fixme, dummy, sample, mock), reine Hex-Strings, UUIDs und bekannte Formate automatisch aus.

Konfiguration für Fortgeschrittene

Installation und Grundkonfiguration stehen im ersten Teil. Hier die erweiterten Möglichkeiten:

Entropy-Konfiguration in .nosecrets.toml

[entropy]
enabled = true
min_length = 20
threshold = 4.2
require_context = true

[entropy.allow]
patterns = ["^[a-f0-9]{32,}$"]

Allowlists auf Regel-Ebene

Neben der globalen Allowlist in .nosecrets.toml können einzelne Regeln eigene Allowlists haben — definiert direkt in der TOML-Regel. Das ist nützlich für Provider-spezifische Testkeys, die ein bekanntes Suffix haben.

Performance

Die Performance kommt aus der Kombination von drei Optimierungen:

Aho-Corasick Prefilter — alle Keywords aller Regeln werden in einem einzigen Automaten zusammengefasst. Ein Durchlauf durch den Text reicht, um zu wissen, welche Regeln überhaupt relevant sind.

Parallele Verarbeitungrayon verteilt die Dateiverarbeitung auf alle verfügbaren CPU-Kerne. Bei einem Scan von 100 Dateien arbeiten alle Kerne gleichzeitig.

Lazy Regex Compilation — Regexes werden nur einmal kompiliert und wiederverwendet. once_cell sorgt dafür, dass die Initialisierung thread-safe und ohne Lock-Contention passiert.

Das Ergebnis: Startup unter 20ms, Durchsatz über 200 MB/s, Speicherverbrauch unter 50 MB. In der Praxis bedeutet das: Der Pre-Commit-Hook ist schneller fertig, als die Git-UI den Status aktualisiert.

Regelformat

Eigene Regeln können als TOML definiert werden:

[[rule]]
id = "my-internal-token"
name = "Internal API Token"
severity = "high"
pattern = '''\b(itk_[a-zA-Z0-9]{32})\b'''
keywords = ["itk_"]
capture = 1

[rule.validate]
prefix = ["itk_"]
charset = "a-zA-Z0-9"
length = 36

[rule.allow]
patterns = ["EXAMPLE$", "TEST$"]

Das Format ist bewusst einfach gehalten. keywords sind die Aho-Corasick-Hints — wenn keines der Keywords in der Datei vorkommt, wird die Regex gar nicht erst ausgewertet. capture gibt an, welche Capture Group das eigentliche Secret enthält. validate prüft strukturelle Constraints nach dem Regex-Match.

Warum Rust

Drei Gründe:

Performance ohne Kompromisse. Ein Pre-Commit-Hook muss schnell sein. Python-basierte Scanner haben spürbare Startzeiten. Rust hat keine Runtime, kein GC, keine Interpreter-Overhead.

Plattformübergreifende Binaries. Ein einziges Build erzeugt native Binaries für macOS (Intel + ARM), Linux und Windows. Das npm-Paket liefert das passende Binary automatisch aus.

Korrektheit. Regex-Handling, Unicode, Parallelität — Rust’s Type-System und Ownership-Modell verhindern ganze Fehlerklassen, die in anderen Sprachen manuell abgesichert werden müssen.

Einordnung

nosecrets ist kein Ersatz für CI-basierte Security-Scans, SAST-Tools oder manuelle Code-Reviews. Es ist die erste Verteidigungslinie: ein schneller, offline-fähiger Check, der die offensichtlichsten Fehler abfängt, bevor sie das Repository erreichen.

Der Fokus auf Pre-Commit statt History-Scanning ist eine bewusste Entscheidung. Wenn ein Secret im Commit verhindert wird, muss man die History nicht bereinigen. Prävention statt Forensik.

Das Tool ist Open Source, die Regeln sind erweiterbar, und Contributions — besonders neue Erkennungsregeln — sind willkommen: github.com/casoon/nosecrets