Leaked embeddings are leaked text: the RAG risk nobody checks
Most RAG security talk is about prompt injection. Here's a risk almost nobody checks: the embedding vectors themselves. Embeddings are not a one-way hash It's tempting to treat an embedding as a safe, anonymi
Most RAG security talk is about prompt injection. Here's a risk almost nobody checks: the embedding vectors themselves.
Embeddings are not a one-way hash
It's tempting to treat an embedding as a safe, anonymized fingerprint of your text. It isn't. Recent work (vec2text, Morris et al., 2023) showed you can invert an embedding back into much of its original text. The attack is simple in spirit: start from a guess, embed it, compare to the target vector, and iteratively edit the text until its embedding matches. Given the vector, the decoder reconstructs a large chunk of what you embedded, often near verbatim for short passages.
So an embedding is as sensitive as the source document it encodes. If your pipeline hands out raw vectors anywhere, it is leaking the content those vectors came from, even if the text never leaves the box.
What the leak actually looks like
The dangerous part is that it never looks like a breach. Here's a "helpful" debug response from a RAG API:
{
"answer": "Our refund window is 30 days.",
"debug": {
"retrieved_chunks": [
{
"source": "internal/refund-policy.md",
"embedding": [0.0123, -0.0917, 0.0442, 0.1131, -0.0075, 0.0881, -0.0210, 0.0559]
}
]
}
}
There's no obvious secret there, just a list of floats. But that embedding array can be inverted back into the chunk it came from. If that chunk was private, you just shipped it to the client in a field nobody thought to guard.
RAG pipelines expose vectors in more places than you'd think:
- A debug or verbose mode that includes the embedding in the response
- Logs that dump the query or chunk vector while troubleshooting
- API metadata that returns the vector alongside the answer
- A vector store or admin endpoint with weak access control
The fix is boring and effective
- Never return raw embeddings to clients. Strip them from responses and debug output.
- Keep vectors out of logs. Log an ID or a hash, not the vector.
- Treat your vector store like a datastore full of sensitive text, because that is what it is. Access-control it.
- Access-control any debug endpoint that can surface vectors.
How to check your own pipeline
The zero-effort version: grep your logs and captured API responses for long runs of floats.
grep -RnE '\[-?[0-9]+\.[0-9]+(, *-?[0-9]+\.[0-9]+){7,}' ./logs
If that finds anything a user or an attacker could reach, treat it like you found a password in there. Because functionally, you did.
I added a probe for exactly this to rag-redteam in v0.3. It asks a pipeline for its vectors a few different ways and flags any response that actually contains a raw embedding. It's one of seven probes that test the retrieval pipeline itself, not the model, for injection and leakage, and it runs as a CI gate:
pip install rag-redteam
rag-redteam run --target mypackage.my_rag:build --probes embedding_inversion
Repo and threat model: https://github.com/Srivatsa03/rag-redteam
Prompt injection gets all the attention, but your embeddings are quietly carrying the same text you were trying to protect. Check where they end up.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.