Skip to content

Commit 64320ca

Browse files
committed
changes for 2.0.0
1 parent dcee5f8 commit 64320ca

13 files changed

+382
-258
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2-
/vendor
2+
vendor
33
composer.lock
44
.phpunit.result.cache
5+
workbench

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## 2.0.0 - 2024-11-05
2+
3+
- Added new AES key generation command (using sha512 hashing alorithm as per [MySQL encrypt docs](https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-decrypt))
4+
- Refactored validators to implement ValidationRule contract
5+
- Automatic decryption when accessing properties on a model
6+
- Fixed a bug where encrypted model fields where not being hydrated with decrypted values (i.e. on creation)
7+
- Updated MySQL AES_DECRYPT/AES_ENCRYPT queries as per [MySQL encrypt docs](https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-decrypt)
8+
- Updated README with example usage
9+
- Discontinued Lumen support as the project has been archived as of Apr 9, 2024
10+
11+
## 1.0.0
12+
13+
- Initial release

README.md

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,40 @@
1-
# Laravel/Lumen MySql AES Encrypt/Decrypt
1+
# Laravel MySql AES Encrypt/Decrypt
22

33
[![Latest Stable Version](https://poser.pugx.org/chr15k/laravel-mysql-encrypt/v)](//packagist.org/packages/chr15k/laravel-mysql-encrypt) [![Latest Unstable Version](https://poser.pugx.org/chr15k/laravel-mysql-encrypt/v/unstable)](//packagist.org/packages/chr15k/laravel-mysql-encrypt) [![Total Downloads](https://poser.pugx.org/chr15k/laravel-mysql-encrypt/downloads)](//packagist.org/packages/chr15k/laravel-mysql-encrypt) [![License](https://poser.pugx.org/chr15k/laravel-mysql-encrypt/license)](//packagist.org/packages/chr15k/laravel-mysql-encrypt)
44

5-
Laravel/Lumen database encryption at database side using native AES_DECRYPT and AES_ENCRYPT functions.
5+
Laravel MySQL encryption using native MySQL AES_DECRYPT and AES_ENCRYPT functions.
66
Automatically encrypt and decrypt fields in your Models.
77

88
## Install
9+
910
### 1. Composer
11+
1012
```bash
1113
composer require chr15k/laravel-mysql-encrypt
1214
```
1315

14-
### 2. Publish config (optional)
15-
`Laravel`
16-
```bash
17-
php artisan vendor:publish --provider="Chr15k\MysqlEncrypt\Providers\LaravelServiceProvider"
18-
```
16+
### 2. Publish config
1917

20-
`Lumen`
2118
```bash
22-
mkdir -p config
23-
cp vendor/chr15k/laravel-mysql-encrypt/config/config.php config/mysql-encrypt.php
19+
php artisan vendor:publish --provider="Chr15k\MysqlEncrypt\Providers\MysqlEncryptServiceProvider"
2420
```
2521

26-
### 3. Configure Provider
27-
`Laravel`
28-
- For Laravel 5.5 or later, the service provider is automatically loaded, skip this step.
22+
### 3. AES Key generation
2923

30-
- For Laravel 5.4 or earlier, add the following to `config/app.php`:
31-
```php
32-
'providers' => array(
33-
Chr15k\\MysqlEncrypt\\Providers\\LaravelServiceProvider::class
34-
);
35-
```
24+
Add to .env file:
3625

37-
`Lumen`
38-
- For Lumen, add the following to `bootstrap/app.php`:
39-
```php
40-
$app->register(Chr15k\MysqlEncrypt\Providers\LumenServiceProvider::class);
4126
```
42-
43-
### 4. Set encryption key in `.env` file
27+
APP_AESENCRYPT_KEY=
4428
```
45-
APP_AESENCRYPT_KEY=yourencryptionkey
29+
30+
Generate a new secure AES key (or add your existing key manually):
31+
32+
```bash
33+
php artisan laravel-mysql-encrypt:key:generate
4634
```
4735

4836
## Update Models
37+
4938
```php
5039
<?php
5140

@@ -68,17 +57,21 @@ class User extends Model
6857
```
6958

7059
## Validators
60+
7161
`unique_encrypted`
62+
7263
```
73-
unique_encrypted:<table>,<field(optional)>
64+
unique_encrypted:<table>,<field(optional)>,<ignore_id(optional)>
7465
```
7566

7667
`exists_encrypted`
68+
7769
```
7870
exists_encrypted:<table>,<field(optional)>
7971
```
8072

8173
## Scopes
74+
8275
Custom Local scopes available:
8376

8477
`whereEncrypted`
@@ -90,6 +83,7 @@ Custom Local scopes available:
9083
Global scope `DecryptSelectScope` automatically booted in models using `Encryptable` trait.
9184

9285
## Schema columns to support encrypted data
86+
9387
```php
9488
Schema::create('users', function (Blueprint $table) {
9589
$table->bigIncrements('id');
@@ -106,5 +100,66 @@ DB::statement('ALTER TABLE `users` ADD `email` VARBINARY(300)');
106100
DB::statement('ALTER TABLE `users` ADD `telephone` VARBINARY(50)');
107101
```
108102

103+
## Example Usage
104+
105+
```php
106+
<?php
107+
108+
use App\Models\User;
109+
use Chr15k\MysqlEncrypt\Rules\ExistsEncrypted;
110+
use Chr15k\MysqlEncrypt\Rules\UniqueEncrypted;
111+
use Illuminate\Support\Facades\Hash;
112+
use Illuminate\Support\Facades\Route;
113+
use Illuminate\Support\Facades\Validator;
114+
use Illuminate\Support\Str;
115+
116+
Route::get('/', function () {
117+
118+
$user = User::firstOrCreate([
119+
'email' => '[email protected]',
120+
], [
121+
'name' => 'Test',
122+
'email' => '[email protected]',
123+
'email_verified_at' => now(),
124+
'password' => Hash::make('password'),
125+
'remember_token' => Str::random(10),
126+
]);
127+
128+
// querying an encrypted value using the base methods will not work (as expected):
129+
dump(User::where('name', 'Test')->first()); // => null
130+
131+
// querying through the encrypted scopes will decrypt the value as expected:
132+
dump(
133+
User::whereEncrypted('name', 'Test')
134+
->orWhereEncrypted('name', 'Chris')
135+
->first()
136+
); // => App\Models\User
137+
138+
// Accessing the encrypted attribute on the model will automatically decrypt the value:
139+
dump($user->name); // => 'Test'
140+
141+
142+
// Validation rules
143+
144+
// ExistsEncrypted
145+
$validator = Validator::make(['name' => 'Chris'], [
146+
'name' => [new ExistsEncrypted('users')],
147+
]);
148+
if ($validator->fails()) {
149+
dump($validator->errors()->first()); // => "The selected name does not exist"
150+
}
151+
152+
// UniqueEncrypted
153+
$validator = Validator::make(['name' => 'Test'], [
154+
'name' => [new UniqueEncrypted('users')],
155+
]);
156+
if ($validator->fails()) {
157+
dump($validator->errors()->first()); // => "The name field must be unique"
158+
}
159+
160+
});
161+
```
162+
109163
## License
164+
110165
The MIT License (MIT). Please see [License File](https://github.com/chr15k/laravel-mysql-encrypt/blob/master/LICENSE) for more information.

composer.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
22
"name": "chr15k/laravel-mysql-encrypt",
3-
"description": "Laravel 5.x | 6.x | 7.x | 8.x Database encryption mysql side",
3+
"description": "Laravel database encryption using native MySQL functions",
44
"keywords": [
55
"laravel",
66
"chr15k",
77
"mysql",
88
"encryption",
99
"php",
10-
"pii"
10+
"pii",
11+
"gdpr"
1112
],
1213
"license": "MIT",
1314
"authors": [
@@ -17,13 +18,15 @@
1718
}
1819
],
1920
"require": {
20-
"php": "^7.2.5",
21-
"illuminate/support": "^5.0|^6.0|^7.0|^8.0",
22-
"illuminate/database": "^5.0|^6.0|^7.0|^8.0"
21+
"php": "^8.0",
22+
"illuminate/support": "^8.0|^9.0|^10.0|^11.0",
23+
"illuminate/database": "^8.0|^9.0|^10.0|^11.0",
24+
"illuminate/console": "^8.0|^9.0|^10.0|^11.0",
25+
"illuminate/contracts": "^8.0|^9.0|^10.0|^11.0"
2326
},
2427
"autoload": {
2528
"psr-4": {
26-
"Chr15k\\MysqlEncrypt\\": "src/"
29+
"Chr15k\\MysqlEncrypt\\": "src"
2730
},
2831
"files": [
2932
"src/helpers.php"
@@ -35,7 +38,7 @@
3538
"extra": {
3639
"laravel": {
3740
"providers": [
38-
"Chr15k\\MysqlEncrypt\\Providers\\LaravelServiceProvider"
41+
"Chr15k\\MysqlEncrypt\\Providers\\MysqlEncryptServiceProvider"
3942
]
4043
}
4144
},
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
namespace Chr15k\MysqlEncrypt\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Console\ConfirmableTrait;
7+
use Illuminate\Support\Facades\Validator;
8+
use Illuminate\Support\Str;
9+
use Symfony\Component\Console\Attribute\AsCommand;
10+
11+
#[AsCommand(name: 'laravel-mysql-encrypt:key:generate')]
12+
class AesKeyGenerateCommand extends Command
13+
{
14+
use ConfirmableTrait;
15+
16+
protected $signature = 'laravel-mysql-encrypt:key:generate';
17+
18+
protected $description = 'Generate secure AES Key hash';
19+
20+
/** @see https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-decrypt */
21+
const HASHING_ALGORITHM = 'sha512';
22+
23+
/**
24+
* Execute the console command.
25+
*/
26+
public function handle()
27+
{
28+
do {
29+
$passphrase = $this->secret('Passphrase?');
30+
31+
$validator = Validator::make(['passphrase' => $passphrase], [
32+
'passphrase' => 'required|min:3|max:255',
33+
]);
34+
35+
if ($validator->fails()) {
36+
$this->error($validator->errors()->first());
37+
}
38+
} while ($validator->fails());
39+
40+
$key = hash(self::HASHING_ALGORITHM, $passphrase);
41+
42+
if (! $this->setKeyInEnvironmentFile($key)) {
43+
return Command::FAILURE;
44+
}
45+
46+
$this->laravel['config']['mysql-encrypt.key'] = $key;
47+
48+
$this->info('AES key set successfully.');
49+
50+
return Command::SUCCESS;
51+
}
52+
53+
/**
54+
* Set the application key in the environment file.
55+
*/
56+
protected function setKeyInEnvironmentFile(string $key): bool
57+
{
58+
$currentKey = $this->laravel['config']['mysql-encrypt.key'];
59+
60+
if (strlen($currentKey) !== 0 && (! $this->confirmToProceed())) {
61+
return false;
62+
}
63+
64+
if (! $this->writeNewEnvironmentFileWith($key)) {
65+
return false;
66+
}
67+
68+
return true;
69+
}
70+
71+
/**
72+
* Write a new environment file with the given key.
73+
*/
74+
protected function writeNewEnvironmentFileWith(string $key): bool
75+
{
76+
$replaced = preg_replace(
77+
$this->keyReplacementPattern(),
78+
'APP_AESENCRYPT_KEY='.$key,
79+
$input = file_get_contents($this->laravel->environmentFilePath())
80+
);
81+
82+
$exists = Str::contains($replaced, 'APP_AESENCRYPT_KEY=');
83+
$error = 'Unable to set application key.';
84+
85+
if ($exists && $input === $replaced) {
86+
$this->comment($error.' No change detected for APP_AESENCRYPT_KEY in the .env file, skipping...');
87+
88+
return false;
89+
}
90+
91+
if (is_null($replaced) || ! $exists) {
92+
$this->error($error.' No APP_AESENCRYPT_KEY variable was found in the .env file.');
93+
94+
return false;
95+
}
96+
97+
file_put_contents($this->laravel->environmentFilePath(), $replaced);
98+
99+
return true;
100+
}
101+
102+
/**
103+
* Get a regex pattern that will match env APP_AESENCRYPT_KEY with any random key.
104+
*/
105+
protected function keyReplacementPattern(): string
106+
{
107+
return sprintf(
108+
'/^APP_AESENCRYPT_KEY%s/m',
109+
preg_quote('='.$this->laravel['config']['mysql-encrypt.key'], '/')
110+
);
111+
}
112+
}

src/Providers/LumenServiceProvider.php

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)