Your AI Agent Knows Your Passwords — Here's How I Fixed It
Source: Dev.to
Overview
When AI agents automate your browser, they need your login credentials in their context window.
That means your passwords are:
- Sent to cloud LLM providers
- Stored in conversation logs
- Potentially exposed via prompt‑injection attacks
I built Cerberus KeyRouter to fix this.
The Problem
AI agents such as OpenClaw, Claude Desktop, and Cursor can automate browsers—filling forms, clicking buttons, navigating pages.
When they hit a login page, the typical flow looks like this:
User: "Log into GitHub for me"
User: "Email: jacob@example.com"
User: "Password: MyS3cretP@ss"
Agent: "Logging in..."
That password travels through:
- LLM provider’s servers (Anthropic, OpenAI, …) – logged, stored, possibly used for training.
- API proxy services – if you use a third‑party relay, the password passes through another unknown middle‑man.
- Conversation history – sitting in plain text in your chat logs.
Prompt‑injection risk
A malicious webpage can embed hidden instructions that trick the AI into leaking credentials it learned earlier in the conversation.
Core Idea
- The AI writes logic using placeholders.
- Real credentials are substituted locally and injected directly into the browser.
- The LLM never sees the actual password.
Example of what the AI sends:
{
"vaultItem": "GitHub",
"steps": [
{ "action": "fill", "selector": "#login_field", "value": "{{email}}" },
{ "action": "fill", "selector": "#password", "value": "{{password}}" },
{ "action": "click", "selector": "[type=submit]" }
]
}
Cerberus KeyRouter – an MCP (Model Context Protocol) server running on your machine – intercepts this call, fetches the real credentials from your local Vaultwarden instance, replaces the {{placeholders}}, and executes the actions via Chrome DevTools Protocol (CDP).
AI Agent (OpenClaw, Claude Desktop, …)
│
│ MCP call: secure_login("GitHub", steps with {{placeholders}})
▼
Login Router (localhost:8899)
├─ Authenticate via bearer token
├─ Fetch credentials from Vaultwarden
├─ Replace {{placeholders}} with real values
├─ Execute via Chrome CDP (localhost only)
├─ Clear credentials from memory
└─ Return { status: "ok" } ← no passwords in response
Vaultwarden (Docker, localhost:8443) provides end‑to‑end encrypted password storage. Everything runs locally; passwords never leave your machine.
Security Features
| # | Feature | Description |
|---|---|---|
| 1 | Implicit allowlist | Vaultwarden only stores credentials for sites you explicitly add. The AI cannot log into arbitrary sites. |
| 2 | URL verification | Before injecting credentials, the router reads the browser’s actual URL via CDP and matches it against the vault entry’s URI. Phishing sites are rejected. |
| 3 | Bearer‑token auth | Each Vaultwarden account gets a unique token. No token → no access. |
| 4 | Rate limiting | 3 attempts per minute, 20 per hour per vault item. Consecutive failures trigger a cooldown. |
| 5 | Audit logging | Every login attempt (success, failure, rate‑limited) is recorded. Passwords are never logged. View at /audit. |
| 6 | Human‑in‑the‑loop | High‑risk accounts can require manual approval before each login (50 s timeout, auto‑reject if not approved). |
Quick Start
git clone https://github.com/DemoJacob/cerberus-keyrouter.git
cd cerberus-keyrouter
cp .env.example .env
# Edit .env and set VW_ADMIN_TOKEN
docker compose up --build -d
What you get
| Service | URL | Purpose |
|---|---|---|
| Vaultwarden | (self‑hosted Bitwarden) | Add your passwords here. |
| Login Router | (MCP server + admin panel) | Handles credential substitution and injection. |
| Admin panel | (same host) | Add your Vaultwarden account, generate a bearer token. |
Connect your AI agent via MCP
{
"mcpServers": {
"cerberus": {
"baseUrl": "http://localhost:8899/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
}
Now the agent can log into any site stored in your vault without ever seeing a plaintext password.
Interaction Patterns
Simple fill
Most sites work with the fill action (sets value + dispatches events).
React / SPA sites
If a site uses controlled components, switch to type (key‑by‑key input with ~50 ms delay).
Multi‑step logins (e.g., banks)
// Step 1: username + next
{
"vaultItem": "MyBank",
"steps": [
{ "action": "type", "selector": "#username", "value": "{{username}}" },
{ "action": "click", "selector": "#next" },
{ "action": "wait", "selector": "input[type=password]" }
]
}
// Step 2: password + submit
{
"vaultItem": "MyBank",
"steps": [
{ "action": "type", "selector": "#password", "value": "{{password}}" },
{ "action": "click", "selector": "#login" }
]
}
Real‑world Example
Running with OpenClaw for daily tasks, I logged into zooplus.de to query order history. The AI reported:
“I didn’t touch any plaintext passwords. I only passed the placeholders
{{email}}and{{password}}. Cerberus fetched the credentials from my local Vaultwarden vault and filled them directly into the browser.”
- No password in the LLM context.
- No password in the logs.
- No password left my machine.
Technical Stack
| Component | Technology |
|---|---|
| Login router | TypeScript / Node.js |
| Browser automation | playwright‑core (CDP) |
| Password store | Vaultwarden (self‑hosted Bitwarden) |
| Transport | MCP Protocol – streamable HTTP |
| Persistence | SQLite (config + audit log) |
| Encryption | AES‑256‑GCM (master‑password encryption) |
| Deployment | Docker Compose (one‑command) |
| Extras | Cookie caching, OpenClaw snapshot‑ref IDs, multi‑agent framework support, SaaS version with zero‑knowledge E2E encrypted backend |
License & Contributions
The project is open source under AGPL‑3.0.
- GitHub: (link omitted in original)
- Works on macOS and Linux (Docker + Chrome + any MCP‑compatible AI agent).
Feedback, issues, and stars are all welcome!
Come. If you're building AI agents that need to handle authentication, I'd love to hear about your use case.