Fortress Security Part 1: How Fortress Secures WordPress

21 min read

Table of Contents

Introduction

Snicco, the team behind the Fortress Security plugin, are an enterprise WordPress development team and independent security research firm. 

Through their research and guidance they have helped 24 of the most well-known security plugins patch vulnerabilities in their code – many extremely serious that could have led to a full site takeover. Their security research found DOS vulnerabilities, a complete lack/misunderstanding of encryption, and unjustifiable security shortcuts. Many plugins even copied code from others verbatim, introducing inherent security issues into their codebase, and every plugin evaluated had at least 1 vulnerability, with most having 3 or more.

Based on their research and the serious issues present in other solutions, they have created Fortress, a plugin that focuses on keeping WordPress locked down by significantly upgrading WordPresses own security practices, adding secure 2FA, rate limiting, more secure sessions, and forcing strong passwords with best available cryptography.

About Fortress

No Security Compromises

The core idea behind hosting partnerships is only to include what can be done effectively at the plugin level and nothing more. You get EVERYTHING that can be hardened effectively in PHP and NONE of the things that are useless at best and resource hogs at worst.

Unlike other security plugins, Fortress is NOT built for the lowest common denominator, which means there are no security compromises by:

  1. Purposefully NOT supporting prehistoric PHP versions and instead only supporting 7.4+|8+|8.1+
  2. Partnering directly with hosting companies so that most of the ambiguity of the unknown runtime is prevented. (The reason every other plugin stores encryption keys in the database is because it’s the only thing they can rely on being available).

Fortress Modules

Fortress consists of the following modules that you can use independently of each other:

  1. Authentication
  2. Password Security
  3. Rate limiting
  4. Session Management
  5. Vaults and Pillars
  6. Code Freeze

The first four modules are activated by default, and together they harden your security at every step of your website’s authentication lifecycle. Vaults and Pillars can be manually configured on an as needed basis.

Quality Assurance

Fortress undergoes rigorous quality assurance testing to ensure every code base change works. This includes 1200 automated tests before every single release across all combinations of supported WordPress and PHP versions.

WP-CLI Configuration

Fortress is built with a CLI-first approach and has full feature parity between the Web UI and the WP-CLI.

To improve the developer experience and reliability of the default WP-CLI, Snicco have created their own open-source BetterWPCLI library, and this is used everywhere in Fortress.

Customization

All of the modules in Fortress are highly customizable and can be configured per user role. Even the most complex scenarios can be accommodated.

Default Settings and Workflow

Everything in Fortress is configurable, so if there’s a default setting that you wish to change (or make recommendations about to your clients) it’s all flexible. That all said, these are the 3 things that may change the workflow of your clients and/or team members with the default settings:

  1. 2FA is required for administrators and editors, it is not optional.
  2. Password resets are disabled by default for administrators and editors, so out of the box, this needs to be done via WP-CLI.
  3. For anyone working inside the website for extended periods, the sudo timeout is 10 mins by default. This means you can still navigate around the UI, but to say install/delete a plugin or start editing a post, they will be required to authenticate.

Secure Two-Factor Authentication

One of the biggest problems with many security plugins (both now and in the past) is that their two-factor authentication is not only NOT secure, but its activation may also have led to your website being hacked in certain circumstances. Snicco provided numerous plugin vendors with the code needed to fix such issues.

In Fortress, two-factor authentication employs the most up-to-date, best security practices, can be enforced at a granular level, and has its own WP-CLI.

2FA Rate Limiting

Fortress is the ONLY security plugin that rate-limits failed 2FA attempts. Without implementing rate-limiting, 2FA can be brute forced, simple as that.

By default, Fortress allows five failed 2FA attempts (configurable). The failed attempts counter is reset after a successful 2FA login.

If the rate-limiting threshold is exceeded, Fortress will “lock” the user account, which means:

  • Destroying all sessions (including the current one) for the associated user.
  • Resetting the password of the user to a completely random one.
  • Sending the user an email about the incident.

Compatibility

  • The Fortress TOTP implementation conforms with the RFC 6238 spec and works with all common authenticator apps like 1Password, Google Authenticator, etc.
  • Fortress 2FA is compatible with “most” custom WordPress login forms out of the box, including WooCommerce.

Password Security

The Fortress password security features are broken down as follows:

  • Main Features:
    • Secure password hashing
    • Password policy
    • Disabling password resets for privileged users
  • Tweaks:
    • Disable application passwords
    • Decrease password reset link duration
    • Destroy all user sessions on password change

We’ll look at each of these below.

Secure Password Hashing

WordPress uses an outdated md5-based hashing scheme for password security that is no longer considered secure. Fortress solves for this.

  • Fortress replaces the password-hashing functions with its own implementation based on the libsodium core PHP extension, which is the best cryptography implementation in PHP.
  • Fortress encrypts the password hash with the ID of the corresponding user as additional data and stores the ciphertext in the database, making it essentially impossible for attackers to crack unless the filesystem is compromised.
  • Encryption keys are stored securely on the server, not the database. Fortress prevents one big attack vector on WordPress sites, where an attacker with database access / or SQL injection changes the password for an existing user or swaps the password hashes with a password known to them.
  • Other security plugins only opportunistically rehash passwords, which means that only the passwords of users who have logged in recently can be updated. Fortress can proactively update password hashes in the background (via WP-CLI) without requiring users to change their passwords AND can disallow legacy hashes after upgrading.
  • Fortress can detect legacy password hashes from various sources and upgrade them to a more secure hash using encryption with argon2 algorithm.
  • Fortress can automatically upgrade legacy password hashes in batches without disrupting regular site operations using its password upgrade-legacy-hashes command.

Password Policy

Fortress enforces a NO-BS password policy for all user accounts, which means:

  • Passwords can be between 12 and 4096 characters, with no character restrictions and full Unicode support*. For example: 
    🧐🧐漢字👀docker-horse-chair is a perfectly valid password.
  • Password strength must score at least a 3 out of 4 according to the zxcvbn password entropy estimator.
  • The policy is enforced when a user edits their profile on the /wp-admin/profile.php page or creates a new user on the /wp-admin/user-new.php page.
  • The policy is evaluated when users reset their password on the wp-login.php page.
  • Users can be excluded from the password policy based on their roles or dynamically using the CheckingPasswordPolicy event.
  • Fortress never allows weak passwords server-side, even if the “Confirm use of weak password” checkbox is clicked.

*The disable emoji GridPane additional security hardening measure does not interfere with password setting.

Additional Note

The zxcvbn estimator is a password strength estimator created by Dropbox inspired by password crackers that weighs 30k common passwords, common names, and surnames according to US census data, popular English words from Wikipedia and US television and movies, and other common patterns like dates, repeats, sequences, keyboard patterns, and l33t/1337 speak.

Disabling password resets for privileged users

Recovering a forgotten password is a balance between security and convenience. Fortress uses an opt-in approach for password resets to enhance security.

A user with any of the roles defined in the password_policy_excluded_roles option will not be able to:

  • request a password reset link.
  • reset passwords on the profile page.

By default, this applies to users of the roles:

  • administrator
  • editor 

Passwords can always be reset using WP-CLI.

  • For these defined roles, Fortress intercepts the allow_password_reset hook, which WordPress uses to send a password recovery email to the user’s email, and lets the user know that they cannot reset their password.
  • You have the ability to add additional user roles (e.g., for WooCommerce sites, you may wish to add the shop_manager role).

Edge Cases

some plugins like WooCommerce and LMS have their own mechanisms to reset passwords without using the allow_password_reset hook. In such cases, Fortress cannot prevent password resets, and you may need to contact the plugin vendor to add the hook or inspect the code and fire it yourself at the appropriate time. More info on this can be found in part 2 of this series.

Disable application passwords

The problem: WordPress application passwords are vulnerable to social engineering attacks, where an attacker can trick an unsuspected site admin into adding a new application password by clicking on a link.

  • As most sites don’t require them, Fortress completely disables WordPress application passwords by default.
  • You can re-enable application passwords on the websites that require them.

WooCommerce + Zapier are a common combination that require this functionality, so on these sites it will need to turned back on.

Decrease password reset link duration

By default, WordPress password reset links are valid for 24 hours. Fortress decreases this duration to 30 minutes.

This can be adjusted if 30 minutes is too strict for your use case but is plenty of time for most websites.

Destroy all user sessions on password change

The Problem: WordPress invalidates a user’s sessions after a password change by coincidence because a user’s password hash happens to be part of his authentication cookie. To be precise, the 8th – 12th characters of the password hash are part of a user’s auth cookie. However, the difference between the 8th – 12th hash characters is not guaranteed, making this unreliable.

For this reason, Fortress explicitly destroys a user’s sessions after he changes his password.

Rate Limiting

Rate limiting is a technique used to control the rate at which requests are made to a system or service to prevent overload and to protect against malicious attacks, such as brute-force attacks, that attempt to flood a system with too many requests.

When the rate limit is reached, further requests are blocked until a later time. It is commonly used in web applications to prevent denial-of-service attacks and to limit the impact of automated bots. 

Rate limiting is an important technique used to maintain the stability and security of your website by controlling the rate of requests that it receives. However, most WordPress security plugins can only protect against attacks from a single IP because they still operate with the 2000s mindset of “one attacker = one IP.” This could not be further from the truth in 2023.

Fortress includes two rate-limiting features: Password reset throttling and Login throttling and has implemented a novel (in WordPress) rate-limiting algorithm (Token Bucket) that, instead of storing all failed attempts, only needs to store a counter per IP/username/etc. This results in far less storage and uses the WP Object Cache directly instead of the database.

Password Reset Throttling

  • Fortress limits password resets to once every 15 minutes per IP address.
  • The purpose of password reset throttling is to prevent attackers from flooding a user’s email inbox with password reset requests.
  • Password reset throttling also prevents attackers from tricking the SMTP service into sending multiple password reset emails.

Login Throttling

Login throttling is a rate-limiting mechanism that restricts the number of login attempts from various dimensions to protect against different attack vectors.

  • Device ID throttling: After an honest user logs in, Fortress assigns a cryptographically secure device ID to the user’s browser that an attacker can not fake or compromise. Honest users will then only be rate-limited pretty loosely. This is a highly effective method of ensuring that honest users are not affected during a brute force attack without reverting to a UX-killing solution like Captchas. 
  • Username throttling: Fortress limits login attempts for a specific username.
    IP throttling: Fortress throttles login attempts from specific IP addresses and allows for bursts with a refill period to prevent false positives.
  • Global throttling: Fortress considers all failed login attempts irrespective of the target username and remote IP.

Secure Sessions

Before diving into each of the features below, it’s important to understand what a session is and how it relates to your website’s security. Here’s a quick breakdown:

  • A session in WordPress refers to the period of time when a user is logged in to the website. When a user logs in, WordPress creates a session for that user, and as long as the session is active, the user can access protected pages and perform actions that require authentication.
  • The session is maintained by using a session cookie, which is a small file stored on the user’s browser. This cookie contains a unique identifier that allows WordPress to associate the user’s browser with their session.
  • When the user logs out or the session expires (due to inactivity or a configured timeout), the session is destroyed, and the user is logged out of the website. 
  • Sessions are an essential part of WordPress security as they help ensure that only authorized users can access sensitive pages and features of the website.

Fortress makes WordPress sessions far more secure.

Custom user session storage

WordPress uses a custom session implementation that stores arbitrary data (the session) in the wp_usermeta table. This has some disadvantages:

  • The wp_usermeta table will get bloated if many users log in frequently.
  • All user sessions are stored bundled together instead of separately, keyed by their token. If your users use several devices, all session data for all sessions has to be retrieved for every authenticated request.
  • Expired sessions can only be garbage collected once the user logs in on a different device.
  • To update one session, all sessions have to be fetched first.
  • (Theoretically) subject to time-based-side-channel attacks, though this will require a very skilled and highly motivated attacker.

Fortress solves for all of these issues with its own custom user sessions.

  • Fortress offloads the storage of sessions to a custom table, where each session represents one row, with the session token being the primary key. This is both more efficient and is a crucial pre-requisite that makes all the other features of the session module possible.
  • Fortress replaces the default session storage in WordPress with a custom table, making all of its session-related features possible.
  • Fortress comes with a WP-CLI command to remove expired sessions from the database, which you can schedule as per your preference.
  • The plugin also provides an option to destroy all sessions on your site, a more efficient and safer way to log out all users instead of rotating your site’s salts, which may not be compatible with all plugins.

Session management and security

The Problem
  • WordPress uses cookies for authenticating users, and these cookies contain a session token linking to an actual WordPress session.
  • An attacker can steal already valid cookies either via network communication or via malware on a user’s infected computer.
  • Stolen cookies are sold to hackers in huge batches on the dark web, and WordPress sites are wide open to these attacks. Neither Core nor third-party security plugins have a solution. 

At the time of writing, the Linus Tech Tips YouTube channel had just suffered a similar attack – detailed video here.

Important

Many many viruses are built for the sole purpose of cookie harvesting. This is a huge security problem, and no one in the WordPress security world even tries to protect against this.

The Solution

Fortress uses four dimensions (timeouts) to harden WordPress session security. Each of these is explained below. Configuration options are available to overwrite default values.

1. The Absolute Timeout
  • Fortress will expire a session regardless of user activity after the absolute timeout has passed.
  • This timeout roughly corresponds to what WordPress Core does by default.
2. The Rotation Timeout
  • This security feature is used to protect user sessions from being exploited if their session token is stolen.
  • After a certain period of time (default is 20 minutes), the current user session is copied to a new session token, and the old token is invalidated.

This means that if an attacker or a virus on the local machine steals a token, they have a limited amount of time (the rotation timeout period) to use it before it becomes invalid. If a user continues to use the site after the token has been stolen, they will receive a new token, and the old one will become invalid.

However, if an attacker immediately starts exploiting the stolen token, there is a race condition between them and the legitimate user.  Once the rotation timeout is exceeded, the contents of the current user session are copied to a newly generated session token (auth-cookie). The first incoming client request with a valid auth cookie will receive the new auth cookie.

This means that either the legitimate user or the attacker could win and the other party will be locked out after the new auth cookie. This is a serious problem, and so shorter the window, the more secure your session is.

3. The Idle Timeout
  • This timeout logs you out of your account if you haven’t been active on a website for a certain amount of time. This helps protect your account from unauthorized access if you’ve left your computer unattended or used a public computer OR if a machine is shared between multiple people using the site with different accounts.
  • Fortress has a default idle timeout of 30 minutes, but you can configure it to be shorter or longer.
  • The timeout is reset whenever you make an HTTP request to the website, except for when using WP-Cron or the Heartbeat API in the WordPress admin area.
4. The Sudo Mode Timeout
  • This timeout requires users to re-enter their password to perform sensitive actions after logging in. This is a feature that companies like Amazon use to let you remain logged in and add items to your basket, but also make you re-authenticate when doing anything billing related.
  • Once they enter the password, they can perform sensitive actions for a certain period without re-authenticating.
  • Fortress has a default sudo timeout of 10 minutes, and this timeout can be changed for individual users using the sudo_timeout option.

This complements the absolute timeout and idle timeout to provide better security.

Fortress Sudo Mode

  • Fortress has a feature called sudo mode that gives a user elevated privileges for a certain period after they log in. This is similar to the Linux sudo command. After the time is up, the user’s privileges go back to normal.
  • During sudo mode, the user can use the website normally. If they are not in sudo mode and try to access a protected page, Fortress will ask them to confirm their password.
  • If they provide the correct password, the sudo timeout is reset, and they can access the page.

Vaults & Pillars

Snicco continue to lead the charge for a safer and more secure WordPress ecosystem. Vaults & Pillars are a game changer in this record, tackling one of the biggest potential security vulnerabilities: Storing sensitive data in plaintext within the WordPress database.

Recommended Reading

I highly recommend checking out Snicco's introductory blog post here:
Solving WordPress’s Pathological Plaintext Problem: Introducing Fortress Vaults&Pillars

This, unfortunately, is such common practice that almost every single WordPress theme or plugin developer defaults to storing sensitive data in plaintext in the database without any consideration for the possible ramifications. This is because (a) they’re not thinking about SQLi vulnerabilities, and/or (b) SQLi vulnerabilities are not considered to be their problem. 

After all, if none of the popular WordPress security plugins consider them as a part of their threat model, why would they?

Even just a read (not even a write) SQLi vulnerability presents a serious threat. For example:

  1. A stolen Stripe API key (or any payment API credentials) could destroy your business and your clients via financial fraud.
  2. Stolen SMTP credentials could be used to destroy your email-sending reputation.
  3. A stolen API token could potentially compromise your WordPress website.

The ramifications of these breaches are not just theoretical. These happen every day and can cause significant harm to businesses and individuals alike (like the $70,000 Stripe hack you may have heard about).

The Fortress Vaults & Pillars (VnP) module allows you to address this critical problem by introducing an encryption layer for sensitive data stored in the WordPress database.

Fortress acts as a hidden translation layer between WordPress and the database, which means that all functionality of the WordPress Option API continues to work as expected without any code modification in WordPress Core or plugins.

Vaults

A Fortress Vault secures a WordPress option (or a subset of it) by storing it encrypted in the wp_options table, allowing you to encrypt sensitive information that an attacker could exploit if they were to gain access to your database.

  1. One of Fortress’s Secure Secrets is used as the encryption key, meaning that decryption of the database value is impossible unless the entire server (filesystem) is compromised.
  2. Fortress is a hidden translation layer between WordPress Options API and the database and provides WordPress with the “real” value when it needs it.

Examples of sensitive data include:

  • Stripe Keys via any plugin (e.g. WooCommerce, Easy Digital Downloads, form builder plugins, etc).
  • SMTP/Transaction Email Keys.
  • Custom Plugin/Theme API Tokens.
  • Third-party API integrations like Zapier, Shipstation, etc.

Any information that’s sensitive and is currently stored as plaintext in the wp_options table can and should be stored in a Vault.

Pillars

Pillars can be used to securely handle important settings and options in the wp_options table. They are not necessarily settings that pose a security threat if exposed, but they are settings that could be maliciously altered in ways that could negatively impact the security and stability of your website.

  • Pillars are not stored in the database. Instead, they are defined in the Fortress configuration and loaded into the WordPress environment at initialization. This a more mature security approach, commonly found in frameworks like Laravel, Ruby on Rails, and more.
  • Pillars are always immutable, and they can’t be changed without deploying a new version of the Fortress configuration, thereby providing an added layer of security and stability for your website.

So what should be a Pillar?

1. Security-Critical Configuration Flags

These are settings that significantly impact your website’s security, and they are typically settings that can be turned on or off.

Unfortunately, in WordPress, these are often stored in the database for convenience, which means these can potentially be exploited if any of your themes or plugins (or even WordPress core) have vulnerabilities.

Examples include:

  • users_can_register: Control whether user registration on the site.
  • default_role: Sets the default role for new users.

Exploiting these settings could allow hackers to enable user registration on the front end of your website and set the default role to administrator, allowing for a full site takeover.

2. Locking Options for Reliability, Integrity, and Stability

Pillars can help secure settings that, if altered, could disrupt the stable operation of your WordPress website. They’re often simple strings or boolean values that shouldn’t be changed in a production environment – whether maliciously or out of user ignorance.

Examples include:

  • permalink_structure: Changing this could break existing links and/or navigation, as well affect your website’s SEO rankings.
  • blog_public: This option could be changed to discourage search engines from indexing the site, potentially resulting in your site losing all of its SEO rankings.

These settings should never be accidentally modified in production.

Code Freeze

Vulnerabilities that allow administrator-level takeover of a WordPress website are the most severe in terms of WordPress security. 

The 3 most common ways this occurs are through:

  1. Session cookie hijacking.
  2. Stolen Administrator account credentials.
  3. Privilege escalation: Converting a lower-level account such as a Subscriber into an Adminstrator account. 

Thomas Raef of WeWatchYourWebsite shared data gathered from millions of WordPress sites that showed that 67% of all WordPress compromises in 2023 were caused by 1 and 2:

The Real Attack Vector Responsible for 60% of Hacked WordPress Sites in 2023

Serious vulnerabilities in overwhelmingly popular plugins such as Elementor have allowed hackers to take advantage of number 3.

Code Lockdown

Code Freeze gives you 80% of the benefits of an immutable deployment strategy without the need to manage your websites via our Git integration.

Code Freeze detects if your site is running in production and ensures that:

  • No new plugins/themes can be installed from the repo or uploaded.
  • No files can be edited.
  • No plugins can be deactivated.
  • Themes cannot be switched.

However, you can still update any existing plugin, theme, or core from within the admin dashboard and WP-CLI.

Code Freeze is fully integrated into GridPane, and this includes our staging site workflows. This means that production sites are locked down and staging sites are automatically “unlocked” without any manual configuration.

Learn More About Code Freeze

You can learn more about Code Freeze in Calvin’s launch post here: 
Fortress Code Freeze – Reap the Security Benefits of “Immutable WordPress” without the Complexity

And the official Developer documentation here:
Developer docs: The Fortress Code Freeze module

Up Next: Part 2 Quick Start Configuration Guide

This is part 1 of a 2 part series of documentation. Click below to begin learning how to configure Fortress:

Fortress Security Part 2: Quick Start Configuration Guide