浅析 APP_KEY 的作用

本文翻译自 APP_KEY And You

可能每个 Laravel 开发者新建或者克隆一个 Laravel 程序的时候,composer install之后最可能以及最重要的的第一步操作就是生成 APP_KEY

在这篇文章中我们会讨论APP_KEY能做的和不能做的,有些人会把用户密码的哈希处理与APP_KEY相关联(实际上它们毫无关联)。

什么是 APP_KEY

每一个 Laravel 程序都会生成一个随机的32位长度的字符串存储在.env文件中的APP_KEY键值中,当我们新建 Laravel 项目的时候会自动为你创建一个,只有在克隆的时候你才有可能发现它没有被设定。

你可能看到过下面的错误,说明APP_KEY没有设定

app_key error

你可以自己手动生成或者使用 php artisan key:generate 来进行创建 APP_KEY

当你的应用程序执行的时候,只有一个地方会用到APP_KEYcookies。Laravel 使用它来加密所有的cookies,在将cookie返回给用户之前 Laravel 会对 cookie进行加密,然后再返回给用户,这样客户端就无法自己修改cookie来伪装成管理员或者其他用户了。

所以的加密和解密都在 [Encrypter](https://laravel.com/docs/master/encryption#using-the-encrypter)中进行处理,其中主要使用了 [openssl_encrypt](https://secure.php.net/manual/en/function.openssl-encrypt.php) 进行加密。

有很多用户都会有一个误解,那就是APP_KEY是用来处理用户哈希密码的。事实上不是这样的。Laravel的密码使用了 Hash::make() 或者 bcrypt() 来进行哈希处理,其中并没有用到APP_KEY

加密 VS 哈希

在 Laravel 中有两个主要的加密 Facade,分别是 Crypt(对称加密) 和 Hash(单向加密哈希)。密码是哈希,而cookie则是对称加密

对称加密

假设我想发送一条加密的信息给我的好友A。我们商议好了一个用于加密和解密的key:

1
$key = "dont-panic";

当我想要发送一条只有上面的key能解锁的信息时,我们可以使用openssl_encrypt()(Laravel 的 Crypt 也是使用的这个方法),具体如下:

1
2
3
4
5
6
$message = "So long and thanks for all the fish";
$key = "dont-panic";
$cipher = "AES-256-CBC";
echo openssl_encrypt($message, $cipher, $key);

// JJEK8L4G3BCfY0evXDRxUke2zqAzq6i7wL/Px4SjaEHXqt3x7hsj4+PhVQaH4ujX

此时,你就可以把这段加密的代码发送给A,然后他可以进行解密了。

1
2
3
4
5
6
$secret = "JJEK8L4G3BCfY0evXDRxUke2zqAzq6i7wL/Px4SjaEHXqt3x7hsj4+PhVQaH4ujX";
$key = "dont-panic";
$cipher = "AES-256-CBC";
echo openssl_decrypt($secret, $cipher, $key);

// So long and thanks for all the fish

Laravel的cookie加密解密也是同理,只不过key使用的是APP_KEY

单向哈希

当我们使用密码的时候,我们应当从不使用对称加密的方法,来避免用户可能解密密码。这表示Crypt无法胜任,因此我们需要使用哈希方法,具有如下的特点:

  1. 快速:计算机能够快速的生成哈希值
  2. 确定性:针对同样的输入,输出是固定的
  3. 随机性:即使只更改一位字符串,哈希之后的输出应该十分不同
  4. 唯一性:碰撞概率应该非常非常的小
  5. 难于被暴力破解: 哈希之后的值应当难以被暴力破解

你应当听说过许多的单向哈希算法,例如md5SHA-1。在Laravel中使用了PHP的原生方法password_hash(),它使用的哈希算法叫bcrypt

1
2
3
4
5
6
use Illuminate\Support\Facades\Hash;

$password = "dont-panic";
echo Hash::make($password);

// $2y$10$hEEF0lv4spxnvw5O4XyLZ.QjCE1tCu8HjMpWhmCS89J0EcSW0XELu

如果你曾经查看过users数据表,那么上面的输出你可能会似曾相识。下面解释一下其中的含义:

  1. $2y$表示我们使用了blowfish算法(bcrypt)
  2. 10$表示算法使用的 cost(值越高表示生成哈希的时间越长)
  3. hEEF0lv4spxnvw5O4XyLZ.表示一个22位的随机“盐”
  4. QjCE1tCu8HjMpWhmCS89J0EcSW0XELu 哈希结果

由于这是一个单向哈希,因此我们无法进行解密,我们能做的只是去验证其是否匹配。

1
2
3
4
5
6
7
use Illuminate\Support\Facades\Hash;

$input = request()->get('password'); // "dont-panic"
$hash = '$2y$10$hEEF0lv4spxnvw5O4XyLZ.QjCE1tCu8HjMpWhmCS89J0EcSW0XELu';
return Hash::check($input, $hash);

// true

因此我们可以看到,在对称加密中我们使用到了APP_KEY,而在密码哈希的时候我们是不需要使用它的。

总结

  • 更改 APP_KEY 不会使用户的密码失效
  • 更改 APP_KEY 会使session 和 cookie 失效,导致当前已登录用户退出登录
  • 不要害怕你的 APP_KEY
  • 如果你在其他地方使用了Laravel的加密方法,那么你需要制定一个计划来应对APP_KEY的更改