Skip to Content
ContributeBackendSecurity Features

Security Features

Backend security controls that are specific to Rhesis. Authentication, authorization, and tenant isolation each have their own page; this page covers the cross-cutting HTTP and data-at-rest controls and links to the rest.

Authentication and authorization

  • Authentication — provider architecture, token system and rotation, password policy, rate limits.
  • Authorization (RBAC) — the single decision point (authorize()) and route backstop (apply_authz_backstop()).

Multi-tenancy and data isolation

Tenant isolation has two layers. The ORM auto-filter stamps and filters every db.query(...) by organization_id (and active project_id), and PostgreSQL row-level security is the backstop for paths that bypass the ORM (raw SQL, ORM 2.0 select()). See Multi-tenancy for the scope model, helpers, and known limitations.

API security

CORS

CORSMiddleware allows only the frontend origin, derived from FRONTEND_URL, with credentials enabled:

main.py
app.add_middleware(
    CORSMiddleware,
    allow_origins=_frontend_settings.cors_origins,  # derived from FRONTEND_URL
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["X-Total-Count", "X-Test-Header"],
)

Security headers

SecurityHeadersMiddleware sets response headers on every request:

main.py
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["X-XSS-Protection"] = "0"  # disabled per modern guidance
# HSTS only over HTTPS:
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"

HTTPSRedirectMiddleware reads X-Forwarded-Proto so the request scheme reflects the proxy’s TLS termination, which is what gates the HSTS header.

Rate limiting

Authentication endpoints are rate-limited per IP (see Authentication).

Database field encryption

Sensitive credentials are encrypted at rest with Fernet (AES-128-CBC + HMAC) via the EncryptedString SQLAlchemy column type, keyed by DB_ENCRYPTION_KEY. Encryption and decryption are transparent at the ORM layer.

Encrypted columns:

TableColumns
endpointauth_token, client_secret, last_token
modelkey (LLM provider API keys)
tokentoken (user-generated API tokens)
toolcredentials (auth data as encrypted JSON)

Ciphertext is useless without DB_ENCRYPTION_KEY, which limits exposure from DB dumps, backups, and many SQL-injection reads. Passwords are a separate case: they are one-way hashed with bcrypt (via passlib), never encrypted.

See Database Field Encryption and Encryption Troubleshooting for key management and migration.

Organization filtering checks

Regression tests and a CI scan guard against cross-tenant leaks in code paths that predate the ambient scope — see Security Improvements.