cymapgt/usercredential

密码认证策略管理包

4.0.0 2020-05-11 23:53 UTC

README

此包可以实现密码认证和政策管理。它可以执行多种类型的检查,以评估用户密码和认证过程是否符合安全建议。

描述

PHP UserCredential 包是一个可插入的服务,允许您验证密码和政策。它根据 OWASP Web 应用最佳实践指南推荐的密码策略集进行验证。

此包还提供了一个接口,允许插入第三方库,特别是用于多因素认证方法。为了说明如何做到这一点,我们已插入 MultiOTP 库(https://github.com/multiOTP/multiotp),用于提供此包的 SMS OTP 和 Google Authenticator TOTP 服务。

安装

通过 Composer 安装应用程序

require "cymapgt/usercredential": "*"

用法

概述

此包旨在用于使用密码进行认证且需要维护某种用户凭证政策的 PHP 应用程序。我们还提供利用 MultiOTP 库的多因素认证服务。

此包的目标是

(版本 1.2 引入)

  • 连续4次非法登录尝试后,账户将被临时锁定10分钟

  • 在第5次连续非法登录尝试时,账户将被无限期锁定

  • 用户不能重复使用过去5个密码

  • 用户密码在45天后过期

  • 当密码不符合所需熵时强制更改密码

  • 所需的最小密码长度为8个字符

  • 最小密码熵为2个大写字母字符,2个小写字母字符,1个数字字符和1个特殊字符

  • 用户不能在密码字符串中使用他们的用户名,或他们的真实姓名(或部分)

(版本 1.3 引入)

  • 密码不能包含超过2个连续字符(例如 aaa

  • 包含密码强度检查类(感谢 Ryan Chouinard 开发了可爱的 Phpass 包(rchouinard/phpasshttps://github.com/rchouinard/phpass),我们为其分支用于强度功能检查,使用了 NIST 和 Wolfram 算法

  • 提供一致的接口,无论使用的后端存储方式如何,都可以用于认证和政策过程

实施 OWASP 指南

  • 所有认证控制都应安全失败:服务不会侵入。服务提供用户配置文件,并且它不会干预,除非它遇到认证或政策问题,此时它抛出 UserCredentialException,然后应该处理该异常

  • 强制执行政策或法规规定的密码复杂度要求。认证凭证应足以抵御部署环境中常见的威胁攻击。(例如,要求使用字母以及数字和/或特殊字符):该服务有一种基于长度和复杂度实现用户定义策略的方法。然而,它不允许设置弱策略,因为它会将用户定义的策略与内置基本策略进行比较,如果用户定义的策略较弱,例如,如果用户定义策略中的密码长度为5个字符,它将回退到基本策略(根据OWASP为8个字符)。注意:回退功能尚未实现。

  • 强制执行政策或法规规定的密码长度要求。通常使用8个字符,但16个字符更好或考虑使用多词密码短语:参见前一点

  • 在达到一定次数的无效登录尝试后强制禁用账户(例如,五次尝试是常见的)。账户必须被禁用足够长的时间以阻止暴力猜测凭证,但不要长到允许执行拒绝服务攻击:该服务将账户临时锁定(基本策略为10分钟)以防止重复尝试。10分钟后,用户可以使用正确的密码成功登录。然而,如果在之后任何时间用户提供了另一个错误的密码,该服务将无限期地锁定账户,这将需要管理员干预才能解锁

  • 强制在下次使用时更改临时密码:当使用弱密码时,该服务将建议/强制在下次尝试登录时更改密码

  • 防止密码重用:该服务可以支持此功能。默认情况下不允许重复使用最后5个密码。用户定义策略可以增加这个数量。我们已经在一些环境中看到使用多达12个以前的密码。

  • 根据政策或法规规定的要求强制更改密码。关键系统可能需要更频繁的更改。重置之间的时间必须由管理员控制:该服务默认密码过期期限为45天。可以使用用户定义策略方法设置更强大的用户定义策略(例如,30天)。

  • 对高度敏感或高价值交易账户使用多因素认证:该服务实现了可以允许多因素认证的接口。

使用该包

命名常量

//UserCredential constants for user authentication
const USERCREDENTIAL_ACCOUNTSTATE_LOGGEDOUT   = 1;
const USERCREDENTIAL_ACCOUNTSTATE_LOGGEDIN    = 2;
const USERCREDENTIAL_ACCOUNTSTATE_LOCKED1     = 3;
const USERCREDENTIAL_ACCOUNTSTATE_LOCKED2     = 4;
const USERCREDENTIAL_ACCOUNTSTATE_RESET       = 5;
const USERCREDENTIAL_ACCOUNTSTATE_SUSPENDED   = 6;
const USERCREDENTIAL_ACCOUNTSTATE_AUTHFAILED  = 7;
const USERCREDENTIAL_ACCOUNTSTATE_WEAKPASSWD  = 8;

/**
 * UserCredential constants for account policy actions. These also serve as
 * exception codes during the authentication and policy check process.
 * Internal Exception codes are documented in the EXCEPTIONS.md file
 */

const USERCREDENTIAL_ACCOUNTPOLICY_VALID         = 1;
const USERCREDENTIAL_ACCOUNTPOLICY_EXPIRED       = 2;
const USERCREDENTIAL_ACCOUNTPOLICY_ATTEMPTLIMIT1 = 3;
const USERCREDENTIAL_ACCOUNTPOLICY_ATTEMPTLIMIT2 = 4;
const USERCREDENTIAL_ACCOUNTPOLICY_REPEATERROR   = 5;
const USERCREDENTIAL_ACCOUNTPOLICY_WEAKPASSWD    = 6;
const USERCREDENTIAL_ACCOUNTPOLICY_NAMEINPASSWD  = 7;

//Password strength constants
const PHPASS_PASSWORDSTRENGTHADAPTER_NIST = 0;
const PHPASS_PASSWORDSTRENGTHADAPTER_WOLFRAM = 1;

构建您的用户档案

  • 此服务与用户和认证信息的后端存储解耦。它需要一个用户档案数组,您应该构建并提供给服务。

示例用户档案

array (
    "username" => "james",
    "password" => "m&$1eLe6Ke()", //Password provided by user when loggin in, else null if youre running this in session and not log in
    "fullname" => "James Rodriguez",
    "passhash" => "bcrypt",
    "passhist" => array( //These should be already stored as encrypted in your backend store and would be of required entropy :)
        \password_hash('abc', \PASSWORD_DEFAULT),
        \password_hash('def', \PASSWORD_DEFAULT),
        \password_hash('ghi', \PASSWORD_DEFAULT),
        \password_hash('jkl', \PASSWORD_DEFAULT),
        \password_hash('mno', \PASSWORD_DEFAULT),
        \password_hash('pqr', \PASSWORD_DEFAULT),
        \password_hash('stu', \PASSWORD_DEFAULT),
        \password_hash('vwx', \PASSWORD_DEFAULT),
        \password_hash('xyz', \PASSWORD_DEFAULT)
    ),
    "policyinfo"=>array(
        'failed_attempt_count' => 0,
        'password_last_changed_datetime' => new \DateTime('2014-05-04'),
        'last_login_attempt_datetime'    => new \DateTime('2014-05-16 23:45:10')
    ),
    "account_state" => \USERCREDENTIAL_ACCOUNTSTATE_LOGGEDIN
);

认证用户

该服务包含3种密码认证服务,您可以选择使用。有关使用每种服务以及实施密码策略的Wiki正在开发中。本文http://bit.ly/29m2aWL解释了使用数据库作为后端存储可能会有助于开始使用此包。

检查这些服务的测试文件以获取它们的工作原理的文档。

UserCredentialPasswordLoginService

此服务仅执行密码认证。要使用此服务,您需要将其连接到您正在使用的认证框架/插件。

UserCredentialSmsTokenLoginService

此服务生成令牌,并将其发送到映射到用户的手机号码或电子邮件。此类扩展了UserCredentialPasswordLoginService,执行认证的第一步。

UserCredentialGoogleAuthLoginService

此服务生成每30秒变化一次的TOTP令牌。因此,这些可以支持Google Authenticator。此类扩展了UserCredentialPasswordLoginService,执行认证的第一步。

认证后强制执行密码策略

use cymapgt\core\application\authentication\UserCredential;
//Build user Profile First (see sample above)

$userCredentialService = new UserCredentialManager($userProfile);

try {
    $usercredentialService->validateEntropy();
    $usercredentialService->validateLength();
    $usercredentialService->validateConsecutiveCharacterRepeat();
    $checkPolicy = true;
} catch (UserCredentialException $enException) {
    $enExceptionId = $enException->getCode();
    $checkPolicy = false;
    //Handle the Exception...
}

if ($checkPolicy) {
    try {
        $usercredentialService->validatePolicy();
    } catch (UserCredentialException $plcyException) {
        //Handle the Exception...
    }
}

//Yay, we made it. Do something Amazing ... :)

会话期间强制执行密码策略

  • 会话期间的使用与上述相同,但您不能使用 validateEntropy() 等... 只能使用 validatePolicy() 等,例如在访问资源时,可能会计算45天已过并抛出异常,要求更改密码

使用Phpass验证密码强度

强度检查方法为静态,以便在不需要实例化 UserCredentialManager 类的情况下使用。因此,它也可以在用户更改密码或设置新密码时协助用户。

use cymapgt\core\application\authentication\UserCredential; 
    
$passwordStrength = UserCredentialManager::passwordStrength($passwordString);

//do something like show strength bar, or enforce stronger password

测试

该软件包提供了PHPUnit测试

贡献

  • 发送电子邮件至 @rhossis 或通过Skype联系
  • 在GitHub上分叉存储库以开始对主分支(或从中分叉)进行更改。
  • 您将被添加为贡献者

许可证

BSD 3 条款