Harden a FusionAuth Production Instance
On this page
A new FusionAuth instance uses defaults that are convenient for development. Before you put an instance into production, work through the steps on this page. Each step links to the full reference for the setting it changes.
The first section applies to every instance, including FusionAuth Cloud. The second section applies only when you self-host, because FusionAuth Cloud manages that infrastructure for you. See Who Secures What for the split.
For a broader checklist to audit against, see the OWASP Application Security Verification Standard.
Secure Every Instance#
These steps change settings in the Admin UI, so they work the same on FusionAuth Cloud and self-hosted instances.
Protect Admin Accounts#
An admin account can change every setting and read every user, so protect these accounts first:
- Confirm that the authenticator app method is enabled at Tenants -> Your Tenant -> Multi-Factor . It is enabled on the default Tenant and available on every plan.
- Have each administrator enable MFA on their account.
- On the Enterprise plan, set the On login policy of the FusionAuth Application to
Requiredat Applications -> FusionAuth -> Multi-Factor . This requires MFA for the Admin UI without requiring it for your other Applications. - Give each administrator only the Admin UI roles they need. For example, support staff need
user_support_manager, notadmin.
Strengthen Passwords#
Configure the password policy at Tenants -> Your Tenant -> Password :
- Raise Minimum length. The default is
8. NIST SP 800-63B recommends at least 15 characters when a password is the only factor. Length helps more than character-type rules, so you can leave those off. - Enable breached password detection on the Starter plan and above. It rejects passwords that appear in known breaches.
- Configure account lockout. FusionAuth counts failed logins by default, but doesn't lock an account until you select a User action under Failed authentication settings.
To avoid passwords altogether, offer passkeys.
Shorten Token and Session Lifetimes#
A stolen token works until it expires, so keep lifetimes short. Configure these at the Tenant level, and override them per Application where needed:
- Lower JWT duration at Tenants -> Your Tenant -> JWT . The default is
3600seconds. Use the shortest value your applications handle well, such as 5 to 15 minutes. - Set Refresh token usage to
One-time usein the same tab. Each refresh then returns a new refresh token and invalidates the old one, which the OAuth 2.0 Security Best Current Practice recommends for public clients. - Lower Refresh token duration from the default of 30 days if users can sign in more often. Set Refresh token expiration to sliding window with a maximum lifetime to keep active users signed in up to a fixed limit.
- Review Session timeout at Tenants -> Your Tenant -> OAuth . This is how long an SSO session can be idle. The default is
3600seconds.
See the Tenant JWT settings for every option.
Require PKCE#
PKCE stops an intercepted authorization code from being exchanged for tokens. For each Application, set PKCE to Required at Applications -> Your Application -> OAuth . The default is Not required.
If a server-side application can't send a code verifier, use Not required when using client authentication for it instead. See RFC 7636 for how PKCE works.
Restrict API Keys#
An API Key with no endpoints selected can call every API, so create keys with only the access they need at Settings -> API Keys :
- Select only the Endpoints and methods the key's consumer calls.
- In a multi-tenant instance, select a Tenant to limit the key to that Tenant.
- Set an Expiration for keys that have a limited lifetime.
- Set Retrievable to
Not Retrievableso the key value can't be read again after you create it. - On the Enterprise plan, add an Access Control List to limit the IP addresses that can use the key.
Store one unrestricted key in a secrets vault for emergencies, and don't use it day to day. See API Key Permissions for details, and Key Rotation to rotate API Keys and signing keys.
Monitor Security Events#
Send login events to your logging or security monitoring system with a webhook. Useful events include user.login.failed, user.password.breach, and on the Enterprise plan, user.login.suspicious.
When you enable these events at Tenants -> Your Tenant -> Webhooks , set Transaction setting to No Webhooks are required to succeed. Otherwise an outage in your monitoring system blocks logins. Sign the webhook so your receiver can verify each event came from FusionAuth.
The audit log records changes made in the Admin UI. Changes made directly with an API Key aren't recorded unless your code adds an entry with the Audit Log API.
Limit What the Login Pages Reveal#
Some hosted page responses reveal whether an account exists, such as the message for a locked account. FusionAuth already avoids this on the forgot password page. See Account Enumeration to decide which responses to change.
Secure a Self-Hosted Instance#
When you self-host, you also secure the network, the database, and the search engine.
Where the self-hosted controls apply. FusionAuth Cloud manages everything except the FusionAuth configuration.
Put FusionAuth Behind a Proxy#
Run FusionAuth behind a reverse proxy, and use the proxy to:
- Terminate TLS with TLS 1.2 or later, and redirect HTTP to HTTPS.
- Add the security headers that FusionAuth doesn't send.
- Restrict the Admin UI to your administrators' networks.
- Rate limit requests per IP address. FusionAuth's own rate limits count actions per user, so they don't stop requests that never name a user.
Then block direct access to FusionAuth on port 9011 from everywhere except the proxy.
Trust Only Your Proxies#
FusionAuth reads the client IP address from the X-Forwarded-For header. By default it trusts every proxy, so a client can forge the header to change the IP address used for IP ACLs and logs. At Settings -> System -> Networking , set Trust policy to Only Configured and list your proxy addresses. See Networking Configuration.
Secure the Database and Search Engine#
The database holds password hashes and personal data, and the search engine holds a copy of user data:
- Encrypt the database connection with TLS. See
database.urlin the configuration reference for an example. - Give FusionAuth its own database user, and restrict access to the database and its backups. See Database Access.
- Keep the search engine off public networks. A single-node install binds it to localhost by default. See Required Ports for clusters.
Protect Configuration and Secrets#
- Set
fusionauth-app.runtime-modetoproduction. In the defaultdevelopmentmode, FusionAuth shows the interactive Maintenance Mode pages to anyone who reaches the instance while the database needs setup or an update. - Keep database passwords and other secrets out of version control. Make the configuration file readable only by the user that runs FusionAuth, or inject secrets as environment variables from a secrets manager. See Configuration Information.
- If you use Kickstart, reference secrets with environment variables, and replace the API Key it creates once the instance is running.
Stay Up to Date#
Updates include security fixes. To hear about them, subscribe to the release notes feed, and assign the Security Officer role to your account at account.fusionauth.io to join the security announcement list. Follow Update a Production Instance to apply updates safely.
Related