> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 加密（Encryption）

> 說明如何使用 Laravel 的 Crypt Facade 進行 AES-256-CBC 的值加密與解密。內容涵蓋 APP_KEY 產生、模型 Cast，以及個資的安全儲存等。

## Crypt Facade 是什麼

Laravel 的加密服務透過 **OpenSSL** 的 AES-256-CBC 加密（或 AES-128-CBC），以簡潔的介面提供值的加密與解密。

Laravel 加密的每個值皆會由\*\*訊息驗證碼（MAC）\*\*簽署。
若加密後的值遭到竄改，將無法被解密。

```mermaid theme={null}
flowchart LR
    A["明文資料"] -->|"Crypt::encryptString()"| B["加密後的值<br>（已由 MAC 簽署）"]
    B -->|"Crypt::decryptString()"| A
    B -->|竄改偵測| C["DecryptException"]
```

***

## 設定

### 產生 APP\_KEY

使用加密前，需先設定 `config/app.php` 的 `key`。
此值會從 `APP_KEY` 環境變數讀取。

請使用 `php artisan key:generate` 指令產生安全的金鑰。
它會透過 PHP 的安全隨機產生器產生密碼學上安全的金鑰。

```shell theme={null}
php artisan key:generate
```

Laravel 安裝時通常會自動產生。產生的金鑰會存到 `.env`。

```ini theme={null}
APP_KEY=base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY=
```

<Warning>
  絕不可將 `APP_KEY` 對外公開。若金鑰洩漏，所有加密資料都可能被解密。
</Warning>

### 加密金鑰輪替

若變更加密金鑰，會使所有已認證使用者的 session 登出。
因為 Laravel 對包含 session cookie 在內的所有 cookie 都進行加密。

此外，用舊金鑰加密的資料將無法解密。

為緩解此問題，可以在 `APP_PREVIOUS_KEYS` 以逗號分隔列出舊金鑰。

```ini theme={null}
APP_KEY="base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="
APP_PREVIOUS_KEYS="base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w="
```

Laravel 加密時永遠使用目前的金鑰，但解密時若目前金鑰失敗，會依序嘗試舊金鑰。
如此一來，即便金鑰輪替中，使用者仍可持續使用應用程式。

***

## 加密

使用 `Crypt` Facade 的 `encryptString` 方法加密值。
加密後的值採 OpenSSL 與 AES-256-CBC，並經由 MAC 簽署。

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;

class DigitalOceanTokenController extends Controller
{
    /**
     * 儲存使用者的 API token
     */
    public function store(Request $request): RedirectResponse
    {
        $request->user()->fill([
            'token' => Crypt::encryptString($request->token),
        ])->save();

        return redirect('/secrets');
    }
}
```

<Tip>
  `encryptString` 加密字串時**不會**做序列化。若要加密物件或陣列，請改用 `encrypt`。
</Tip>

| 方法                             | 用途                 |
| ------------------------------ | ------------------ |
| `Crypt::encryptString($value)` | 直接加密字串             |
| `Crypt::encrypt($value)`       | 序列化後再加密（支援陣列 / 物件） |

***

## 解密

使用 `Crypt` Facade 的 `decryptString` 方法解密加密的值。
當 MAC 無效等，無法正常解密時會拋出 `DecryptException`。

```php theme={null}
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;

try {
    $decrypted = Crypt::decryptString($encryptedValue);
} catch (DecryptException $e) {
    // 解密失敗時的處理
    abort(400, '資料無效。');
}
```

| 方法                             | 用途                 |
| ------------------------------ | ------------------ |
| `Crypt::decryptString($value)` | 以字串解密              |
| `Crypt::decrypt($value)`       | 解密後反序列化（支援陣列 / 物件） |

***

## 模型的 Cast

在 Eloquent 模型中使用 `encrypted` cast，就能自動加密 / 解密屬性。

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected function casts(): array
    {
        return [
            'secret_note'    => 'encrypted',            // 加密字串
            'profile_data'   => 'encrypted:array',      // 加密為陣列存放
            'preferences'    => 'encrypted:collection', // 加密為集合存放
            'metadata'       => 'encrypted:object',     // 加密為物件存放
            'settings'       => 'encrypted:json',       // 加密為 JSON 存放
        ];
    }
}
```

一旦設定 cast，指派或取得屬性值時會自動加密 / 解密。

```php theme={null}
// 會自動加密後存入 DB
$user->secret_note = '祕密備註';
$user->save();

// 會自動解密後取出
echo $user->secret_note; // '祕密備註'
```

<Info>
  `encrypted:*` cast 內部使用 `Crypt::encrypt` 與 `Crypt::decrypt`。
  資料庫欄位型別建議使用 `text` 或 `longText`。
</Info>

***

## 實用範例：個資的加密儲存

以下是將個資（身分證字號、信用卡號等）安全地存入資料庫的典型作法。

### Migration

```php theme={null}
Schema::create('profiles', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
    $table->text('my_number')->nullable();     // 加密後的值以 text 儲存
    $table->text('bank_account')->nullable();
    $table->timestamps();
});
```

### 模型

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Profile extends Model
{
    protected $fillable = ['user_id', 'my_number', 'bank_account'];

    protected function casts(): array
    {
        return [
            'my_number'    => 'encrypted',
            'bank_account' => 'encrypted',
        ];
    }
}
```

### 控制器

```php theme={null}
use App\Models\Profile;
use Illuminate\Http\Request;

class ProfileController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'my_number'    => ['required', 'string'],
            'bank_account' => ['required', 'string'],
        ]);

        // 模型會自動加密後儲存
        Profile::create([
            'user_id'      => $request->user()->id,
            'my_number'    => $request->my_number,
            'bank_account' => $request->bank_account,
        ]);

        return redirect('/profile');
    }

    public function show(Request $request)
    {
        $profile = $request->user()->profile;

        // 模型會自動解密後回傳
        return view('profile.show', ['profile' => $profile]);
    }
}
```

***

## 總結

| 想要做的事       | 方法                             |
| ----------- | ------------------------------ |
| 產生 APP\_KEY | `php artisan key:generate`     |
| 加密字串        | `Crypt::encryptString($value)` |
| 解密字串        | `Crypt::decryptString($value)` |
| 加密陣列 / 物件   | `Crypt::encrypt($value)`       |
| 自動加密模型屬性    | `'column' => 'encrypted'` cast |
| 金鑰輪替        | 於 `APP_PREVIOUS_KEYS` 列出舊金鑰    |

## 下一步

<Columns cols={2}>
  <Card title="雜湊" icon="lock" href="/zh-TW/hashing">
    學習密碼的安全雜湊化與驗證方式。
  </Card>

  <Card title="授權" icon="shield" href="/zh-TW/authorization">
    說明使用 Policy 與 Gate 的存取控制。
  </Card>
</Columns>


## Related topics

- [Crypto — AT Protocol 加密](/zh-TW/packages/laravel-bluesky/crypto.md)
- [設定](/zh-TW/configuration.md)
- [教學 - Laravel Console Starter](/zh-TW/packages/laravel-console-starter/tutorial.md)
- [Redis](/zh-TW/redis.md)
- [OAuth 2.0 驗證 - Google Sheets API for Laravel](/zh-TW/packages/laravel-google-sheets/oauth.md)
