diff --git a/.travis.yml b/.travis.yml index 5126473..268926b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ # Import Travis configuration from dev-tools repo version: ~> 1.0 import: - - source: humanmade/altis-dev-tools:travis/module.yml@4accc74 + - source: humanmade/altis-dev-tools:travis/module.yml@0bfa112a mode: deep_merge_append # Add your custom config below, which will merge with the default module config from the section above. diff --git a/docs/2-factor-authentication.md b/docs/2-factor-authentication.md index 6c03614..a2f36f9 100644 --- a/docs/2-factor-authentication.md +++ b/docs/2-factor-authentication.md @@ -1,6 +1,8 @@ # Two Factor Authentication -For increased security of user authentication, Altis supports the use of a second factor to authorize the login request. The Two Factor Authentication feature is enabled by default, and only required for network administrators and site administrators by default. +For increased security of user authentication, Altis supports the use of a second factor to authorize the login request. The Two +Factor Authentication feature is enabled by default, and only required for network administrators and site administrators by +default. To disable Two Factor Authentication, set the `modules.security.2-factor-authentication` setting to `false`. @@ -8,38 +10,42 @@ Second factor authentication options are Email, Time-based one-time passwords an Two Factor methods can be configured by each user in their Edit Profile page in the CMS. -**Note:** Two Factor Authentication is not required on local environments for convenience, in order to require it, use local environment specific configuration. +**Note:** Two Factor Authentication is not required on local environments for convenience, in order to require it, use local +environment specific configuration. ## Requiring Two Factor Authentication -The site can be configured to require all users enable two factor authentication, or set requirement options on a per-role basis. To require all users of the site enable two factor authentication set the `modules.security.2-factor-authentication.required` setting to `true`: +The site can be configured to require all users enable two factor authentication, or set requirement options on a per-role basis. To +require all users of the site enable two factor authentication set the `modules.security.2-factor-authentication.required` setting +to `true`: -``` +```json "altis": { - "modules": { - "security": { - "2-factor-authentication": { - "required": true - } - } - } + "modules": { + "security": { + "2-factor-authentication": { + "required": true + } + } + } } ``` -Alternatively, to require two factor authentication only for specific user roles, define the roles in the `modules.security.2-factor-authentication.required` array: +Alternatively, to require two factor authentication only for specific user roles, define the roles in +the `modules.security.2-factor-authentication.required` array: -``` +```json "altis": { - "modules": { - "security": { - "2-factor-authentication": { - "required": [ - "super-admin", - "administrator", - "editor" - ] - } - } - } + "modules": { + "security": { + "2-factor-authentication": { + "required": [ + "super-admin", + "administrator", + "editor" + ] + } + } + } } ``` diff --git a/docs/README.md b/docs/README.md index 5ccbf91..d6e9b43 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,6 @@ # Security -![](./assets/banner-security.png) +![Security banner](./assets/banner-security.png) -The security module provides high-level security focused features to the Altis. This module is concerned with security features such as Multi Factor Authentication, Password Strength Rules, Audit Logging etc. +The security module provides high-level security focused features to the Altis. This module is concerned with security features such +as Multi-Factor Authentication, Password Strength Rules, Audit Logging etc. diff --git a/docs/audit-log.md b/docs/audit-log.md index 4148939..06f0883 100644 --- a/docs/audit-log.md +++ b/docs/audit-log.md @@ -1,10 +1,13 @@ # Audit Log -All change activity made in the CMS is tracked in the Audit Log. This provides a historical account of who changed what, when. The Audit Log is "always on" and tracks changes across all sites. +All change activity made in the CMS is tracked in the Audit Log. This provides a historical account of who changed what, when. The +Audit Log is "always on" and tracks changes across all sites. Audit logging base functionality is provided by the [Stream](https://github.com/xwp/stream/) plugin. -The Audit Log is tamper resistant. Once entries have been added to the Audit Log, they can not be removed. This is to preserve knowledge of historical changes for auditing and compliance purposes. The Cloud infrastructure application layer has no permissions to delete or modify records, therefore it's also resilient to modification from rogue custom code. +The Audit Log is tamper resistant. Once entries have been added to the Audit Log, they can not be removed. This is to preserve +knowledge of historical changes for auditing and compliance purposes. The Cloud infrastructure application layer has no permissions +to delete or modify records, therefore it's also resilient to modification from rogue custom code. The Audit Log will also be persistent across site restores, overrides and imports. @@ -33,8 +36,13 @@ The Audit Log records create, update and delete actions for the following conten ## Custom Action Recording -Any custom functionality or data types that are built on CMS primitives such as Custom Post Types, Custom Taxonomies, Post Meta or similar will already be tracked by default in the Audit Log. There are situations where you may want to insert your own custom records for reporting / compliance purposes. For example, you have built a feature with a custom database table, and want to track changes made to those entities. +Any custom functionality or data types that are built on CMS primitives such as Custom Post Types, Custom Taxonomies, Post Meta or +similar will already be tracked by default in the Audit Log. There are situations where you may want to insert your own custom +records for reporting / compliance purposes. For example, you have built a feature with a custom database table, and want to track +changes made to those entities. -In this scenario, you are responsible for also triggering the necessary API calls to the Audit Log in the application code. See the detailed documentation on [creating custom Connectors](https://github.com/xwp/stream/wiki/Creating-a-Custom-Connector) via the Stream plugin documentation. +In this scenario, you are responsible for also triggering the necessary API calls to the Audit Log in the application code. See the +detailed documentation on [creating custom Connectors](https://github.com/xwp/stream/wiki/Creating-a-Custom-Connector) via the +Stream plugin documentation. Once registered, your custom Stream Connector's records will be part of the Audit Log with the same data integrity guarantees. diff --git a/docs/basic-auth.md b/docs/basic-auth.md index a4d01e7..a872432 100644 --- a/docs/basic-auth.md +++ b/docs/basic-auth.md @@ -1,111 +1,121 @@ # Basic Authentication -In many instances, the [Require Login](./require-login.md) functionality of Altis is sufficient to block access to websites. However, sometimes, it's desirable to be able to test elements (particularly on development environments) as a logged-out user. +In many instances, the [Require Login](./require-login.md) functionality of Altis is sufficient to block access to websites. +However, sometimes, it's desirable to be able to test elements (particularly on development environments) as a logged-out user. -Altis provides support for Basic Authentication access control, which uses [standard HTTP Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to limit access instead of WordPress users. - -**Note:** Require Login only applies to URLs served from your [application servers](docs://cloud/architecture.md), and will not apply to `/uploads/` or `/tachyon/` URLs. +Altis provides support for Basic Authentication access control, which +uses [standard HTTP Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) to limit access instead of +WordPress users. +**Note:** Require Login only applies to URLs served from your [application servers](docs://cloud/architecture.md), and will not +apply to `/uploads/` or `/tachyon/` URLs. **Note:** Enabling PHP Basic Auth for an environment will prevent it from being indexed with search engines. ## Configuration -By default, Basic authentication is disabled. To enable it, a value must be passed to `security.php-basic-auth`; either `true` or an array that includes a username and password. +By default, Basic authentication is disabled. To enable it, a value must be passed to `security.php-basic-auth`; either `true` or an +array that includes a username and password. -The recommended setup is to define everything in your `composer.json` file, including the username and passwords. The same configuration in the manual setup example below could be handled in the Composer file like this: +The recommended setup is to define everything in your `composer.json` file, including the username and passwords. The same +configuration in the manual setup example below could be handled in the Composer file like this: ```json { - "extra": { - "altis": { - "modules": { - "security": { - "php-basic-auth": { - "username": "altisusername", - "password": "altispassword" - } - } - } - } - } + "extra": { + "altis": { + "modules": { + "security": { + "php-basic-auth": { + "username": "altisusername", + "password": "altispassword" + } + } + } + } + } } ``` **Note:** The authentication username and password _must_ be defined or basic authentication will not be active. -**Note:** You _must_ specify the username and password in this configuration, even if you are reusing the same username and password in different environments. - -You may also want to [disable Require Login](./require-login.md) in this configuration to ensure only one form of authentication is used. +**Note:** You _must_ specify the username and password in this configuration, even if you are reusing the same username and password +in different environments. +You may also want to [disable Require Login](./require-login.md) in this configuration to ensure only one form of authentication is +used. ### Manual Configuration -Manual setup involves a simpler configuration in your `composer.json` but an additional step in your configuration. Your Composer file would look like this: +Manual setup involves a simpler configuration in your `composer.json` but an additional step in your configuration. Your Composer +file would look like this: ```json { - "extra": { - "altis": { - "modules": { - "security": { - "php-basic-auth": true - } - } - } - } + "extra": { + "altis": { + "modules": { + "security": { + "php-basic-auth": true + } + } + } + } } ``` -This _turns on_ the Basic Auth component, but does not define the username and password. Without the username and password, basic authentication will not be required. To specify the username and password in this configuration, you must add them as PHP constants to a file in the `.config/` directory (e.g. `.config/load.php` or a file required by `.config/load.php`). The following example is recommended: +This _turns on_ the Basic Auth component, but does not define the username and password. Without the username and password, basic +authentication will not be required. To specify the username and password in this configuration, you must add them as PHP constants +to a file in the `.config/` directory (e.g. `.config/load.php` or a file required by `.config/load.php`). The following example is +recommended: ```php if ( in_array( \Altis\get_environment_type(), [ 'staging', 'development' ], true ) ) { - define( 'HM_BASIC_AUTH_USER', 'altisusername' ); - define( 'HM_BASIC_AUTH_PW', 'altispassword' ); + define( 'HM_BASIC_AUTH_USER', 'altisusername' ); + define( 'HM_BASIC_AUTH_PW', 'altispassword' ); } ``` - ## Overrides -By default, Basic Auth will work on development and staging environments but not local or production environments. These defaults can be overridden in the `composer.json` file as well, or environment-specific username/password combinations could be defined: +By default, Basic Auth will work on development and staging environments but not local or production environments. These defaults +can be overridden in the `composer.json` file as well, or environment-specific username/password combinations could be defined: ```json { - "extra": { - "altis": { - "modules": { - "security": { - "php-basic-auth": { - "username": "devuser", - "password": "devpass" - } - } - }, - "environments": { - "local": { - "modules": { - "security": { - "php-basic-auth": { - "username": "altis", - "password": "altis" - } - } - } - }, - "production": { - "modules": { - "security": { - "php-basic-auth": { - "username": "produser", - "password": "prodpass" - } - } - } - } - } - } - } + "extra": { + "altis": { + "modules": { + "security": { + "php-basic-auth": { + "username": "devuser", + "password": "devpass" + } + } + }, + "environments": { + "local": { + "modules": { + "security": { + "php-basic-auth": { + "username": "altis", + "password": "altis" + } + } + } + }, + "production": { + "modules": { + "security": { + "php-basic-auth": { + "username": "produser", + "password": "prodpass" + } + } + } + } + } + } + } } ``` diff --git a/docs/browser.md b/docs/browser.md index 65e59ac..a10acdf 100644 --- a/docs/browser.md +++ b/docs/browser.md @@ -1,22 +1,23 @@ # Browser Security -Altis includes a framework to ensure frontend security in browsers. Out of the box, Altis sends basic security headers, but we recommend sending more specific headers where you can. +Altis includes a framework to ensure frontend security in browsers. Out of the box, Altis sends basic security headers, but we +recommend sending more specific headers where you can. To set browser security settings, set values in your `composer.json` configuration under `extra.altis.modules.security.browser`: ```json { - "extra": { - "altis": { - "modules": { - "security": { - "browser": { - "automatic-integrity": true - } - } - } - } - } + "extra": { + "altis": { + "modules": { + "security": { + "browser": { + "automatic-integrity": true + } + } + } + } + } } ``` @@ -26,15 +27,14 @@ You can also disable browser security altogether by setting `browser` to `false` ```json { - "browser": false + "browser": false } ``` - ## Content-Security-Policy -Altis can automatically gather and send [Content-Security-Policy policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) for you. - +Altis can automatically gather and +send [Content-Security-Policy policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) for you. ### Site-wide policy @@ -45,76 +45,82 @@ Out of the box, only basic policies are sent: (These are considered best practices for all sites, but can be overridden if needed.) -To change the default Content-Security-Policy, you can configure it in your `composer.json` under `extra.altis.modules.security.browser.content-security-policy`: +To change the default Content-Security-Policy, you can configure it in your `composer.json` +under `extra.altis.modules.security.browser.content-security-policy`: ```json { - "browser": { - "content-security-policy": { - "base-uri": [ - "self" - ], - "object-src": [ - "none" - ] - } - } + "browser": { + "content-security-policy": { + "base-uri": [ + "self" + ], + "object-src": [ + "none" + ] + } + } } ``` -Keys under `content-security-policy` should be a valid [directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#Directives), and the value should be either a string or list of strings: +Keys under `content-security-policy` should be a +valid [directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#Directives), and the value +should be either a string or list of strings: ```json { - "browser": { - "content-security-policy": { - "base-uri": [ - "self" - ], - "object-src": [ - "none" - ], - "font-src": [ - "https://fonts.gstatic.com", - "https://cdnjs.cloudflare.com" - ], - "script-src": [ - "https:", - "unsafe-inline" - ] - } - } + "browser": { + "content-security-policy": { + "base-uri": [ + "self" + ], + "object-src": [ + "none" + ], + "font-src": [ + "https://fonts.gstatic.com", + "https://cdnjs.cloudflare.com" + ], + "script-src": [ + "https:", + "unsafe-inline" + ] + } + } } ``` Special directives (`'self'`, `'unsafe-inline'`, `'unsafe-eval'`, `'none'`, `'strict-dynamic'`) do not need to be double-quoted. -To build Content-Security-Policy policies, we recommend using the [Laboratory CSP toolkit extension](https://addons.mozilla.org/en-US/firefox/addon/laboratory-by-mozilla/) for Firefox, and the [CSP Evaluator tool](https://csp-evaluator.withgoogle.com/). - +To build Content-Security-Policy policies, we recommend using +the [Laboratory CSP toolkit extension](https://addons.mozilla.org/en-US/firefox/addon/laboratory-by-mozilla/) for Firefox, and +the [CSP Evaluator tool](https://csp-evaluator.withgoogle.com/). ### Page-specific policies -For page-specific policies, you can add a filter to `altis.security.browser.content_security_policies` to set policies. This filter receives an array, where the keys are the policy directive names. Each item can either be a string or a list of directive value strings: +For page-specific policies, you can add a filter to `altis.security.browser.content_security_policies` to set policies. This filter +receives an array, where the keys are the policy directive names. Each item can either be a string or a list of directive value +strings: ```php add_filter( 'altis.security.browser.content_security_policies', function ( array $policies ) : array { - // Policies can be set as strings. - $policies['object-src'] = 'none'; - $policies['base-uri'] = 'self'; - - // Policies can also be set as arrays. - $policies['font-src'] = [ - 'https://fonts.gstatic.com', - 'https://cdnjs.cloudflare.com', - ]; - - // Special directives (such as `unsafe-inline`) are handled for you. - $policies['script-src'] = [ - 'https:', - 'unsafe-inline', - ]; - - return $policies; + // Policies can be set as strings. + $policies['object-src'] = 'none'; + $policies['base-uri'] = 'self'; + + // Policies can also be set as arrays. + $policies['font-src'] = [ + 'https://fonts.gstatic.com', + 'https://cdnjs.cloudflare.com', + ]; + + // Special directives (such as `unsafe-inline`) are handled for you. + $policies['script-src'] = [ + 'https:', + 'unsafe-inline', + ]; + + return $policies; } ); ``` @@ -123,27 +129,27 @@ You can also modify individual directives if desired: ```php // You can filter specific keys via the filter name. add_filter( 'altis.security.browser.filter_policy_value.font-src', function ( array $values ) : array { - $values[] = 'https://fonts.gstatic.com'; - return $values; + $values[] = 'https://fonts.gstatic.com'; + return $values; } ); // A filter is also available with the directive name in a parameter. add_filter( 'altis.security.browser.filter_policy_value', function ( array $values, string $name ) : array { - if ( $name === 'font-src' ) { - $values[] = 'https://cdnjs.cloudflare.com'; - } + if ( $name === 'font-src' ) { + $values[] = 'https://cdnjs.cloudflare.com'; + } - return $values; + return $values; } ); ``` - - ### Subresource Integrity -Altis automatically adds [subresource integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes where possible. These are generated for any files on the same server; i.e. any plugin or theme assets. +Altis automatically adds [subresource integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes +where possible. These are generated for any files on the same server; i.e. any plugin or theme assets. -For external assets, you can manually set the integrity hash. After enqueuing (or registering) your asset, use the `set_hash_for_script()` or `set_hash_for_style()` helpers: +For external assets, you can manually set the integrity hash. After enqueuing (or registering) your asset, use +the `set_hash_for_script()` or `set_hash_for_style()` helpers: ```php // Setting hashes for scripts. @@ -157,37 +163,40 @@ wp_enqueue_style( 'my-handle', 'https://...' ); set_hash_for_style( 'my-handle', 'sha384-...' ); ``` -Automatically-generated hashes are automatically cached in the object cache, linked to the filename and version of the script or stylesheet. +Automatically-generated hashes are automatically cached in the object cache, linked to the filename and version of the script or +stylesheet. You can disable the automatic generation of the integrity hashes if desired by setting `browser.automatic-integrity` to `false`: ```json { - "browser": { - "automatic-integrity": false - } + "browser": { + "automatic-integrity": false + } } ``` - ### Security Headers -Altis automatically adds various miscellaneous security headers by default. These follow best-practices for web security and aim to provide a sensible, secure default. +Altis automatically adds various miscellaneous security headers by default. These follow best-practices for web security and aim to +provide a sensible, secure default. In some cases, you may want to adjust or disable these headers depending on the use cases of your site. - #### Strict-Transport-Security -The [`Strict-Transport-Security` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) (sometimes called HSTS) is used to enforce HTTPS (TLS/SSL) connections when loading a site and can be used to enhance the site's security. +The [`Strict-Transport-Security` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) ( +sometimes called HSTS) is used to enforce HTTPS (TLS/SSL) connections when loading a site and can be used to enhance the site's +security. -By default, Altis enables HSTS with the value `max-age=31536000; includeSubDomains`. You can configure the header using the `strict-transport-security` setting: +By default, Altis enables HSTS with the value `max-age=31536000; includeSubDomains`. You can configure the header using +the `strict-transport-security` setting: ```json { - "browser": { - "strict-transport-security": "max-age=3600" - } + "browser": { + "strict-transport-security": "max-age=3600" + } } ``` @@ -195,64 +204,71 @@ You can also switch the header off completely by setting this to false: ```json { - "browser": { - "strict-transport-security": false - } + "browser": { + "strict-transport-security": false + } } ``` Finally, if you set the value to `null` then Altis _will_ send the header but only if the current request is already using HTTPS. - #### X-Content-Type-Options -By default, Altis adds a [`X-Content-Type-Options` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) with the value set to `nosniff`. This prevents browsers from attempting to guess the content type based on the content, and instead forces them to follow the type set in the `Content-Type` header. +By default, Altis adds +a [`X-Content-Type-Options` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) with the value +set to `nosniff`. This prevents browsers from attempting to guess the content type based on the content, and instead forces them to +follow the type set in the `Content-Type` header. -This should generally always be sent, and your content type should always be set explicitly. If you need to disable it, set `browser.nosniff-header` to `false`: +This should generally be sent, and your content type should always be set explicitly. If you need to disable it, +set `browser.nosniff-header` to `false`: ```json { - "browser": { - "nosniff-header": false - } + "browser": { + "nosniff-header": false + } } ``` - #### X-Frame-Options -By default, Altis adds a [`X-Frame-Options` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) with the value set to `sameorigin`. This prevents your site from being iframed into another site, which can prevent [clickjacking attacks](https://en.wikipedia.org/wiki/Clickjacking). +By default, Altis adds a [`X-Frame-Options` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) with +the value set to `sameorigin`. This prevents your site from being embedded into another site with an `iframe`, which can +prevent [clickjacking attacks](https://en.wikipedia.org/wiki/Clickjacking). -This should generally always be sent, but in some cases, you may want to allow specific sites to iframe your site, or allow any sites. To disable the automatic header, set `browser.frame-options-header` to `false`: +This should generally be sent, but in some cases, you may want to allow specific sites to embed your site. To disable the automatic +header, set `browser.frame-options-header` to `false`: ```json { - "browser": { - "frame-options-header": false - } + "browser": { + "frame-options-header": false + } } ``` You can then send your own headers as needed. We recommend hooking into the `template_redirect` hook to send these headers. - #### X-XSS-Protection -By default, Altis adds a [`X-XSS-Protection` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection?) with the value set to `1; mode=block`. This prevents browsers from loading if they detect [cross-site scripting (XSS) attacks](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)). +By default, Altis adds a [`X-XSS-Protection` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection?) +with the value set to `1; mode=block`. This prevents browsers from loading if they +detect [cross-site scripting (XSS) attacks](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)). -This should generally always be sent. If you need to disable it, set `browser.xss-protection-header` to `false`: +This should generally be sent. If you need to disable it, set `browser.xss-protection-header` to `false`: ```json { - "browser": { - "xss-protection-header": false - } + "browser": { + "xss-protection-header": false + } } ``` ### Restrict CORS origins -By default, WordPress will allow REST API requests from any origin. You can add a filter to `altis.security.browser.rest_allow_origin` to restrict [CORS origins](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). +By default, WordPress will allow REST API requests from any origin. You can add a filter +to `altis.security.browser.rest_allow_origin` to restrict [CORS origins](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). To completely disallow external requests, set the filter to `false`: @@ -261,22 +277,24 @@ add_filter( 'altis.security.browser.rest_allow_origin', '__return_false' ); ``` To allow specific origins only: + ```php add_filter( 'altis.security.browser.rest_allow_origin', function ( bool $allow, string $origin ) : bool { - $allowed_origins = [ - 'https://www.example.com', - ]; + $allowed_origins = [ + 'https://www.example.com', + ]; - if ( in_array( $origin, $allow_origins, true ) ) { - return true; - } + if ( in_array( $origin, $allow_origins, true ) ) { + return true; + } return false; }, 10, 2 ); ``` To disallow all .local domains: + ```php add_filter( 'altis.security.browser.rest_allow_origin', function ( bool $allow, string $origin ) : bool { if ( false !== strpos( $origin, '.local' ) ) { diff --git a/docs/disable-accounts.md b/docs/disable-accounts.md index faa7d8d..4de5a9d 100644 --- a/docs/disable-accounts.md +++ b/docs/disable-accounts.md @@ -1,19 +1,24 @@ # Disable Accounts -Altis provides the ability to disable accounts without having to actually delete user accounts. This allows for disabling accounts without reassigning content or changing any other user details, preserving any content created by the users. +Altis provides the ability to disable accounts without having to actually delete user accounts. This allows for disabling accounts +without reassigning content or changing any other user details, preserving any content created by the users. ## Usage -Access the Users screen in the Network Admin, from there you can disable or re-enable individual accounts, or use the bulk actions to make the changes. +Access the Users screen in the Network Admin, from there you can disable or re-enable individual accounts, or use the bulk actions +to make the changes. Disabled accounts have the following effects applied: * Their password will be reset to a random, unguessable 40 character password. * Their existing login sessions will be reset, logging them out from all sites. * They will be blocked from logging in with a message that their account is disabled. -* The account cannot perform any actions even if they do regain access (i.e. using third-party authentication plugins for access keys) +* The account cannot perform any actions even if they do regain access (i.e. using third-party authentication plugins for access + keys) -All other details of the accounts are preserved, and accounts can be reactivated. This includes per-site roles, so their access can easily be restored if desired. It also includes email addresses (preserving Gravatars), and in some cases, email messages will continue to be sent to their accounts. +All other details of the accounts are preserved, and accounts can be reactivated. This includes per-site roles, so their access can +easily be restored if desired. It also includes email addresses (preserving Gravatars), and in some cases, email messages will +continue to be sent to their accounts. **Note**: Custom email code should check user capabilities before sending any potentially sensitive email. @@ -23,14 +28,14 @@ You can also set the `security.disable-accounts` setting to `false` in `composer ```json { - "extra": { - "altis": { - "modules": { - "security": { - "disable-accounts": false - } - } - } - } + "extra": { + "altis": { + "modules": { + "security": { + "disable-accounts": false + } + } + } + } } ``` diff --git a/docs/password-security.md b/docs/password-security.md index c714e47..ee30f2c 100644 --- a/docs/password-security.md +++ b/docs/password-security.md @@ -1,6 +1,6 @@ # Password Security -## Bcrypt Password Hashing +## bcrypt Password Hashing Altis uses the `wp-password-bcrypt` library (from [Roots](https://github.com/roots/wp-password-bcrypt)) to provide bcrypt password hashing for WordPress. This library is a drop-in replacement for WordPress' default password hashing @@ -15,32 +15,34 @@ To protect against brute force and dictionary attacks, Altis enforces a minimum Passwords are scored one of four possible scores: -* Very Weak (score: 1) -* Weak (score: 2) -* Medium (score: 3) -* Strong (score: 4) + +* 'Very Weak' (score: 1) +* 'Weak' (score: 2) +* 'Medium' (score: 3) +* 'Strong' (score: 4) -By default, passwords which score below 2 (i.e. Very Weak passwords) will be rejected. +By default, passwords which score below 2 (i.e. 'Very Weak' passwords) will be rejected. + -To change the minimum password strength, set the `modules.security.minimum-password-strength` setting to a different score (i.e. `3`). +To change the minimum password strength, set the `modules.security.minimum-password-strength` setting to a different score +(i.e. `3`). To disable the minimum password strength checks, set the `modules.security.minimum-password-strength` setting to `0`. - ## Additional strength checks -To add additional strength checks, the `altis.security.passwords.is_weak` filter is provided. This filters the -boolean `$is_weak` which can be set to `true` to reject a password. +To add additional strength checks, the `altis.security.passwords.is_weak` filter is provided. This filters the boolean `$is_weak` +which can be set to `true` to reject a password. For example, to reject any passwords which contain the word "human": ```php add_filter( 'altis.security.passwords.is_weak', function ( $is_weak, $password ) { - if ( strpos( $password, 'human' ) !== false ) { - return true; - } + if ( strpos( $password, 'human' ) !== false ) { + return true; + } - return $is_weak; + return $is_weak; }, 10, 2 ); ``` @@ -49,10 +51,10 @@ password strength score for administrators or for specific capabilities. ```php add_filter( 'altis.security.passwords.is_weak', function ( bool $is_weak, string $password, WP_User $user, array $results ) { - if ( $user->has_cap( 'publish_newsletter' ) && ( $results['score'] < 4 ) ) { - return true; - } + if ( $user->has_cap( 'publish_newsletter' ) && ( $results['score'] < 4 ) ) { + return true; + } - return $is_weak; + return $is_weak; }, 10, 4 ); ``` diff --git a/docs/require-login.md b/docs/require-login.md index 3fb3934..8d8556b 100644 --- a/docs/require-login.md +++ b/docs/require-login.md @@ -1,21 +1,25 @@ # Require Login -By default, all websites are publicly accessible. In some situations, you may want to require users to be logged in to access the website. This is especially useful when in pre-launch mode. +By default, all websites are publicly accessible. In some situations, you may want to require users to be logged in to access the +website. This is especially useful when in pre-launch mode. Environments running in Cloud that are not of type `production` have the `require-login` feature enabled by default. Enabling Require Login for an environment will also prevent it from being indexed with search engines. -**Note:** Require Login only applies to URLs served from your [application servers](docs://cloud/architecture.md), and will not apply to `/uploads/` or `/tachyon/` URLs. - +**Note:** Require Login only applies to URLs served from your [application servers](docs://cloud/architecture.md), and will not +apply to `/uploads/` or `/tachyon/` URLs. ## Controlling Site Access -Requiring login on individual sites is as easy as unchecking the site's public setting in the Edit Site screen. To access this setting, go to [My Sites > Network Admin > Sites](internal://network-admin/sites.php) and then click the URL for the site you want to edit. From there you check the box for whether the site is public or not under the "Attributes" section. +Requiring login on individual sites is as easy as unchecking the site's public setting in the Edit Site screen. To access this +setting, go to [My Sites > Network Admin > Sites](internal://network-admin/sites.php) and then click the URL for the site you want +to edit. From there you check the box for whether the site is public or not under the "Attributes" section. ## Excluding Pages and Endpoints -In certain cases you may need to exclude a URL or PHP file from redirecting to the login page when Require Login is active. This is possible using the `hm-require-login.allowed_pages` filter: +In certain cases you may need to exclude a URL or PHP file from redirecting to the login page when Require Login is active. This is +possible using the `hm-require-login.allowed_pages` filter: ```php add_filter( 'hm-require-login.allowed_pages', function ( array $allowed, ?string $page = null ) : array { @@ -26,39 +30,45 @@ add_filter( 'hm-require-login.allowed_pages', function ( array $allowed, ?string }, 10, 2 ); ``` -The 2nd parameter `$page` is populated from WordPress's `$pagenow` global variable. If you need to make exceptions for frontend URLs this value will be `index.php`, as such this will require additional logic to restrict which requests are allowed. +The 2nd parameter `$page` is populated from WordPress's `$pagenow` global variable. If you need to make exceptions for frontend URLs +this value will be `index.php`, as such this will require additional logic to restrict which requests are allowed. To allow a custom REST API endpoint you would do something similar to the following example: ```php add_filter( 'hm-require-login.allowed_pages', function ( array $allowed, ?string $page = null ) : array { - if ( $_SERVER['REQUEST_URI'] === ( '/' . rest_get_url_prefix() . '/public-endpoint/' ) ) { - $allowed[] = $page; - } + if ( $_SERVER['REQUEST_URI'] === ( '/' . rest_get_url_prefix() . '/public-endpoint/' ) ) { + $allowed[] = $page; + } - return $allowed; + return $allowed; }, 10, 2 ); ``` ## Environment Specific Overrides -You can also set the `security.require-login` setting to `true` in `composer.json` to require all users to be logged in to view the website (this will override individual sites' public setting). You can require login for all environments by adding the setting directly under `altis.modules`, or individual environments by nesting it within `altis.environments`. The following example sets all environments except for local to require login: +You can also set the `security.require-login` setting to `true` in `composer.json` to require all users to be logged in to view the +website (this will override individual sites' public setting). You can require login for all environments by adding the setting +directly under `altis.modules`, or individual environments by nesting it within `altis.environments`. The following example sets all +environments except for local to require login: ```json -"altis": { - "modules": { - "security": { - "require-login": true - } - }, - "environments": { - "local": { - "modules": { - "security": { - "require-login": false - } - } - } - } +{ + "altis": { + "modules": { + "security": { + "require-login": true + } + } + }, + "environments": { + "local": { + "modules": { + "security": { + "require-login": false + } + } + } + } } ```