淺析 App_KEY 的作用

Epona發表於2019-07-14

本文翻譯自 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://learnku.com/docs/laravel/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:

$key = "dont-panic";

當我想要傳送一條只有上面的key能解鎖的資訊時,我們可以使用openssl_encrypt()(Laravel 的 Crypt 也是使用的這個方法),具體如下:

$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,然後他可以進行解密了。

$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

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 雜湊結果

由於這是一個單向雜湊,因此我們無法進行解密,我們能做的只是去驗證其是否匹配。

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的更改

There's nothing wrong with having a little fun.