创新后的 PHP: 现代 PHP 中之密码安全性

从今同开始,PHP 就是千篇一律种植也构建网站要那个的编程语言。这无异于意见植根于 PHP
核心的吃水比其余其他编程语言都使杀,这或许便是 PHP 在 Web
应用程序构建领域转移得都维持这样流行的一个因。但是,当 PHP 于上世纪 90
年代中首次于设计出时,术语 Web
应用程序
 甚至还非在。因此,密码保护不是 PHP
创建者投入资源规划的功能之一。毕竟,在只是下 PHP
在网页上栽入站点访问计数器或一个窜日期戳时,您不用再度担心密码问题。

而是 20
年病逝矣,现在,人们几乎无法想像创建一个非关乎为密码保护的用户帐户的 Web
应用程序。PHP
程序员最要害的办事就是是采用新型、最安全的主意来保护帐户密码。为夫,PHP
5.5 添加了一个出于 Anthony Ferrara
(@ircmaxell)
创建的新的密码哈希库。该库提供了部分函数,您而采用它们经时的超级实践方法来拍卖才为密码加密。其他职能旨在满足未来底安要求,以便就电脑和攻击者水平更提高,您一直可抢先坏人一步。本文将深入介绍该库的函数,以及哪尽得力地动用它们。

有关以系列

PHP 作为一个活蹦乱跳的开源项目于不停的改善,目前呢广大 Web 网站提供支持。PHP
早期是同一种植模板语言,从当下到现,PHP
已经历了显眼的浮动。如果您多年尚未运用过还是评估了 PHP
技术,那么您大可能几认不发出目前底组成部分 PHP
项目。本系列文章以于你出示时的 PHP 功能,以及哪运用本底 PHP
来构建现代化的、安全的网站。

有惊无险哈希的根本

  • class=”ibm-pullquote-open”>“像比牙刷一样比你的密码。不要被别任何人以它们,并且每隔
    6 个月转移一个初密码。”*

Clifford Stoll

以下建议可能不言自明(至少自己要是这样):绝不将用户的密码存储吗公开。始终以 哈希算法 等就为加密算法存储密码的加密版本,以要其他能够访问您的钱户数据库的人口犹爱莫能助察觉而用户之密码。此警告不仅适用于维护用户远离通过获取数据库访问权来伤害你的网站的食指。您还亟需防御您组织内拥有恶意企图的人数。

嗬是哈希?

哈希算法 获取一个别长度的字符串并应用同样的方开创一个哈希值:一种固定长度的字符串表示。每次传入同一个原始字符串,您还见面收取一模一样的哈希值。这是一个特为经过,您无法从中得到原始字符串。

以此需要变换得愈紧,因为背的凡,许多用户对大多个网站以同样之密码。如果有人会访问您的某某用户之电子邮件地址和旧密码,他大可能能够访问该用户以另外网站上的帐户。

破解速度

抱有哈希值不是以同一种植方式创造。那么你就好运用各种算法来创造哈希值。过去常用的一定量独算法是 MD5 和 SHA-1。如今兵不血刃的微机很容易破解这简单种植算法。举例而言,已出 软件 能够在么
GPU 上因每秒 36.5 亿蹩脚计算的速率破解 MD5 的哈希值,以各级秒 13.6
亿次等计算的速度破解 SHA-1
的哈希值。依据这样的速率,根据密码的复杂和尺寸,可以当匪顶一半钟头外破解一个哈希值。

从而,使用于盘算上再次复杂的哈希算法很要紧。您不只想只要重新增长的哈希值(这会减少哈希碰撞的机会,也就是说,减少两独短语生成相同之哈希值的机),您还愿意生成哈希值所消费的日尽可能长。为什么?对于你自己之
Web
应用程序,用户每次登录时只是得等待生成一个密码哈希值一次于。如果等待时不断
1
秒(或者甚至有数秒),用户不见面关注,甚至不见面注意到。但透过将破解尝试从各级秒
36 亿这计算的减速及各个秒 1 次,任何尝试的难度还见面呈指数级增长。

彩虹表

若还需要防守彩虹表。彩虹表(比如您可以于 md5cracker.org 上访问的
MD5)是哈希值的逆向查找表。该表的主创者预先计算有所大单词、短语、修改的单词,甚至随意字符串的
MD5
哈希值。能够访问某个哈希值的人口可当查找表中输入其,发现用于转移它的密码,从而使得地反转之只有为经过。破解
MD5 哈希值的相对比较逊色的拍卖资产令创建彩虹表成为了说不定。

呢一个盘算达复杂的算法生成彩虹表需要花长得差不多的辰。但仍可能毕其功于一役,而且创建者只待交一次性的奋力。合适的允诺本着法是通往您的哈希值添加 SALT。在斯前后文中, SALT 指的凡另外在创建哈希值之前率先补充加到公的密码被的短语。通过行使
SALT,您最终见面(在骨子里被)战胜彩虹表。其他人用特别成一个一定于你的应用程序的彩虹表,然后破解多个密码,才会找到
SALT是呀 — 这是相同种植复杂的、代价高昂的现象。

 

回页首

改良过去底 PHP 密码实践

今昔拘留一下清单 1,这是几年前 PHP 中行之有效的密码实践的一个演示。

清单 1. PHP 中常见给视为有效之密码安全做法
<?php
// Create a password class to handle management of this:
class Password {
    const SALT = 'MyVoiceIsMyPassport';

    public static function hash($password) {
        return hash('sha512', self::SALT . $password);
    }

    public static function verify($password, $hash) {
        return ($hash == self::hash($password));
    }
}

// Hash the password:
$hash = Password::hash('correct horse battery staple');

// Check against an entered password (This example will fail to verify)
if (Password::verify('Tr0ub4dor&3', $hash)) {
    echo 'Correct Password!\n';
} else {
    echo "Incorrect login attempt!\n";
}

阅读:php.net 上的 hash()
文档

譬如说清单 1 这样的示范在 Web
中被视为所谓的极品实践。长时吧,此措施曾是 最佳实践 — 显然比采用
MD5 更好,而且于将密码存储吗明要好得多。清单 1 使用了复杂得差不多之
SHA-512 算法,而且她强制有密码都补加上了
SALT,以克服预先制定的彩虹表。但这个方还是存在部分问题。

动擅自 SALT

清单
1 使用了一个
SALT,但每个密码都利用完全相同的
SALT。所以,一旦有人破解了一个密码(或者又糟地,通过看代码库而发现了
SALT),那么他就算得经向每个采用表条目添加该
SALT来创造于定义之彩虹表。战胜彩虹表的缓解方案是,在创立密码时也每个密码使用一个即兴
SALT,将 SALT和密码并囤,以便可落密码。

愈来愈增多资本

清单
1 也用了
SHA-512(PHP 随带的一样种植复杂得差不多之算法),而尚未采用 MD5 或
SHA-1。但是,甚至 SHA-512 哈希值也得各秒 4600
万不成计算的速率破解。尽管较 MD5 或 SHA1
破解速率更慢有,但此速率对足的安全性而言仍是不够的。此题材之解决方案是,使用于计算达重复复杂的算法,而且多次动这些算法。例如,为每个密码连续运行
SHA-512 100 次,这会明白减缓任何攻击尝试。

吓信息是,您不欲尝试运用自己之代码实现之解决方案。PHP 5.5
中新的密码哈希计算库解决了即同样题目。

 

回页首

介绍 password_hash()

这个密码哈希扩展为卿创建于计算上复杂的平安之密码哈希值,包括在幕后生成和拍卖随机的
SALT。在对君想如果计算哈希值的密码调用password_hash() 的极端简单易行用例中,该扩展会吗你处理所有工作。您还需提供次只参数:您想使推而广之使用的哈希算法。您发出少数种植选择,但每当脚下,指定 PASSWORD_DEFAULT 常量是极品选项(我稍后会说其中的来头):

<?php
$hash = password_hash('correct horse battery staple', PASSWORD_DEFAULT);

指定 cost 参数

也可是资第三独参数,这是一个反哈希值生成道的选项项数组。您可以在这里指定
SALT,但最好是休指定 SALT,并允许吗而生成随机
SALT。更重要之是,在这数组中,您得指定一个 cost 值。此值(默认值为 10)可以规定该算法应多复杂,进而确定生成哈希值将费大多长日子。(将此值视为更改算法本身还运行的次数,以徐计算。)如果想要一个又安全之密码,而且你的电脑能够处理它,您可以像这么实行调用:

<?php
$hash = password_hash('correct horse battery staple', PASSWORD_DEFAULT, ['cost' => 14]);

运自自己之 MacBook Pro 作为测试环境,生成一个 cost
为 10(默认值)的 password_hash 大约会花 0.085 秒的时日。将拖欠 cost
调强及14,会用欠日转移为每次计算 1.394 秒。

证明生成的密码

因前面两只示范中的密码是行使一个随意 SALT
过程生成的,所以我一筹莫展直接了解有关的
SALT。因此,如果自身尝试再度运行password_hash() 并较字符串,以此作为证明密码的方法,结果以会见无匹配。每次你调用该函数,都见面生成一个初
SALT,返回的哈希值也不同。所以该扩展提供了次只函数 password_verify(),它吗而处理验证过程。您调用 password_verify(),传入用户所提供的密码与贮的哈希值,如果密码是毋庸置疑的,该函数返回一个布尔值 TRUE,否则回 FALSE

<?php
if (password_verify($password, $hash)) {
    // Correct Password
}

今日自己得以更构 清单
1 中的类,使用内置的密码哈希扩展,如清单
2 所示。

清单 2. 重构清单 1 的 Password 类
<?php
class Password {
    public static function hash($password) {
        return password_hash($password, PASSWORD_DEFAULT, ['cost' => 14]);
    }

    public static function verify($password, $hash) {
        return password_verify($password, $hash);
    }
}

阅读:php.net
上之密码哈希文档

 

回页首

拍卖不断变动的安需要

通过动新的密码哈希扩展,您可以拿您的代码库提升至本之安全专业水平。但无非以几乎年前,专家既说了,SHA-1
是一致种极品实践解决方案。那么,如果它们不是超级实践, 密码加密需要还强时,会有啊?幸运的是,新扩张有一个放置的功能考虑了立无异于可能。

好动用 password_needs_rehash() 函数(在背后)检测存储的密码是否与您指定的目前安全需求相配合。如果无配合,原因想必是,您加了 cost 参数,或者一个初的
PHP
版本在不动声色更改为同一栽不同之哈希算法。正因如此,PASSWORD_DEFAULT 应当是您首选之算法;此选项始终会如你的软件以时之特等实践版本。

斯功效的利用更为错综复杂,但不是过分复杂。核对用户的密码时(比如用户尝试发表录时),您需执行一个附加的任务:调用password_needs_rehash(),它承受和 password_hash() 类似的参数。password_needs_rehash() 函数针对初请求的安设置来检查所提供的密码哈希值。如果密码哈希值与这些设置不般配,那么该函数会向你报告及时等同真情。

一对程序员在此处难以知晓,因为 password_needs_rehash() 函数所召开的一切都是为了吃你明白密码是否需要重新计算哈希值。然后是否别密码的新哈希值并保存其,这了取决于你,因为密码扩展不亮你要什么存储密码。

于清单 3
中,我提供了一个圆的效仿的 User 类,在这个近乎吃,通过动自家谈谈的家伙,既会平安地拍卖用户之密码,又能够支撑未来持续变化的安全需求。

清单 3. 以此法的 User 类显示了密码扩展的完全用途
<?php
class User {
    // Store password options so that rehash & hash can share them:
    const HASH = PASSWORD_DEFAULT;
    const COST = 14;

    // Internal data storage about the user:
    public $data;

    // Mock constructor:
    public function __construct() {
        // Read data from the database, storing it into $data such as:
        //  $data->passwordHash  and  $data->username
        $this->data = new stdClass();
        $this->data->passwordHash = 'dbd014125a4bad51db85f27279f1040a';
    }

    // Mock save functionality
    public function save() {
        // Store the data from $data back into the database
    }

    // Allow for changing a new password:
    public function setPassword($password) {
        $this->data->passwordHash = password_hash($password, self::HASH, ['cost' => self::COST]);
    }

    // Logic for logging a user in:
    public function login($password) {
        // First see if they gave the right password:
        echo "Login: ", $this->data->passwordHash, "\n";
        if (password_verify($password, $this->data->passwordHash)) {
            // Success - Now see if their password needs rehashed
            if (password_needs_rehash($this->data->passwordHash, self::HASH, ['cost' => self::COST])) {
                // We need to rehash the password, and save it.  Just call setPassword
                $this->setPassword($password);
                $this->save();
            }
            return true; // Or do what you need to mark the user as logged in.
        }
        return false;
    }
}

 

回页首

结束语

而今公知道新的密码哈希库如何呢 PHP
所用,以及她什么延续拉而的用户防御安全破坏。在生同样望 PHP
的复兴
 中,我会将讨论主题转向
PHP 语言本身向生态系统的演化,以及已上马围绕她进行演变的工具:首先是
Composer,一个风行整个社区的 PHP 依赖项管理器。

参考资料

学习

  • PHP
    项目资源:在
    developerWorks 上查看 PHP 项目资源,以恢宏您的 PHP 技能。
  • 彩虹表已好(ircmaxell
    的博客,2011 年 8 月):了解 Anthony Ferrara
    为何将暴力破解视为比彩虹表更不行的威胁。
  • PHP:正确的道:进一步询问如何为现代方式构建
    PHP
    项目,包括动用 密码哈希。
  • PHP 文档:参阅所有 PHP 文档的法定自。
  • PHPDeveloper.org:获取有关 PHP
    的资讯、观点及社区信息。
  • php[architect]:查阅一个留意让 PHP
    教育与流行讯息之在线与印刷杂志。
  • 更多 PHP
    内容:浏览
    developerWorks 上之有着 PHP 内容。
  • developerWorks Web development
    专区:通过专门关于 Web
    技术之篇章和学科,扩展您于网站开方的艺。
  • developerWorks Ajax
    资源为主:这是关于 Ajax
    编程模型信息之一律站式中心,包括多文档、教程、论坛、blog、wiki
    和资讯。任何 Ajax 的初消息还能够当此地找到。
  • 查看 HTML5
    专题,了解又多和
    HTML5 相关的学问与动向。

讨论

  • 加入 developerWorks
    国语社区,developerWorks
    社区是一个面向世界 IT
    专业人员,可以供博客、书签、wiki、群组、联系、共享与协作等社区职能的正式社交网络社区。

相关文章