OWASP LLM01 defense via structural enforcement, not LLM promises.
Deinject is a Rust-based HTTP proxy that protects AI agents from prompt injection attacks using cryptographic verification and multi-layer enforcement. Unlike existing defenses that rely on the LLM respecting rules, Deinject enforces security at the proxy layer—outside the LLM's reasoning—making it immune to jailbreaking, adversarial suffixes, and encoding-based attacks.
- Quantum-safe cryptography: ML-DSA-65 (NIST FIPS 204) master keys with HMAC-SHA256 session keys
- Three enforcement layers: Inbound normalization, outbound action policy, output scanning
- Deterministic exfiltration detection: Canary tokens with zero false positives
- Inter-agent taint propagation: Signed HTTP headers prevent SEMANTIC-WORM attacks
- Zero-latency streaming: Text tokens pass through immediately; only tool_calls are buffered
- Comprehensive attack coverage: Unicode steganography, base64/URL/hex encoding, model control tokens, multimodal injection, and more
╔══════════════════════════════════════════════════════════════════╗
║ DEINJECT PROXY (single Rust binary) ║
║ ║
║ INBOUND PIPELINE (→ LLM) OUTBOUND PIPELINE (← LLM) ║
║ ───────────────────────── ────────────────────────── ║
║ 1. Normalize + truncate Layer 2 — Action ║
║ 2. Pre-decode encodings 9. Buffer SSE tool_calls ║
║ 3. Strip fake DEINJECT tags 10. Evaluate capability ║
║ 4. Classify message roles policy + taint state ║
║ 5. Sign trusted (HMAC-SHA256) 11. Block violations ║
║ 6. Sandbox untrusted + Layer 3 — Output ║
║ inject canary tokens 12. Scan for canary tokens ║
║ 7. Taint multimodal content 13. Scan sensitive patterns ║
║ 8. Inject enforcement (2x pos) 14. Forward clean output ║
║ ║
║ INTER-AGENT ║
║ 15. Outbound: X-Deinject-Taint (ML-DSA-65 signed, if tainted) ║
║ 16. Inbound: verify taint token → propagate taint state ║
╚══════════════════════════════════════════════════════════════════╝
- Rust 1.70+ (install via rustup)
- macOS, Linux, or Windows
git clone https://github.com/acidrums7/deinject.git
cd deinject
cargo build --releaseThe binary will be at target/release/deinject.
deinject init --keygenThis creates .deinject.toml in the current directory and generates an ML-DSA-65 master key in your OS keychain.
deinject serveThe proxy listens on http://127.0.0.1:7777 by default.
Point your agent's OpenAI-compatible API client to the proxy:
# Python example
import openai
client = openai.OpenAI(
base_url="http://127.0.0.1:7777/v1",
api_key=os.environ["OPENAI_API_KEY"]
)// Node.js example
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'http://127.0.0.1:7777/v1',
apiKey: process.env.OPENAI_API_KEY,
});All /v1/chat/completions requests will be processed through Deinject's security layers.
Edit .deinject.toml to customize behavior:
[proxy]
listen_port = 7777
mode = "env" # "env" | "system" | "transparent"
upstream_url = "https://api.openai.com"
[trust]
policy = "standard" # "standard" | "paranoid"
taint_window = 0 # 0 = full session, N = last N turns
[keys]
label = "deinject-main" # OS keychain label
[tools]
trusted = ["bash", "file_write", "git"]
untrusted = ["web_fetch", "read_email", "read_file"]
[capabilities]
safe_always = ["calculator", "get_time", "search_docs"]
deny_when_tainted = [
"send_email", "http_post", "execute_shell",
"web_fetch", "git_push"
]
require_approval_when_tainted = [
"write_file", "read_private_file"
]
[output_scanning]
sensitive_patterns = [
"sk-[A-Za-z0-9]{48}", # OpenAI keys
"sk-ant-[A-Za-z0-9\\-]+", # Anthropic keys
"AKIA[A-Z0-9]{16}", # AWS keys
"-----BEGIN [A-Z ]+ KEY-----" # PEM blocks
]
max_untrusted_bytes = 65536 # 64KB truncation# Generate a new ML-DSA-65 master key
deinject keygen --label my-key
# Initialize config (optionally with keygen)
deinject init --keygen
# Start the proxy
deinject serve
# Check status
deinject status| Attack Vector | Defense Mechanism | Status |
|---|---|---|
| Injected instructions in tool results | Sandboxed in [DEINJECT:UNTRUSTED] blocks |
✓ |
| LLM ignores trust labels | Blocked by L2 capability policy | ✓ |
Fake [DEINJECT:TRUSTED] tags |
Stripped before sandboxing (Step 3) | ✓ |
| HMAC replay attack | Nonce + timestamp validation | ✓ |
| Forged HMAC | Computationally infeasible (HMAC-SHA256) | ✓ |
| Quantum computer attacks | ML-DSA-65 is post-quantum safe | ✓ |
| Unicode steganography | Stripped (Tag blocks, zero-width, RTL) | ✓ |
| Base64/URL/hex encoding | Pre-decoded then sandboxed | ✓ |
| Model control token smuggling | Stripped (`< | im_start |
| Text exfiltration | Canary token detection (deterministic) | ✓ |
| Sensitive data in output | Pattern matching + redaction | ✓ |
| SEMANTIC-WORM / multi-agent | ML-DSA-65 signed taint propagation | ✓ |
| Multimodal injection (images/PDFs) | Taint on presence from untrusted source | ✓ |
| GCG adversarial suffixes | L2 blocks regardless of LLM intent | ✓ |
| Crescendo escalation | Taint accumulates monotonically | ✓ |
| Many-shot jailbreaking | Dual-position enforcement + L2 | ✓ |
| Context window overflow | 64KB truncation + dual injection | ✓ |
- Latency overhead: ~1ms p50, ~3ms p99 (64KB worst case)
- Streaming: Zero latency for text tokens (passthrough)
- Tool calls: Buffered until complete for policy evaluation
GPT-4o first-token latency is 200–2000ms. Deinject adds <0.5% overhead.
- Normalize: Strip Unicode steganography, truncate to 64KB
- Pre-decode: Multi-pass base64/URL/hex decoding (untrusted only)
- Strip fakes: Remove any
[DEINJECT:tags from untrusted content - Classify: System/trusted tools → sign; untrusted tools/user → sandbox
- Sign trusted: HMAC-SHA256 with nonce + timestamp
- Sandbox untrusted: Wrap in
[DEINJECT:UNTRUSTED]with canary token - Taint multimodal: Mark session tainted if images/PDFs from untrusted source
- Inject enforcement: Dual-position rules (start + end of system prompt)
Layer 2 — Action Enforcement
- Buffer tool_calls until complete
- Evaluate against capability policy + taint state
- Block/approve/require-approval per tool
Layer 3 — Output Scanning
- Scan for canary tokens (exfiltration detection)
- Redact sensitive patterns (API keys, PEM blocks)
When a tainted session makes an outbound HTTP request, Deinject attaches:
X-Deinject-Taint: ML-DSA-65 signature over payloadX-Deinject-Taint-Pubkey: Public key (base64)X-Deinject-Taint-Payload: JSON payload (base64)
Receiving Deinject proxies verify the signature and propagate taint state.
In scope:
- Prompt injection via external data (tool results, emails, web pages)
- Encoding-based smuggling (base64, URL, hex, Unicode)
- Model control token injection
- Silent exfiltration via text responses
- Multi-agent attack propagation
- Multimodal injection (images, PDFs)
Out of scope:
- Compromised LLM provider (use local models)
- Compromised OS keychain (limits scope to single agent)
- Side-channel attacks (timing, power analysis)
- Physical access to the proxy host
cargo test --workspaceRUST_LOG=debug deinject servedeinject/
├── crates/
│ ├── deinject-core/ # Crypto, normalization, policy
│ ├── deinject-proxy/ # HTTP proxy server
│ └── deinject-cli/ # CLI binary
├── docs/
│ └── plans/ # Design documents
└── Cargo.toml # Workspace config
MIT — see LICENSE
- OWASP LLM01: Prompt Injection
- NIST FIPS 204: ML-DSA
- s1ngularity attack (Aug 2025)
- Promptfoo: Base64 encoding makes LLMs more vulnerable
Contributions welcome! Please open an issue or PR.
- GitHub Issues: https://github.com/acidrums7/deinject/issues
- Security issues: Email maintainers privately (see SECURITY.md)