公開日:2021.08.24

Laravel Eloquent ModelでDBカラムの暗号化を自動で行う

テクログphp

どうも!他人の秘密は聞きたいけど、自分の秘密は話したくないわいです!

今回は、Laravelでデータベースカラムの暗号・復号を自動で行う方法について書きたいと思います。

かなりシンプルにできます。

以下のTraitを作って、Model側に追加するだけで、簡単に実現します。

appModelsTraitsEncryptable.php

<?php
namespace AppModelsTraits;

use IlluminateSupportFacadesCrypt;

/**
 * カラムの暗号化を行う
 * use先で$this->encryptableを定義
 * 同じ文字列でも暗号化のたびに結果が変わるので、検索には使えない
 * 検索を行う場合は全取得後phpでフィルタリングする
 */

trait Encryptable
{
    public function getAttribute($key)
    
{
        $value = parent::getAttribute($key);

        if (in_array($key, $this->encryptable, true))
        {
            $value = !empty($value) ? Crypt::decrypt($value) : null;
            return $value;
        }
        return $value;
    }

    public function setAttribute($key, $value)
    
{
        if (in_array($key, $this->encryptable, true))
        {
            $value = !empty($value) ? Crypt::encrypt($value) : null;
        }
        return parent::setAttribute($key, $value);
    }
}

appModelsSomeModel.php

<?php

namespace AppModels;

use IlluminateDatabaseEloquentModel;

class SomeModel extends Model
{
    use TraitsEncryptable;

    // 暗号化したいカラム名を指定
    protected $encryptable = [
        "email",
        "phone_number",
    ];
}

あとは、Eloquent Modelを介してDBにアクセスすれば自動で暗号・復号をやってくれます。

$model = new SomeModel();
$model->phone_number = 00000000000;
$model->save();
// 暗号化されてDBに保存される


$model = SomeModel::find(1);
dd($model->phone_number);
// 00000000000
// 復号されている

しかし、この方法には大きな欠点がありまして、

コメントに書いてあるとおり、Cryptの仕様で暗号結果が毎回変わる(もちろん復号結果は同じ)ので、検索ができません。

上の例で、入力された電話番号に該当するデータを取ってこようとすると、

一度全データ取ってきて、そっからPHP側でループ回しながら復号結果と入力が一致するかを確認していかなくてはなりません。

これはかなり微妙なので、AES_ENCRYPTを使ってDB側で暗号化する方法も考えたのですが、特段検索が速くなるわけでもなさそうですし、Eloquent Modelを使って簡単に実装できないなと思い、上記方法を採用しました。

なにか他に良い方法があれば、ぜひ教えていただきたいです!!

みなさんの秘伝のソースを教えてください!

以上、わいでした。

健闘を祈る!!

この記事を書いた人

わい

入社年2019年

出身地大阪

業務内容システム開発

特技または趣味芸人のラジオを聴く、ダイビング

わいの記事一覧へ

テクログに関する記事一覧