Open-Source Wikis

/

Bitwarden Server

/

Security

bitwarden/server

Security

Bitwarden Server is a security product. This page captures the trust boundaries, the cryptographic invariants, and the most important guardrails that every contributor needs to keep in mind.

Trust model

The server is never trusted with plaintext vault data. Cipher payloads, send payloads, organization key material, and 2FA secrets are encrypted on the client and decrypted on the client. Server-side code must:

  • Treat ciphertext as opaque. Don't attempt to parse, fingerprint, or "validate" ciphertext beyond size limits and JSON well-formedness.
  • Never log secret material. Don't log master-password hashes, master keys, user keys, send keys, 2FA secrets, encrypted attachments, or anything that could feed an offline attack.
  • Use constant-time comparisons. CryptographicOperations.FixedTimeEquals for password / token comparisons.
  • Trust the data-protection key, not application code. Anything that needs server-issued tokens uses Tokenable<T> so the payload is authenticated by data-protection.

Reporting vulnerabilities

Bitwarden runs a public bug-bounty programme on HackerOne. The repo's SECURITY.md is the canonical place to start. Please do not open public issues for security findings.

Authentication

  • Passwords — master password is hashed client-side with PBKDF2 / Argon2id (per User.Kdf). The resulting hash is sent over TLS, then re-hashed server-side with PBKDF2 (ASP.NET Core Identity's PasswordHasher). The server never sees plaintext passwords.
  • Two-factor — supported providers: TOTP, Email, Duo, WebAuthn, YubiKey OTP. A "Remember this device" cookie can suppress 2FA for a configurable window.
  • CaptchaHCaptchaTokenable on suspicious login attempts.
  • Rate limitsAspNetCoreRateLimit middleware in cloud. Self-host operators run their own ingress.
  • Login policies — orgs can require 2FA, single-org membership, master-password complexity, etc.

OAuth scopes

Each scope grants a narrow capability:

  • api — full client API.
  • api.organization — public-API operations on a specific org.
  • api.installation — self-host installation provisioning.
  • api.licensing — license generation + refresh.
  • api.secrets — Secrets Manager.
  • api.send.access — single-shot Send access.
  • api.push — push registration.

Encryption keys

  • User key (64 bytes) encrypts the user's vault. Master-password-sealed by default (User.Key); held by Key Connector or wrapped to a trusted device for non-master-password orgs.
  • Org key is wrapped per-member under their public key.
  • Send key wrapped under the user key; rotated together with the user key.
  • Device key (TDE) — per-device key pair stored on the Device row.
  • IdentityServer signing key — RSA, persisted via ASP.NET Data Protection (Azure Blob in cloud, mounted volume in self-host).

Data protection

AddCustomDataProtectionServices (src/SharedWeb/Utilities/) sets up ASP.NET Core data protection with persistent storage. It is the underlying mechanism for:

  • Authentication cookies.
  • Tokenables (every signed payload — OrgUserInviteTokenable, EmergencyAccessInviteTokenable, ResetPasswordTokenable, SsoTokenable, SendAccessTokenable, RegistrationEmailVerificationTokenable).
  • Session ticket store (DistributedCacheTicketStore).

Keys rotate automatically based on the persisted ring; nothing in product code needs to manage rotation.

Stripe / external secrets

  • Stripe API keys live in globalSettings.Stripe.ApiKey; the webhook signing key in globalSettings.Stripe.WebhookKeys.
  • PayPal IPN credentials, BitPay / Braintree / Apple iAP signing keys all live in globalSettings.
  • Mail transport credentials likewise.

Never commit any of those values; the dev/secrets.json.example is a placeholder template only.

SSRF / CORS

  • The Icons host validates target hostnames before fetching (no RFC1918 / link-local / loopback).
  • API CORS allows only the cloud / dev origins via CoreHelpers.IsCorsOriginAllowed.
  • Outbound HTTP from billing webhooks uses Stripe's signed receipts; we don't follow user-controlled redirects.

Reverse-proxy headers

SecurityHeadersMiddleware (src/Core/Utilities/SecurityHeadersMiddleware.cs) emits CSP, HSTS, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: same-origin, Permissions-Policy. Every controller-based host applies it.

Audit log

User-visible mutating actions (cipher / org / member / device changes) write to the Event table via IEventService. The Public API exposes the audit log; integration channels (Slack/Teams/Webhook/HEC/Datadog) can stream it in real time. Bitwarden staff cannot read user vault contents, even via the audit log, because cipher payloads remain encrypted.

Static analysis & SCA

  • Checkmarx.checkmarx/ config; runs in .github/workflows/scan.yml.
  • Renovate — dependency updates; .github/renovate.json5.
  • Dependabot — supplemental.
  • Code scanning — GitHub default code scanning is enabled.
  • packages.lock.json — committed everywhere so the build is reproducible (CODEOWNERS protects them).

Things NOT to do

  • Do not write decryption code on the server. Use the SDK port (util/RustSdk/) only when the same code on the client uses the same logic.
  • Do not skip the Bitwarden-Migrate deprecation cadence on the public API.
  • Do not embed user input in stored procedure parameters by string concatenation. Use Dapper's parameter binding.
  • Do not introduce new long-lived API tokens without considering rotation.
  • Do not weaken SecurityHeadersMiddleware defaults without an @bitwarden/team-appsec review.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Security – Bitwarden Server wiki | Factory