Encrypt/Decrypt DB fields in laravel

2020-06-16 04:16发布

问题:

I am encrypting/decrypting the DB field values in Laravel through accessors and mutators, which is working fine in normal eloquent transactions.

class Person extends Model
{
    use Notifiable;
    protected $table = 'person';

    public function getFirstNameAttribute($value)
    {
        return Crypt::decryptString($value);
    }
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $guarded = array();

    protected function user()
    {
        return $this->belongsTo('App\Models\User', 'useraccount_id', 'id');
    }
}

But the encryption and decryption not working under the following conditions

  1. Eloquent relationships
  2. DB raw queries

Working

$person = Person::find($person_id);
$person->firstName;

Not Working

$user = User::find($user_id);
$user->person->firstName;

回答1:

You would probably have the encryption at the database level, as if someone gets access to the database you don’t want them to be able to read people’s medical data in plain text.

You could create a trait that encrypts and decrypts data on save and retrieval respectively:

namespace App\Traits;

use Illuminate\Support\Facades\Crypt;
trait Encryptable
{
    /**
     * If the attribute is in the encryptable array
     * then decrypt it.
     *
     * @param  $key
     *
     * @return $value
     */
    public function getAttribute($key)
    {
        $value = parent::getAttribute($key);
        if (in_array($key, $this->encryptable) && $value !== '') {
            $value = decrypt($value);
        }
        return $value;
    }
    /**
     * If the attribute is in the encryptable array
     * then encrypt it.
     *
     * @param $key
     * @param $value
     */
    public function setAttribute($key, $value)
    {
        if (in_array($key, $this->encryptable)) {
            $value = encrypt($value);
        }
        return parent::setAttribute($key, $value);
    }
    /**
     * When need to make sure that we iterate through
     * all the keys.
     *
     * @return array
     */
    public function attributesToArray()
    {
        $attributes = parent::attributesToArray();
        foreach ($this->encryptable as $key) {
            if (isset($attributes[$key])) {
                $attributes[$key] = decrypt($attributes[$key]);
            }
        }
        return $attributes;
    }
}

You can then just apply the trait to your models, and define a property called $encryptable that’s an array of columns whose data should be encrypted:

class YourModelextends Model
{
    use Encryptable;

    protected $encryptable = [
        'code',
        'keys',
        'allergies'
    ];
}


回答2:

You can do it with laravel's Crypt Facades. please follow this example.

use Illuminate\Support\Facades\Crypt;

$encrypted = Crypt::encryptString('Hello world.');

$decrypted = Crypt::decryptString($encrypted);

I implemented from this article link : https://hackthestuff.com/article/laravel6-encryption-and-decryption-model-data-using-crypt-class



回答3:

Based on Iman his answer i changed the trait so it works with the casts array from Laravel self.

<?php
namespace App\Library\Traits;

use Illuminate\Support\Facades\Crypt;
trait Encryptable
{
    /**
     * If the attribute is in the encryptable array
     * then decrypt it.
     *
     * @param  $key
     *
     * @return $value
     */
    public function getAttribute($key)
    {
        $value = parent::getAttribute($key);

        if (isset($this->casts[$key]) && $value !== '' && !is_null($value) && $this->casts[$key] == 'encrypt') {
            $value = decrypt($value);
        }
        return $value;
    }
    /**
     * If the attribute is in the encryptable array
     * then encrypt it.
     *
     * @param $key
     * @param $value
     */
    public function setAttribute($key, $value)
    {
        if (isset($this->casts[$key]) && $value !== '' && !is_null($value) && $this->casts[$key] == 'encrypt') {
            $value = encrypt($value);
        }
        return parent::setAttribute($key, $value);
    }
    /**
     * When need to make sure that we iterate through
     * all the keys.
     *
     * @return array
     */
    public function attributesToArray()
    {
        $attributes = parent::attributesToArray();
        foreach ($this->casts as $key => $value) {
            if($value == 'encrypt') {
                if (isset($attributes[$key]) && $attributes[$key] !== '' && !is_null($attributes[$key])) {
                    $attributes[$key] = decrypt($attributes[$key]);
                }
            }
        }
        return $attributes;
    }
}