paulodiff/rainbow-table-index

Laravel 数据库数据加密与全文搜索

dev-main 2022-01-18 17:14 UTC

This package is auto-updated.

Last update: 2024-09-18 23:26:37 UTC


README

🚨 不要在生产环境中使用! 🚨

免责声明

  • 此库是一个概念验证。

待办事项

  • 前缀表
  • 配置默认加密
  • 安全定制
  • 测试关系
  • 统计

简介

数据库数据加密是必不可少的,使用 Laravel Eloquent ORM 转换在数据库中加密数据非常简单。

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
    protected $casts = [
        'title' => 'encrypted',
    ];
}

有了加密数据字段,您只能通过精确的字段名值进行搜索。

$p = Post::where('title','Beautiful Post'); // OK

但是不能使用 LIKE 操作符

$p = Post::where('title','LIKE','%Bea%'); // WRONG!

因为真实数据以加密形式存储在数据库表中

"id";"title"
"1";"7OioIr/njEtH0fFHDoVopndh2z/yfQ4r8i40QlZjaITQ7Mh5QwnhH/Gjug=="

Laravel 包含 Eloquent,一个对象关系映射器 (ORM)。通过扩展 Eloquent 类并使用 Laravel Traits,可以构建一个库,允许您使用“彩虹表索引”对加密文本进行全文搜索。

想法...

目标:在加密数据中使用 LIKE 操作符进行搜索。通过库构建一个替代索引(彩虹表索引),可以索引加密值并查询这些值。

这是一个工作示例,有一个包含字段 "id" 和 "title" 的 Post 模型。我们创建一个 Post 项目

Post::create(['id'=> 88, 'title' => 'Beauty']); 

生成的 RainbowTableIndex(最小标记大小为 3)

Rainbow Table Index data
post:title BEA 88
post:title EAU 88
post:title AUT 88
post:title UTY 88
post:title BEAU 88
post:title EAUT 88
post:title AUTY 88
post:title BEAUTY 88

对加密字段进行 LIKE 搜索

$p = Post::where('title','LIKE','%BEA%');

首先自动转换为对彩虹表索引的搜索,然后是标记 'BEA' 的 id,查询变为

$p = Post::whereIn('id', [88]);

彩虹表索引 - 详情

“彩虹表索引”是一个由 RainbowTableService 构建的索引。Eloquent 模型是一个与数据库交互的对象。这是一个示例

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Paulodiff\RainbowTableIndex\RainbowTableIndexTrait;

class Author extends Model
{
    use RainbowTableIndexTrait;
    protected $fillable = [
      'name'
    ];
    // Rainbow table configuration
    public static $rainbowTableIndexConfig = [
        'table' => [
            'primaryKey' => 'id',
            'tableName' => 'authors',
        ],
        'fields' => [
            [
              'fName' => 'name',
              'fType' => 'ENCRYPTED_FULL_TEXT',
              'fSafeChars' => " 'àèéìòùqwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.",
              'fTransform' => 'UPPER_CASE',
              'fMinTokenLen' => 3,
            ]
        ]
    ];
}

此模型定义了一个具有一个加密字段 'name' 的 Author。彩虹表索引配置

  • primaryKey:表的键
  • tableName:数据库表名

对于每个要加密和用彩虹表索引索引的字段,您定义以下内容

  • fName:字段名
  • fType:ENCRYTPED_FULL_TEXT(其他类型即将推出)
  • fSafeChars:用于清理字段值的字符数组
  • fTransform:UPPER_CASE|LOWER_CASE|NONE 应用字段转换
  • fMinTokenLen:在标记生成中使用的最小标记大小。如果 = 0,则标记器返回完整数据。

当创建 Eloquent 模型时,会在数据库表中插入一行,并加密配置的所有字段。然后创建所有条目在 RainbowTableIndex。

Author::create(['id'=> 1, 'name' => 'Billy White']); 

显示数据库作者数据

id;name;
1ZXlKcGRpSTZJa1JEUTB4cWNuTmxTRzFOSzFZMVowNUxiazVITjFFOVBTSXNJblpoYkhWbElqb2lUMFp2V1hwRVlsSkVWSFZ3TDBGNldURklXRzVuVEZnMk16QXhVQ3RpY1dWUVowMTJha1V5WkdOeFNUMGlMQ0p0WVdNaU9pSm1NbUpqWTJSaE5HSTNORFUxTlRjME16ZGlOalEyWVRjd05tRXdORFF4T1RCbU9EZGtaREl6TlRsallqa3laR0UwTTJJelkyVTFaV1kyWWpFd00yUXpJaXdpZEdGbklqb2lJbjA9;

显示与字段 'name' 相关的彩虹表索引数据。

table name: rt_6f48819d50e9b840e0c9a5e4a1375145
table data:
rt_key;rt_value
7327b0fa163b48eb9dca10700a55d717406e25f24da3223d78257bb55cbd5e77;1
3da30095b68d8df0348b86660aa9f8c33617b2f9bbecae4ef342bd708fcbf1fc;1
89aa7c63a9087d2f4da32e49d5949024d9c9a41cde32bb0c80ea3edc28c9384b;1
979d88c360ae25de60c5afdf97e48760e1dbff047c595431272d3c69c0056484;1
91024262f3055431a4aca784cca25fde500a3e6a498e12717d498667c322fec3;1
bf64a77aef0838485fff59fde2c889bea48c451ad7c157f005d5b93bf686ddb1;1
3d5834be7f15366d5a5d9c823d3e39713f932b6378f210cf3e842417bd20f81f;1
334dea03de177f5f22bfd8a571f4f1c696be6d08fa135a161680a3b0dfa1302c;1
4b0cdc4e3d3c57e606bdd14b4c69b4eb0d6535947290429fa135231b150b1173;1
0f91f40beba2d9e7400653124342097b17a05d62ddbc4fe8b4205d0a256344e2;1
b5da40127d66e01bc324557fa056c3ed89b7c44b832399c2373df3dc7ea60b29;1
fad5fc5354ddbd9adaccd041f96e7aa4c8a460d4acd94c0eb099ef637c6fb6df;1
f0647065844b606d53db1adef3d540404cc00e9ab040d14c422f6f4b9f5e4979;1
a5fd15ffb7150fb7dec15b758f3a118d724b11c67a4483ac2ec6d8b631d0320b;1
...

当需要在 RainbowTableIndex 中插入数据时,会应用转换

  1. 仅“安全字符”有效 - fSafeChars
  2. 可选:CASE 转换 - fTransform
  3. 标记大小生成 - fMinTokenLen

示例

地址数据:"77712 O'Conner Plain Apt. 996 nw"

  1. 安全字符过滤器:"O'Conner Plain Apt. nw"
  2. 大小写转换:"O'CONNER PLAIN APT. NW"
  3. 标记化"[O'C]['CO]ONNER PLAIN APT. NW".... TODO (NW 跳过!)

如果模型被更新或删除,彩虹表索引也会更新。

RainbowTableIndex 特性

  • 当创建/更新 Eloquent 模型(数据库项)时,自动创建/更新彩虹表索引
  • 所有值都是哈希数据
  • 索引表名是加密的
  • 每个 Eloquent 模型数据库表都包含所有彩虹表索引数据

用例

该库可用于需要保证敏感数据隐私,并需要执行LIKE等搜索的场景。

  • 姓名或姓氏
  • 地址
  • 信用卡号码

安装

要求

  • Laravel 8
  • Mysql
  • Redis(即将推出)

设置

创建Laravel应用程序

composer create-project laravel/laravel rainbow-table-index-app
cd rainbow-table-index-app
composer require paulodiff/rainbow-table-index
php artisan vendor:publish --provider="Paulodiff\RainbowTableIndex\RainbowTableIndexServiceProvider" --tag="config"

检查.env文件中的Laravel Mysql配置

运行检查配置

php artisan RainbowTableIndex:checkConfig

配置选项

  • .env配置

启用调试信息 DEBUG_LEVEL = debug

  • config/rainbowtableindexconfig.php

为不加密调试RainbowIndexTable使用以下选项:RAINBOW_TABLE_INDEX_ENCRYPT=true

一切正常!您可以开始了!

创建一个工作演示

警告!此过程将在数据库中创建表

  • 作者

并为测试填充这些表1000行数据。

复制模型

在app/models中,从...复制Author.php和Posts.php

运行dbseed

运行dbStats

创建模型Author

composer create-project laravel/laravel rainbow-table-index-app

cd rainbow-table-index-app

composer require paulodiff/rainbow-table-index


php artisan RainbowTableIndex:keyGenerator

.env
RAINBOW_TABLE_INDEX_ENCRYPT=true
RAINBOW_TABLE_INDEX_PREFIX=rti // TODO

php artisan RainbowTableIndex:checkConfig

配置并运行演示和测试

创建迁移

php artisan make:migration create_authors_table

编辑迁移文件

    public function up()
    {
       Schema::create('authors', function (Blueprint $table) {
            $table->increments('id');
            $table->text('name');
            $table->text('name_enc'); // for test only
            $table->text('card_number');
            $table->text('card_number_enc'); // for test only
            $table->text('address');
            $table->text('address_enc'); // for test only
            $table->text('role');
            $table->text('role_enc'); // for test only
            $table->timestamps();
        });
    }
    public function down()
    {
        Schema::dropIfExists('authors');
    }

迁移数据库

php artisan migrate

创建Author模型

编辑App/Models/Author.php

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Paulodiff\RainbowTableIndex\RainbowTableIndexTrait;

class Author extends Model
{
    use HasFactory;
    use RainbowTableIndexTrait;

    public static $rainbowTableIndexConfig = [
  
        'table' => [
            'primaryKey' => 'id',
            'tableName' => 'authors',
        ],
        'fields' => [
            [
              'fName' => 'name_enc',
              'fType' => 'ENCRYPTED_FULL_TEXT',
              'fSafeChars' => " 'àèéìòùqwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.",
              'fTransform' => 'UPPER_CASE',
              'fMinTokenLen' => 3,
            ],
            [
                'fName' => 'address_enc',
                'fType' => 'ENCRYPTED_FULL_TEXT',
                'fSafeChars' => " 'àèéìòùqwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.",
                'fTransform' => 'UPPER_CASE',
                'fMinTokenLen' => 4,
            ],
            [
                'fName' => 'card_number_enc',
                'fType' => 'ENCRYPTED_FULL_TEXT',
                'fSafeChars' => '1234567890',
                'fTransform' => 'NONE',
                'fMinTokenLen' => 4,
            ],
            [
                'fName' => 'role_enc',
                'fType' => 'ENCRYPTED',
                'fSafeChars' => ' àèéìòùqwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.',
                'fTransform' => 'ENCRYPTED_FULL_TEXT',
                'fMinTokenLen' => 0,
            ],
        ]
    ];
}

运行db seed(100行)

php artisan RainbowTableIndex:dbSeed 100

运行搜索测试和指标(100)

php artisan RainbowTableIndex:dbCrud 100

停止!Rainbow索引已配置并运行,您可以使用Web应用程序进行测试!

这是一个包含5个表的Web应用程序CRUD演示。

Player
    'player_id'
    'player_name',           // ENCRYPTED
    'player_address',  
    'player_credit_card_no', // ENCRYPTED
    'player_phone',

PlayerRole
    'player_role_id'
    'player_role_description', // ENCRYPTED
    'player_role_salary',

Team
    'team_id',   // ENCRYPTED
    'team_name',
    'team_type_id', (1-1 with TeamType)

TeamType
    'team_type_id'
    'team_type_description',  // ENCRYPTED
    'team_type_rules',

Roster
    'roster_id'
    'roster_description',  // ENCRYPTED
    'roster_player_id',
    'roster_team_id',
    'roster_player_role_id',
    'roster_amount',       // ENCRYPTED

Livewire安装 composer require livewire/livewire

设置Mysql Set .env 复制模型并重命名每个模型的路径 复制资源 复制web.app

php artisan RainbowTableIndex:dbCrud 100
php artisan optmize
php artisan serve

composer require livewire/livewire

php artisan make:livewire posts

app/Http/Livewire/Authors.php

resources/views/livewire/authors.blade.php

# Security customization

- Laravel Encrypt/Decrypt functions
- Hash



# Service operation

You can rebuild all indexed data for an intaciated model:

```php
$a = Author::where('id', 3)->first();
$r1 = $a->rebuildRainbowIndex();

或者您可以重建模型的所有实例的所有索引

$r2 = Author::rebuildFullRainbowIndex();

统计/性能和维度

性能系统特性

  • I3 / 16gb
  • Windows 10 / XAMPP
  • 1000个作者
  • 4000篇帖子

实例的访问时间不同。我的测试

  • 1000实例
  • 索引维度
  • 访问时间。 ù [2022-01-07 12:25:55] testing.INFO: CRUD:TOTAL TIMING: [["name_enc-2613#2613-time (enc,flat) : . [16.161010447761,2.5544727133563]","address_enc-1408#1408-time (enc,flat) : . [10.007975923295,2.5428640625]","card_number_enc-9100#9100-time (enc,flat) : . [43.496440604396,2.1048867032967]","role_enc-5#5-time (enc,flat) : . [5.10922,5.35346]"]]

可以计算要索引的每个数据行的索引数量($w是标记长度,$s是要分词的字符串)

for ($w=3;$w<= strlen($s); $w++) $numOfEntries += (strlen($s) + 1 - $w);

自定义

sanitize_string

最终考虑

  • 优点 -- 加密!加密!!加密!!!

缺点

  • 对加密数据的OrderBy不起作用! -> 使用集合排序!
  • 其他SQL函数(SUM,MIN等)不起作用
  • 搜索开销

未来工作

alt text