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
Unless the table says otherwise, the “field” is a field in the response body.
| 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 |
/access/api/v1/tokens | access_token | Artifactory admin access token (CVE-2026-82329) |
/key/generate | key | LiteLLM virtual key |
/key/info | keys[].token | LiteLLM key listing |
/model/info | litellm_params.api_key | Upstream model-provider key |
/api/v4/projects/*/repository/commits | gitlab_rails['initial_root_password'] inside the leaked gitlab.rb | GitLab root password (CVE-2026-85706) |
/api/fmc_platform/v1/auth/generatetoken | response header X-auth-access-token | Cisco FMC API access token (CVE-2026-20079) |
/api/fmc_platform/v1/auth/generatetoken | response header X-auth-refresh-token | Cisco FMC API refresh token |
/api/…/configs (not /api/v1/configs) | taskRunList[].outputs.vars.KESTRA_API_TOKEN | Kestra worker environment leaked by the injected command (CVE-2026-49869) |
/api/v1/*/namespaces/*/kv/* | value | Kestra namespace KV secret |
/ers/config/networkdevice | authenticationSettings.radiusSharedSecret | Cisco ISE RADIUS shared secret (CVE-2026-76460 target) |
/ers/config/internaluser, /ers/config/adminuser | password | Cisco ISE internal identity-store password |
/j_security_check | Set-Cookie: JSESSIONID | Cisco SD-WAN Manager session (CVE-2026-76504) |
/dataservice/client/token | response body | Cisco SD-WAN Manager CSRF token |
/client-registration/* | clientSecret | WSO2 consumer secret (CVE-2026-5430 target) |
/api/am/* | endpoint_security.production.password | WSO2 backend endpoint password |
/~/api/loginSrp1 | Set-Cookie: hfs_$id | Rejetto HFS session cookie (CVE-2026-61500 target) |
/~/api/get_config | upload_token | Rejetto HFS config credential read with a forged admin session |
/app?service=…Setup… | Set-Cookie: JSESSIONID | PaperCut session after the setup-wizard bypass |
They all mimic the format of real credentials that automated scanners and credential-harvesting tools look for specifically.
The Langflow, Metabase, N-central, Artifactory, LiteLLM, FMC, Kestra, ISE, SD-WAN, WSO2, HFS and PaperCut 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.
Cisco FMC is the first carrier that lives in a response header rather than a body, because that is where a real FMC puts the credential: the token endpoint answers
204 No Contentand returns nothing else. It gets two distinct tokens — one forX-auth-access-token, one forX-auth-refresh-token— so a replayed access token can be told apart from a replayed refresh token when thehoneytoken_usedevent fires.
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