Giter Site home page Giter Site logo

altis-security's Introduction

Altis Security

Security features for Altis.

Packagist Version

Security

The security module provides high-level security focused features to Altis. It is not meant to "make Altis secure", and all other modules and components of Altis should be secure in their own right. This module is concerned with security features such as Multi Factor Authentication, Password Strength Rules, Password Expiration, Audit Logging etc.

altis-security's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar ferschubert-hm avatar jazzsequence avatar jerico avatar joehoyle avatar kevinlangleyjr avatar kovshenin avatar mikelittle avatar ntwb avatar pdewouters avatar rmccue avatar roborourke avatar sambulance avatar shadyvb avatar wisyhambolu avatar yumito avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

altis-security's Issues

Asset URLs not registered properly in subdomain sites

Steps to reproduce:

  1. For a multisite network e.g. something.altis.cloud, define a subsite at something.altis.cloud/some/path
  2. Force 2FA on that environment
  3. Observe that no scripts or styles load on the "choose your 2FA provider" form when trying to log in

I expected a functional 2FA form. Instead, we see 404s for all the two-factor plugin scripts and styles, because they get registered as,

https://subdomain.site.altis.cloud/sub/directory/vendor/humanmade/two-factor/assets/js/force-2fa.js

instead of

https://subdomain.site.altis.cloud/vendor/humanmade/two-factor/assets/js/force-2fa.js

The two-factor plugin (both our fork and the upstream copy) use plugins_url( '...blah.js', __FILE__ ) to deduce the URL for the asset. That resolves to full/site/path/vendor... instead of /vendor... when used in an Altis site where the plugin is installed to vendor/humanmade/two-factor.

Acceptance criteria:

  • Scripts and styles are registered with valid URIs

Remove limit login attempts plugin

This plugn hasn't been working properly and can result in db writes during logins - this is a problem during brute force login attempts.

We have infra level protection already anyway so it's somewhat superfluous.

Related to #79

Fatal error when running `wp core multisite-convert`

Original report from @fklein-lu

Describe the bug

This error is generated when running the CLI command wp core multisite-convert when migrating a single site install to Altis.

PHP Fatal error:  Uncaught Error: Call to undefined function Altis\Security\get_site() in /usr/src/app/vendor/altis/security/inc/namespace.php:84

To Reproduce
Steps to reproduce the behavior:

  1. Get a single site install database (spin up a plain copy of chassis and export for example)
  2. Import the single site db into a clean Altis install
  3. Search & replace the URLs
  4. Run wp core multisite-convert

Expected behavior

The command should be successful with no errors.

Enabling basic auth for local & prod environments doesn't work

Steps to reproduce:

  1. Follow the docs to add local environment specific config for php basic auth
  2. Load the site
  3. Basic auth not requested / required

There should be a prompt to enter the username and password.

I think the way environment checks are incorporated into the plugin is very confusing. Environments should be opted in to explicitly by type or globally as required.

In the meantime though the plugin integration should be fixed so that the docs are accurate.

Acceptance criteria:

  • ...

Impossible to disable require-login for non-public sites

It's impossible to disable require-login on a non-public site. This means it's not possible to have a site that is publicly accessible but blocked to search engines, because it will always require visitors to be logged in to access it.

Related docs: https://docs.altis-dxp.com/security/require-login/#overrides

Steps to reproduce:

  1. Put config in place to disable require-login:
    "modules": {
    	"security": {
    		"require-login": false
    	}
    }
    
  2. From the Settings -> Reading menu, check the Discourage search engines from indexing this site checkbox
  3. Try to access the site when you're not logged in
  4. Observe that you're redirected to the login screen despite require-login being false

Problem code:

Altis Security should require hm-require-login ^1.0.4

Altis Security versions linked to Altis on WP 5.6+ should require hm-require-login 1.0.4 which has fixes for the applications password feature that debuted with WP 5.6. Otherwise it's possible to pin Altis Security to a version that breaks APW support.

Require login breaks all REST API OPTIONS requests even when logged in

Steps to reproduce:

  1. Enabled require-login in the Altis config.
  2. Send an http OPTIONS /wp-json/ -H 'Cookie: wordpress_logged_in.... request (psuecode)
  3. Observe the response is empty (should be populated with all routes information)

Expected:

  1. Response should be JSON blob of all endpoints
  2. Allow HTTP header should be present

Acceptance criteria:

  • Logged in requests should have normal REST API OPTIONS behavior

The code that is causing this is in https://github.com/humanmade/hm-require-login/blob/master/inc/namespace.php#L21, as you can see, it will shortcircuit all REST API OPTIONS requests regardless of whether the user is logged in.

As far as I can tell, all those REST API exceptions should be placed below the logged in checks at https://github.com/humanmade/hm-require-login/blob/master/inc/namespace.php#L49-L61

Documentation linting issues

This ticket addresses the issues raised by the documentation linting

composer dev-tools lintdocs all -l packages/security

File Issues:

None

Markdown issues:

Linting: 8 file(s)
Summary: 284 error(s)

Style issues:

11 errors, 0 warnings and 0 suggestions in 8 files.

require-login cannot be disabled

The require-login functionality can't be disabled. While the value can be set to true in the config, this enables it on all sites. There's no reverse of this to disable the functionality altogether.

Repeated login attempts do not block users

hm-limit-login-attempts does not appear to be functioning as designed. A support request came in describing being able to attempt a log in more than 5 times. I've been able to reproduce the issue by attempting logging in with bogus credentials on a test environment. Often It simply reset the counter to "3 attempts remaining." Other times it'd say "too many failed login attempts," and on the subsequent request display another counter. Eventually, our nginx configuration kicks in and blocks the repeated attempts.

Steps to reproduce:

  1. Attempt to log in with any user more than 4 times.

Describe what you expected to see:

User is blocked from an attempted login.

Screenshots are helpful too if the bug is visual:

I have one recording that's more in line with my original description, but it had information from 1Password I didnt want to expose. This one is more sanitized, but with less of what I observed:

limit logins 2020-12-04 12_46_15

Acceptance criteria:

  • ...

Enforce 2FA for admins by default

Acceptance criteria:

  • Change the default 2FA configuration so that is:
    • required for super admins by default
    • required for admins by default
    • only applies for non-local environments
    • Update docs

Grammar issue in Documentation

The recommended setup is to define everything in your composer.json file, including the username and passwords. The same configuration in the below, manual setup example could be handled in the Composer file like this:

https://github.com/humanmade/altis-security/blob/master/docs/basic-auth.md

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:

Styling issues on Force 2FA Screen

  1. Generate Verification Codes and Register New Key links are overlapping each other in the top-right of the screen.

Screenshot 2019-11-19 at 16 41 27

  1. The Logout link is obscured by the Query Monitor bar.

Screenshot 2019-11-19 at 16 41 33


Acceptance criteria:

  • The links in the top right are no longer overlapping but side by side
  • On the login screen when query monitor is active add bottom padding to the body, logout link should not be obscured

Basic auth cannot be used in production environments

Steps to reproduce:

  1. Set basic auth config up per the instructions in the docs
  2. Set an environment override for production
  3. View on a production environment

The site should have Basic Auth required, however it does not.

This is due to the merging of the config always setting the php-basic-auth config rather than using environment overrides correctly, which means it then has environment-specific behaviour. Due to this, you cannot enable it on production per the docs.

I'm filing a hotfix for this which explicitly checks the environment configuration, but we should look at reworking this to be standard instead.

Acceptance criteria:

  • ...

The 2FA/MFA setup screen is confusing

When a user gets added or invited to a site which requires multi-factor authentication to be enabled on their account, the initial MFA setup workflow is very confusing. It appears to be optimised for allowing users to use more than one MFA method, but I would expect the most common use case is using one MFA method.

Screenshot

This is the interstitial MFA configuration screen that a user sees after signing in for the first time. You can trigger this on an account by deleting the _two_factor_* user meta fields for a user.

Problems

  • If a user wants to use TOTP with Google Authenticator, Authy, 1Password, etc, they have to click three buttons: the "Enabled" checkbox, the "Primary" radio button, and the "View Options" toggle. If they fail to click all three of those then the setup fails.
  • There's no client-side checking of the required TOTP code field
  • No error message is shown if the user submits the form without having filled out everything they need to.
  • There's an unnecessary distinction between enabled and primary on this screen - how many users use multiple MFA methods compared to those who just use one?
  • The user is not reminded or forced to generate backup verification codes, greatly increasing the probability of them locking themselves out of their account if they lose access to their MFA method.
  • Being able to select "Backup Verification Codes" as the primary or default MFA method makes no sense.
  • The empty "Security Keys" listing table at the bottom of the screen makes no sense. This should only be displayed after the user chooses the FIDO option and sets up a new key.
  • It should not be possible to set an MFA method as "Primary" without it also being "Enabled".

Two-factor scripts depend on /vendor paths being web-accessible

The two-factor auth plugin expects to be installed as a normal WP plugin (or MU plugin), and there are some issues with generating the correct URLs for the plugin modules in a subdirectory multisite environment. Additionally, if for whatever reason the /vendor directory is not accessible over HTTP due to client domain configuration, the two-factor system does not work at all.

It would be helpful to have some way to "proxy" the scripts, or load them from a virtual route within the site domain, rather than hitting vendor/ directly.

Retain user details for deleted accounts

From an internal survey:

Retain deleted users so that posts and Stream logs continue to show a history of users for audit trails

(HMers can see the internal issue below.)

Require secure cookies & HttpOnly

We could filter the authentication cookies to be set with the secure flag and also to be HTTP Only which prevents accessing the cookies from client side script.

https://www.owasp.org/index.php/HttpOnly

Equivalent PHP ini directives.

ini_set('session.cookie_httponly', true);
ini_set('session.cookie_secure', true);
ini_set('session.use_only_cookies', true);

Idea: Web push notification method for recieving 2fa codes

Wanted to note this idea down while it was fresh in my head.

At the cybersec conference I was at the holy grail for 2FA was to keep everything "in-band" eg. encrypted network traffic between the user and the app only rather than going "out-of-band" via email or sms. Web push / desktop notifications would be the way to achieve this.

Granted not an issue really when using 1pass with the 1 time password field but this method might actually be easier to set up / use for some people and would be an interesting one to explore.

One for down the line.

Require login and basic auth block robots.txt

Some non-production sites are being indexed unintentionally, or at least some content is as the require login and basic auth features both block access to robots.txt which is generated by WP.

Acceptance criteria:

  • Require login allows unauthenticated access to robots.txt
  • Basic auth allows unauthenticated access to robots.txt
  • If either require login or basic auth is enabled robots.txt returns:
    User-agent: *
    Disallow: /
    

2FA edit user shows options for current user

Steps to reproduce:

  1. Edit a user other than yourself
  2. Update 2FA Enabled option to "email only" (or something other than current setting
  3. Hit Update
  4. Observe option were not changed

It #turnsout that the currently selected options are for the current user, not the user you are editing, so though updating a user will work, you won't see the changes. And if you were to go update that user's name or something, they would be, by default, updated with the current user's options.

This is happening in https://github.com/humanmade/two-factor/blob/force-2fa/class.two-factor-core.php#L625 get_available_providers_for_user takes a WP_User object, not an id, and this function falls backs to the current user.

Stream plugin does not exist

After finishing the setup and visiting the admin, I see a notification: `

The plugin stream/stream.php has been deactivated due to an error: Plugin file does not exist.

Document public and private site behaviour with require login feature

We currently don't document the ways in which the require login feature affects the behaviour of the public/private site setting in WordPress. This is used to prevent search engine indexing typically but can be confusing because it hard codes the
"Discourage search engines" option value even though it looks like it can be changed in the admin still.

Strong passwords are not enforced when resetting lost password

When resetting a lost password through wp-login.php, it is possible to use a weak password by ticking the 'Confirm use of weak password' box.

Screenshot 2021-11-15 at 20 58 37

When resetting through the user's profile, the confirmation box has been removed to enforce a strong password. We should remove the checkbox here too.

Tested on a fresh local install of V9 (screenshot above) and an existing client project on Altis V8. Screencast available in Slack.

Support configuring required 2fa

2fa has options to force-require it for all users, and also on a per-role basis. This is done via the network admin atm, but we should add support for configuring via the platform config.

Application Passwords triggers 401 on REST API requests when using Basic Auth

Steps to reproduce:

  1. Enable and configure the Basic Auth functionality for an environment
  2. Create an Application Password (this triggers WP_Application_Passwords::is_in_use() to return true by setting the using_application_passwords network setting)
  3. Send a REST API request to the environment with your Basic Auth setting set

Publicly-accessible API endpoints should be accessible; instead, they return a 401.

This is increased priority, as if sending REST API requests from the browser, the 401 returned by Application Passwords causes the browser's internal auth cache to be reset, which requires users to log in again repeatedly.

Basic Auth should take priority here as it's site-wide, but this will mean that Application Passwords can't actually be used in combination. I think that's an acceptable compromise, as regular Require Login can be used in those cases instead, but we should ensure it's documented.

Acceptance criteria:

  • Sending a request with Basic Auth headers to a public REST API endpoint (e.g. /wp-json/) should return a 200 response
  • Documentation should indicate Basic Auth is not compatible with Application Passwords

WP Stream tables not created on local envs

Seems to be after updating to the official package, we will need to hook into either wp altis migrate or some other hook to ensure the tables are created just in time.

Security option check throws notice message

Steps to reproduce:

  1. Nothing set about security in composer.json. This results that 2 factor auth is not used.
  2. Open page.
  3. Find PHP error message in Query Monitor

https://github.com/humanmade/altis-security/blob/master/inc/namespace.php#L108

The line above code is like below:

if ( is_array( $config ) && ( ! empty( $config['required'] ) || is_bool( $config['required'] ) ) ) {
	return $config['required'];
}

May be this condition means "config is array and 'required' param is set, use it".
But this condition test config with is_bool() in spite of non-existent key. This causes notice message.

It's better.

if ( is_array( $config ) && isset( $config['required'] ) && is_bool( $config['required'] ) ) {
	return $config['required'];
}

スクリーンショット 2021-08-06 15 51 00

Thanks!

WordPress db error: wp_stream table already exists

I noticed this item in the debug log, but I'm not able to reproduce - might've been only at setup as I've recently setup the local server from scratch

[01-Jul-2020 13:45:51 UTC] WordPress database error Table 'wp_stream' already exists for query CREATE TABLE wp_stream (
			ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,
			site_id bigint(20) unsigned NOT NULL DEFAULT '1',
			blog_id bigint(20) unsigned NOT NULL DEFAULT '1',
			object_id bigint(20) unsigned NULL,
			user_id bigint(20) unsigned NOT NULL DEFAULT '0',
			user_role varchar(50) NOT NULL DEFAULT '',
			summary longtext NOT NULL,
			created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
			connector varchar(100) NOT NULL,
			context varchar(100) NOT NULL,
			action varchar(100) NOT NULL,
			ip varchar(39) NULL,
			PRIMARY KEY  (ID),
			KEY site_id (site_id),
			KEY blog_id (blog_id),
			KEY object_id (object_id),
			KEY user_id (user_id),
			KEY created (created),
			KEY connector (connector),
			KEY context (context),
			KEY action (action)
		) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci made by include('phar:///usr/local/bin/wp/php/boot-phar.php'), include('phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/wp-cli.php'), WP_CLI\bootstrap, WP_CLI\Bootstrap\LaunchRunner->process, WP_CLI\Runner->start, WP_CLI\Runner->load_wordpress, require('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, WP_Stream\Install->verify_db, do_action('wp_stream_before_db_notices'), WP_Hook->do_action, WP_Hook->apply_filters, WP_Stream\Install->check, WP_Stream\Install->install, dbDelta, Altis\Cloud\DB->query, wpdb->print_error

It doesn't seem to break anything obvious and the audit log is displayed normally

Add framework for setting script/style integrity hashes

We should add a framework for Sub-Resource Integrity (SRI). This adds an integrity parameter to script/link[rel=style] tags which contains a hash of the resource, allowing sites to avoid being MITM'd as well as CDNs changing assets out on you.

For assets loaded from the filesystem, we can automatically generate integrity hashes and serve these up. We should disable this behaviour for local environments, as it's common to change assets without bumping the version.

For external assets, we can provide an API for registering integrity hashes.

Add PHP Basic Auth feature

In some cases require-login isn't the best solution to building and developing a site. You may need to test authentication scenarios on staging / dev servers for instance.

@jazzsequence has built a PHP Basic Auth plugin that would be straightforward to integrate and configure as an Altis feature.

https://github.com/humanmade/PHPBasicAuth

Acceptance criteria:

  • Feature can be enabled through composer.json config, off by default
  • Username and password can be configured in composer.json
  • Documentation added

Warn user more clearly when their password is not accepted

When a password is not strong enough, the submit button on the profile page is disabled. We need to be clearer about this, as particularly on long profile pages, it may not be obvious why the button is disabled.

Suggested microcopy: "Your password does not meet the minimum requirements. Please select a stronger password."

  • When the profile submit button is disabled, display a message warning the user about their password strength next to the button.

Original description follows

If a user tried to change their password to something considered too weak, they are unable to update their profile. There is no error state to tell them why this is the case.

Screenshot 2019-11-19 at 16 53 16

With regular WordPress, there is a checkbox that the user must check to confirm the password. This appears and disappears depending on the password strength, drawing the users attention to it.

Kapture 2019-11-19 at 17 00 23

As this functionality has been removed, we should instead show an additional warning that the password is not accepted.


Acceptance criteria:

  • Show a warning message if the password is too weak next to the password field as it is typed

Stream DB tables cannot be trusted to exist

The Stream plugin only creates its tables on activation, and the way it is used in this project, that step will never necessarily be hit. This means that (especially in local environments) you can end up in a situation where you have no DB tables for stream, and your errors are littered with warnings about it.

Steps to reproduce:

  1. composer server cli -- db clean and reinstall your local install, or import a database
  2. Observe that you begin to see errors like this in your logs:
WordPress database error Table 'wordpress.wp_stream' doesn't exist

I would expect to see no errors of this type, because I would expect the Altis Security module to orchestrate ensuring the tables are present if they did not previously exist.

As a stopgap for now, you can manually recreate these tables by invoking the stream plugin activation directly:

wp_stream_get_instance()->install->check();

Acceptance criteria:

  • Stream tables are created if not present, especially in local environments, with no interaction from the developer

Shout-out to @tomjn for figuring this one out, this issue documents discussion we just had in Slack. Tom has opened a related issue on the upstream Stream repo (two of them, actually), but this is something we could fix at this level, too.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.