Honeytokens
Honeytokens are fake credentials embedded in the honeypot’s fake responses. When an attacker steals them and later reuses them — even from a different IP, days later — the honeypot detects and alerts on the reuse.
Table of contents
- What are honeytokens?
- Token format
- Where tokens are embedded
- Detection
- What happens on detection
- n8n routing for honeytoken events
- Prometheus metric
What are honeytokens?
A honeytoken is a fake credential that looks real enough to fool automated tools, but is unique to the honeypot and functionally useless. The goal is not to protect anything, but to detect post-theft behaviour: did the attacker actually try to use what they stole?
This is high-signal detection. A scanner blindly hitting your honeypot is background noise. An attacker sending back a token they received from your honeypot means:
- They collected credentials from your fake endpoint
- They tried to authenticate with them somewhere
- You now know their attack IP, user agent, and timing
Token format
Every token follows the pattern:
hp_live_{md5(ip + "-" + trap_name)[:20]}
Examples:
hp_live_a1b2c3d4e5f6789012ab(from/.envfor IP1.2.3.4)hp_live_f09e8d7c6b5a432109de(from/actuator/envfor the same IP)
The hp_live_ prefix is recognizable at a glance. The 20-hex suffix is deterministic per IP + trap, so if an attacker from 1.2.3.4 steals your fake AWS key and then 5.6.7.8 sends it back, you can trace the token back to the original theft.
Where tokens are embedded
| Trap | Field | Token used as |
|---|---|---|
/actuator/env | AWS_SECRET_ACCESS_KEY | AWS secret key |
/.env | STRIPE_SECRET_KEY | Stripe live key |
/.aws/credentials | aws_secret_access_key | AWS secret key |
/api/v*/users/{id} | api_key | REST API key |
/api/v1/api_key | api_keys[].api_key | Langflow API key |
/api/v1/auto_login | access_token | Langflow SUPERUSER JWT (CVE-2026-9198) |
/api/session/properties | setup-token | Metabase setup token (CVE-2023-38646 chain) |
/api/database | data[].details.password | Password of a connected database (CVE-2026-72898 target) |
/p/u/doAuthentication.do | <InitialValue> | NetScaler session material in fake leaked memory |
/global-protect/getconfig.esp | <portal-userauthcookie> | GlobalProtect portal auth cookie |
/.claude/mcp.json and friends | env.GITHUB_PERSONAL_ACCESS_TOKEN | GitHub PAT in an MCP server config |
/.claude/.credentials.json | claudeAiOauth.accessToken | AI assistant OAuth token |
/api/auth/authenticate | tokens.access.token | N-central admin bearer token |
They all mimic the format of real credentials that automated scanners and credential-harvesting tools look for specifically.
The Langflow, Metabase and N-central carriers sit on endpoints an attacker reaches through an authentication bypass. The bypass appears to succeed, the attacker replays the token on the next request, and
detectHoneytokenInRequestcatches it — so the whole chain is observable end to end.
Detection
The honeypot scans every incoming request for honeytoken reuse:
- All request headers — including
Authorization: Bearer hp_live_...,X-Api-Key: hp_live_...,Cookie: NSC_AAAC=hp_live_..., and any custom header - Query parameters — e.g.
?api_key=hp_live_... - POST/PUT body — scanned after the standard 8 KB body read
- Already-captured API key — from
captureAPIKey()which readsX-Api-KeyandAuthorization
Header, cookie and query values are split on every delimiter that can surround a credential (whitespace, =, &, ;, ,, :, quotes, brackets), so a token pasted into a cookie or a JSON field is still found.
Detection is prefix-based (hp_live_) plus exact length check (28 characters total), so false positives are essentially impossible.
What happens on detection
When a token is detected, the honeypot immediately (before path routing):
- Sets
attack_tag = "honeytoken-used"andis_honeytoken_use = true - Fires a
honeytoken_usedwebhook event — a separate event type from"attack" - Increments
http_honeytokens_usedPrometheus counter - Adds the IP to the blacklist log
- Reports to AbuseIPDB (if configured)
- Logs
HONEYTOKEN USED by {ip}: {token}to stdout - Still responds normally — the attacker has no indication they were detected
n8n routing for honeytoken events
Filter on the event field:
Webhook node
└─ Switch on event
├─ "honeytoken_used" → 🚨 Slack #security-critical + PagerDuty + block IP
├─ "attack" → Normal processing
└─ "country_notify" → Pushover (already sent)
To correlate original theft with reuse:
// In n8n Code node
const token = $json.api_key_used; // e.g. "hp_live_a1b2c3d4e5f6789012ab"
const thiefIP = $json.ip;
// Search your logs for the original request that returned this token
// (it will have the same token in the response body)
Prometheus metric
# Total honeytoken reuse events
http_honeytokens_used
# Rate over last hour
rate(http_honeytokens_used[1h]) * 3600