0

我正在使用 AWS KMS 为每个用户的会话生成唯一的密钥对,然后对定义为使用开放 ssl 加密的每一列进行加密。因此,我实现了以下特征,它加密和解密getEncryptAttributes在保存/检索时定义的字段:

trait HasEncryption
{
    /**
     * Set the array of encrypted attributes which will be stored in the database as encrypted text. Ensure the database
     * column type to be binary. If you know a precise size, set it like in the respective database migration linked below. Else, use laravel's builtin $table->binary()
     * @return string[]
     * @see \AddCipherKeyToUsers::up()
     */
    abstract public function getEncryptAttributes(): array;

    /**
     * Init the model event-handlers
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public static function bootHasEncryption()
    {
        // On save: Encrypt the data and store the keys to decrypt in the database
        static::saving(function (Model $model) {
            // For all encrypt-attributes, encrypt the data
            foreach ($model->getEncryptAttributes() as $attribute) {
                if (!is_null($model->getAttribute($attribute))) {
                    $res = $model->encryptionService->encrypt($model->getAttribute($attribute));
                    $model->setAttribute($attribute, $res['encrypted']);
                }
            }
            // If something was encrypted, store the encryption keys to the database to use for decryption
            if (isset($res)) {
                $model->setAttribute('encrypted_key', $res['key']);
                $model->setAttribute('encryption_iv', $res['iv']);
            }
            return $model;
        });

        // On retreive: Decrypt all attributes using the keys stored above
        static::retrieved(function (Model $model) {
            // For all encrypt-attributes, decrypt the data with the encryption keys stored on encrypt method
            foreach ($model->getEncryptAttributes() as $attribute) {
                if (!is_null($model->getAttribute($attribute)) && !is_null($model->getAttribute('encrypted_key')) && !is_null($model->getAttribute('encryption_iv'))) {
                    $decrypted = $model->encryptionService->decrypt($model->getAttribute($attribute), $model->getAttribute('encrypted_key'), $model->getAttribute('encryption_iv'));
                    $model->setAttribute($attribute, $decrypted);
                }
            }
            return $model;
        });
    }

    /**
     * Get the app's encryption service singelton through magic attribute accessor
     *
     * @return EncryptionService
     */
    public function getEncryptionServiceAttribute(): EncryptionService
    {
        return app()->make(EncryptionService::class);
    }
}

EncryptionService是一个单例,它将在会话的整个生命周期中使用相同的密钥。如果会话中没有可用的密钥,它将连接到 AWS STS 并为此会话获取新令牌。

我们不能使用 aurora DB 的默认 AWS 加密,因为数据必须以加密状态通过网络,并且每个用户的数据必须独立加密。

现在在实践中,加密和解密在存储某些东西时起作用,然后再次重新加载和读取数据。但是当我有这样的事情时:

$thing->setAttribute('encrypted_attribute', 'value');
$thing->save();
dump($thing->getAttribute('encrypted_attribute'); // will print ú©N╬B═Ø©ªx\rÒ┤Ò·&

观察者显然会修改加密的saving属性,但我只想加密实际进入数据库的内容,但保留本地状态。否则,例如多次保存时,它也会再次加密二进制数据,而不是使用实际属性。

有没有办法按照我前进的方向实现这一目标,或者您是否建议采用完全不同的设计来实现部分加密的数据库?


编辑:有时,提问可以帮助我的大脑。我认为雄辩的模型具有finishSave触发saved事件的方法。所以我添加了static::savedtrait 并在那里解密。似乎工作。不过,您是否发现此设计存在任何潜在缺陷,或者您是否有经验通常如何实现这一点?

4

0 回答 0