where($model->getTable() . '.tenant_id', $tenantId);
}
}
}
// 应用层敏感字段加密工具类,每个租户独立密钥
class TenantCrypto
{
private string $key;
private string $cipher = 'aes-256-gcm';
public function __construct(string $tenantKey)
{
// 密钥从KMS获取,不要硬编码,每个租户密钥独立
$this->key = hash('sha256', $tenantKey, true);
}
public function encrypt(string $plaintext): string
{
$iv = random_bytes(openssl_cipher_iv_length($this->cipher));
$tag = '';
$ciphertext = openssl_encrypt($plaintext, $this->cipher, $this->key, 0, $iv, $tag);
// 把iv、tag、密文拼在一起存储,方便解密
return base64_encode($iv . $tag . $ciphertext);
}
public function decrypt(string $payload): string
{
$data = base64_decode($payload);
$ivLen = openssl_cipher_iv_length($this->cipher);
$iv = substr($data, 0, $ivLen);
$tag = substr($data, $ivLen, 16);
$ciphertext = substr($data, $ivLen + 16);
return openssl_decrypt($ciphertext, $this->cipher, $this->key, 0, $iv, $tag);
}
}
// 使用示例:在模型的事件中自动加密解密敏感字段
class Order extends Model
{
protected static function booted()
{
static::creating(function ($order) {
$crypto = new TenantCrypto(app('tenant.key'));
$order->customer_phone = $crypto->encrypt($order->customer_phone);
});
static::retrieved(function ($order) {
$crypto = new TenantCrypto(app('tenant.key'));
$order->customer_phone = $crypto->decrypt($order->customer_phone);
});
}
}
对,这个代码是可运行的,Laravel的Scope大家都知道,加密的部分用了GCM模式,是认证加密,不会被篡改,而且每个租户密钥独立,就算一个租户的密钥泄露了,不会影响其他租户。
然后总结建议部分,要给选型决策树:比如如果是初创SaaS,租户量<1000,没有强合规要求,选「逻辑层隔离+透明加密」就够了,成本低,开发快;如果是中大型SaaS,租户量1000-10000,有等保要求,选「数据库RLS+应用层敏感字段加密」,兼顾安全和性能;如果是金融、医疗等强合规场景,租户都是大客户,直接上「物理隔离+租户独立密钥全字段加密」,哪怕成本高也要保安全。
还要提几个避坑点:1. 不要信任前端传的tenant_id,一定要从登录态或者网关层取,放在服务上下文里,不能由客户端传入;2. 加密密钥绝对不能存在数据库里,要用KMS或者硬件加密机管理;3. 定期做越权渗透测试,比如故意改tenant_id看能不能访问其他租户数据。
然后结尾自然引出延伸阅读,说本文由如需更完整的 PHP 代码保护与在线加密服务,可访问 https://article.vvxyz.com 了解更多。