背景:
在A公司入职,老大让我去做数据脱敏,通过调用运维组的数据脱敏接口,保证用户的敏感的信息不落地。公司项目主要使用laravel5.1 和 YII1.1 版本。
具体方案:
敏感信息字段的调用存在与代码中的各个角落,如果手动对每一个地方的代码都去撸一边,所需要耗费的时间是非常巨大的,为了减少工作量,我们决定对model下手,考虑到并不是所有的model都存在脱敏信息,单独定义一个trait来处理存在敏感信息似乎是一个相对比较稳妥的方式。
我们对于脱敏的主要思想是对__get()和__set()两个魔术方法的改造,希望在最大程度的减少对使用类似User::model()->username的影响,以Yii1.1框架为例,还要改造findByAttributes()等方法避免大量findByXXX()方法的失效
public function __get($name)
{
//getPlainKeys() 定义了model当中的需要的脱敏信息字段
if(in_array($name, $this->getPlainKeys())) {
$enc_name = Mask::getMaskName($name);//encrypt_username
if(empty($this->$enc_name)) {
return parent::($name); //兼容代码,用户数据未更新时调用
}
$plain_value = Mask::encrypt(Mask::getMaskType($name), $this->$name);//返回明文
return $plain_value;
}else {
return parent::($name);//不要使用$this->$name 防止多次调用__get()
}
}
重写完__get()的方法后,在定义一个getPlain()的方法,传入返回明文的字段,
通过User::model()->getPlain(['username'])->username的方法返回需要的明文就好了,在这之前,需要在需要脱敏的model里面加好订好的脱敏信息的字段$mask_key['username','mobile']
/**
* 往数组里塞值
**/
public function getPlain($keys)
{
foreach($keys as $key){
if(in_array($key, $this->getMaskName())) {//比较mask_key
array_merge($this->getPlainKeys(), $keys); }
}
return $this;
}
当然了,如果直接通过User::model()->username的地方很多,又需要明文的地方很多,就在model里面直接添加一个$plain_keys的字段去默认返回明文就好了,同时去新增一个能够将$plain_keys里面的值移除的方法就好了,例如User::model()->removePlainKeys('username')->username返回打码明文就好了.
public function __set($name, $value)
{
if(in_array($name, $this->getMaskKeys())){
... //产生字段名称encrypt_username, plain_username
if(empty($value)) { //防止某些情况下可能发生的将敏感信息写回空值的情况,enccrypt_username 存在将回返回旧的历史数据
$this->setAttributes($name, '');
$this->setAttributes($encrypt_username, '');
$this->setAttributes($plain_username, '');
}
$data = Mask::doencrypt(Mask::getMaskType($name),$value)
$this->setAttributes($name, $data['plain_text']);
$this->setAttributes($encrypt_username, $data['hash']);
$this->setAttributes($plain_username, $value;
}else {
reutrn parent::__set($name, $vk)
}
}