【Laravel】保存・更新時に特定のカラムを自動で暗号化(復号化)させる

システムを実装する上で、個人情報の暗号化は必ず対応すべき事です。
Laravelの場合、エンクリプタが用意されていますので処理自体は非常に簡単です。

readouble.com

しかし、ルートやキー毎に手動で暗号化と復号化処理を入れるのは手間です。

さて、
今回はモデル設定で、暗号化と復号化を自動で行えるようにする方法について記載します。

データ保存(更新)時、取得時に処理を差し込むには?

ミューテタアクセサを設定する事で、データ保存時、取得時に処理を挟む事ができます。
ミューテタが保存(更新)時、アクセサが取得時にそれぞれ設定できます。

readouble.com

もちろん、ミューテタかアクセサどちらか片方だけ設定する事も可能なので、出力時だけ加工をするという事も可能です。

ミューテタ

ミューテタを定義する場合は、set(カラム名)Attributeという関数を作成します。

ミューテタを定義するにはアクセスしたいカラム名がFooの場合、モデルに「ローワーキャメルケース」でsetFooAttributeメソッドを作成します。

Laravel 5.8 Eloquent:ミューテタ ミューテタの定義より出典

アクセサ

アクセサを定義する場合は、set(カラム名)Attributeという関数を作成します。

アクセサを定義するには、アクセスしたいカラム名が「studlyケース(Upper Camel Case)」でFooの場合、getFooAttributeメソッドをモデルに作成します。

Laravel 5.8 Eloquent:ミューテタ アクセサの定義より出典

暗号化、復号化のミューテたとアクセサの定義方法

例えば、Userモデルでnameという日本語名を管理するカラムがあるとします。
入力した値を保存する時は暗号化し、出力時は復号化された状態で取得する場合は次のように設定します。

class User extends Model
{
    /**
     * データの取得周り
     */
    Public function getUserAttribute($value){
        return Crypt::decrypt($value);
    }

    /**
     * データの保存周り
     */
    Public function setUserAttribute($value){
        $this->attributes['name'] = Crypt::encrypt($value);
    }
}

保存(更新)時

上記の指定をする事で加工をせずに保存処理をしても、自動で暗号化されます。

$user = new User();
$user->name = "ユーザー名";

$user->save
// データベース上は暗号化された状態で格納されます。

もちろん、更新時も同様です。

$user = User::find(1);
$user->name = "新しいユーザー名";

$user->save();
// データベース上は暗号化された状態で格納されます。

取得時

特に特殊な処理をせず取得するだけです。

$user = User::find(1);

dd($user->name); 
// 復号化された名前

ミューテタ、アクセサで暗号化(復号化)する場合の注意点

暗号化するとデータベースから平文で検索できなくなるという問題があります。
しかし、それ以外に特有の問題もあります。

空の入力でも暗号化の文字が入ってしまいNullに出来ない

入力値をPOST(あるいはPUT)で保存(更新)する際に、入力がなくても暗号化処理されたデータが入ってしまいます。

例えば、対象のカラムを空にして保存した直後にのデータを見ると、下記のようにNullではなく暗号化されたデータが入っていると思います。

>>> use App\User
>>> User::all();
=> Illuminate\Database\Eloquent\Collection {
     all: [
       App\User {
           id: 1,
           name: (暗号化された空欄),
           created_at: "2019-07-16 11:24:16",
           updated_at: "2019-07-16 11:24:16",
        

未入力時は入力値をNullにする

入力値を取り出すだけのカラムであれば特に支障はありませんし、復号すれば判定できます。
しかし、入力の有無の判定だけであれば、復号前にやってしまいたいものです。

その場合は、ミューテタアクセサそれぞれにnullの場合の処理を追加します。

class User extends Model
{
    /**
     * データの取得周り
     */
    Public function getUserAttribute($value){
        return empty($value)
            ? null
            : Crypt::decrypt($value);
    }

    /**
     * データの保存周り
     */
    Public function setUserAttribute($value){
        $this->attributes['name'] = empty($value)
            ? null
            : Crypt::encrypt($value);
    }
}

DBファザードから直接データ取得するとミューテタが機能しない

ミューテタはあくまで、モデルからデータを取得した場合のみ機能します。
そのため、DBファサードのを使用して対象モデルのテーブルを指定しても、値を取り出し時にミューテタは機能しません

$user = DB::table('users')
   ->where('id', 1)
   ->first();

dd($user->name);
// 暗号化された名前

©︎2017-2018 WebSandBag