跳转到主要内容

什么是 Crypt 门面

Laravel 的加密服务借助 OpenSSL 的 AES-256-CBC 加密(或 AES-128-CBC),通过简洁的接口提供数据加解密能力。 Laravel 加密后的值都会带上消息认证码(MAC) 签名。 一旦加密后的值被篡改,就无法再被解密。

配置

生成 APP_KEY

在使用加密之前,需要设置 config/app.php 中的 key。 该值会从 APP_KEY 环境变量中读取。 使用 php artisan key:generate 命令生成安全的密钥。 它会通过 PHP 的安全随机源生成密码学上安全的密钥。
php artisan key:generate
Laravel 在安装时通常会自动生成,并将其保存到 .env 文件中。
APP_KEY=base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY=
绝对不要公开 APP_KEY。一旦该密钥泄露,所有加密数据都可能被解密。

密钥轮换

更改加密密钥会导致所有已认证用户的会话被登出。 因为 Laravel 会加密所有 Cookie(包括 Session Cookie)。 而且旧密钥加密的数据也无法再被解密。 为了缓解此问题,可以在 APP_PREVIOUS_KEYS 中以逗号分隔列出旧密钥。
APP_KEY="base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="
APP_PREVIOUS_KEYS="base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w="
Laravel 加密时始终使用当前密钥,但在解密时若当前密钥失败,则会按顺序尝试旧密钥。 这样在密钥轮换期间用户仍能继续使用应用。

加密

使用 Crypt 门面的 encryptString 方法进行加密。 加密值使用 OpenSSL 与 AES-256-CBC,且会带上 MAC 签名。
<?php

namespace App\Http\Controllers;

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

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

        return redirect('/secrets');
    }
}
encryptString 不会序列化字符串直接加密。若要加密对象或数组,请使用 encrypt
方法用途
Crypt::encryptString($value)直接加密字符串
Crypt::encrypt($value)先序列化再加密(支持数组、对象)

解密

使用 Crypt 门面的 decryptString 方法解密。 若 MAC 无效或其它原因导致解密失败,会抛出 DecryptException
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 转型可以自动加解密属性。
<?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 之后,模型属性在赋值与读取时会自动加解密。
// 自动加密后保存到数据库
$user->secret_note = '秘密备忘';
$user->save();

// 自动解密后返回
echo $user->secret_note; // '秘密备忘'
encrypted:* cast 内部使用 Crypt::encryptCrypt::decrypt。 数据库列类型建议使用 textlongText

实用示例:加密存储个人信息

以下是一个将个人信息(如个人编号、信用卡号等)安全存入数据库的典型模式。

迁移

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

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',
        ];
    }
}

控制器

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_KEYphp artisan key:generate
加密字符串Crypt::encryptString($value)
解密字符串Crypt::decryptString($value)
加密数组/对象Crypt::encrypt($value)
自动加密模型属性使用 'column' => 'encrypted' cast
密钥轮换APP_PREVIOUS_KEYS 中列出旧密钥

下一步

哈希

学习如何安全地对密码进行哈希与校验。

授权

介绍如何使用 Policy 与 Gate 进行访问控制。
最后修改于 2026年7月13日