mirror of
https://github.com/Bubka/2FAuth.git
synced 2024-11-09 01:44:59 +01:00
Add a user option to encrypt/decrypt sensitive db data
This commit is contained in:
parent
fe02bac6d6
commit
53bb3b9c54
@ -2,9 +2,15 @@
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use Throwable;
|
||||
use App\TwoFAccount;
|
||||
use App\Classes\Options;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Contracts\Encryption\EncryptException;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
|
||||
class OptionController extends Controller
|
||||
{
|
||||
@ -29,9 +35,115 @@ public function index()
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// The useEncryption option impacts the [existing] content of the database.
|
||||
// Encryption/Decryption of the data is done only if the user change the value of the option
|
||||
// to prevent successive encryption
|
||||
|
||||
if( $request->useEncryption && !Options::get('useEncryption') ) {
|
||||
|
||||
// user enabled the encryption
|
||||
if( !$this->encryptAccounts() ) {
|
||||
return response()->json(['message' => __('errors.error_during_encryption'), 'settings' => Options::get()], 422);
|
||||
}
|
||||
}
|
||||
else if( !$request->useEncryption && Options::get('useEncryption') ) {
|
||||
|
||||
// user disabled the encryption
|
||||
if( !$this->decryptAccounts() ) {
|
||||
return response()->json(['message' => __('errors.error_during_decryption'), 'settings' => Options::get()], 422);
|
||||
}
|
||||
}
|
||||
|
||||
// Store all options
|
||||
Options::store($request->all());
|
||||
|
||||
return response()->json(['message' => __('settings.forms.setting_saved'), 'settings' => Options::get()], 200);
|
||||
return response()->json(['message' => __('settings.forms.setting_saved'), 'settings' => Options::get()], 200);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt 2FA sensitive data
|
||||
* @return boolean
|
||||
*/
|
||||
private function encryptAccounts() : bool
|
||||
{
|
||||
// All existing records have to be encrypted without exception.
|
||||
// This means that if any of the encryption failed we have to rollback
|
||||
// all records to their original value.
|
||||
|
||||
$twofaccounts = TwoFAccount::all();
|
||||
|
||||
$twofaccounts->each(function ($item, $key) {
|
||||
try {
|
||||
$item->uri = Crypt::encryptString($item->uri);
|
||||
$item->account = Crypt::encryptString($item->account);
|
||||
}
|
||||
catch (EncryptException $e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return $this->tryUpdate($twofaccounts);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt 2FA sensitive data
|
||||
* @return boolean
|
||||
*/
|
||||
private function decryptAccounts() : bool
|
||||
{
|
||||
// All existing records have to be decrypted without exception.
|
||||
// This means that if any of the encryption failed we have to rollback
|
||||
// all records to their original value.
|
||||
|
||||
$twofaccounts = TwoFAccount::all();
|
||||
|
||||
$twofaccounts->each(function ($item, $key) {
|
||||
try {
|
||||
$item->uri = Crypt::decryptString($item->uri);
|
||||
$item->account = Crypt::decryptString($item->account);
|
||||
}
|
||||
catch (DecryptException $e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return $this->tryUpdate($twofaccounts);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to update all records of the collection
|
||||
* @param Illuminate\Database\Eloquent\Collection $twofaccounts
|
||||
* @return boolean
|
||||
*/
|
||||
private function tryUpdate(\Illuminate\Database\Eloquent\Collection $twofaccounts) : bool
|
||||
{
|
||||
// The whole collection has its sensible data encrypted/decrypted, now we update the db
|
||||
// using a transaction to ensure rollback if an exception is thrown
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$twofaccounts->each(function ($item, $key) {
|
||||
DB::table('twofaccounts')
|
||||
->where('id', $item->id)
|
||||
->update([
|
||||
'uri' => $item->uri,
|
||||
'account' => $item->account
|
||||
]);
|
||||
});
|
||||
|
||||
DB::commit();
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,12 @@
|
||||
|
||||
use OTPHP\HOTP;
|
||||
use OTPHP\Factory;
|
||||
use App\Classes\Options;
|
||||
use Spatie\EloquentSortable\Sortable;
|
||||
use Spatie\EloquentSortable\SortableTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
|
||||
class TwoFAccount extends Model implements Sortable
|
||||
@ -157,33 +159,70 @@ public function getCounterAttribute()
|
||||
|
||||
|
||||
/**
|
||||
* Set the user's first name.
|
||||
* Set encrypted uri
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
// public function setUriAttribute($value)
|
||||
// {
|
||||
// $this->attributes['uri'] = encrypt($value);
|
||||
// }
|
||||
public function setUriAttribute($value)
|
||||
{
|
||||
$this->attributes['uri'] = Options::get('useEncryption') ? Crypt::encryptString($value) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's first name.
|
||||
* Get decyphered uri
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
// public function getUriAttribute($value)
|
||||
// {
|
||||
// try {
|
||||
public function getUriAttribute($value)
|
||||
{
|
||||
if( Options::get('useEncryption') )
|
||||
{
|
||||
try {
|
||||
return Crypt::decryptString($value);
|
||||
}
|
||||
catch (DecryptException $e) {
|
||||
return '*encrypted*';
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
// return decrypt($value);
|
||||
|
||||
// } catch (DecryptException $e) {
|
||||
/**
|
||||
* Set encrypted account
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function setAccountAttribute($value)
|
||||
{
|
||||
$this->attributes['account'] = Options::get('useEncryption') ? Crypt::encryptString($value) : $value;
|
||||
}
|
||||
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// }
|
||||
/**
|
||||
* Get decyphered account
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getAccountAttribute($value)
|
||||
{
|
||||
if( Options::get('useEncryption') )
|
||||
{
|
||||
try {
|
||||
return Crypt::decryptString($value);
|
||||
}
|
||||
catch (DecryptException $e) {
|
||||
return '*encrypted*';
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
'showAccountsIcons' => true,
|
||||
'kickUserAfter' => '15',
|
||||
'activeGroup' => 0,
|
||||
'useEncryption' => false,
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -2,14 +2,25 @@
|
||||
<form-wrapper>
|
||||
<form @submit.prevent="handleSubmit" @change="handleSubmit" @keydown="form.onKeydown($event)">
|
||||
<h4 class="title is-4">{{ $t('settings.general') }}</h4>
|
||||
<!-- Language -->
|
||||
<form-select :options="langs" :form="form" fieldName="lang" :label="$t('settings.forms.language.label')" :help="$t('settings.forms.language.help')" />
|
||||
<!-- display mode -->
|
||||
<form-select :options="layouts" :form="form" fieldName="displayMode" :label="$t('settings.forms.display_mode.label')" :help="$t('settings.forms.display_mode.help')" />
|
||||
<!-- show icon -->
|
||||
<form-checkbox :form="form" fieldName="showAccountsIcons" :label="$t('settings.forms.show_accounts_icons.label')" :help="$t('settings.forms.show_accounts_icons.help')" />
|
||||
|
||||
<h4 class="title is-4">{{ $t('settings.security') }}</h4>
|
||||
<!-- auto lock -->
|
||||
<form-select :options="kickUserAfters" :form="form" fieldName="kickUserAfter" :label="$t('settings.forms.auto_lock.label')" :help="$t('settings.forms.auto_lock.help')" />
|
||||
<!-- protect db -->
|
||||
<form-checkbox :form="form" fieldName="useEncryption" :label="$t('settings.forms.use_encryption.label')" :help="$t('settings.forms.use_encryption.help')" />
|
||||
<!-- token as dot -->
|
||||
<form-checkbox :form="form" fieldName="showTokenAsDot" :label="$t('settings.forms.show_token_as_dot.label')" :help="$t('settings.forms.show_token_as_dot.help')" />
|
||||
<!-- close token on copy -->
|
||||
<form-checkbox :form="form" fieldName="closeTokenOnCopy" :label="$t('settings.forms.close_token_on_copy.label')" :help="$t('settings.forms.close_token_on_copy.help')" />
|
||||
|
||||
<h4 class="title is-4">{{ $t('settings.advanced') }}</h4>
|
||||
<!-- basic qrcode -->
|
||||
<form-checkbox :form="form" fieldName="useBasicQrcodeReader" :label="$t('settings.forms.use_basic_qrcode_reader.label')" :help="$t('settings.forms.use_basic_qrcode_reader.help')" />
|
||||
</form>
|
||||
</form-wrapper>
|
||||
@ -30,6 +41,7 @@
|
||||
showAccountsIcons: this.$root.appSettings.showAccountsIcons,
|
||||
displayMode: this.$root.appSettings.displayMode,
|
||||
kickUserAfter: this.$root.appSettings.kickUserAfter,
|
||||
useEncryption: this.$root.appSettings.useEncryption,
|
||||
}),
|
||||
langs: [
|
||||
{ text: this.$t('languages.en'), value: 'en' },
|
||||
@ -73,7 +85,7 @@
|
||||
|
||||
this.$notify({ type: 'is-danger', text: error.response.data.message })
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -23,6 +23,8 @@
|
||||
],
|
||||
'something_wrong_with_server' => 'Something is wrong with your server',
|
||||
'Unable_to_decrypt_uri' => 'Unable to decrypt uri',
|
||||
'wrong_current_password' => 'Wrong current password, nothing has changed'
|
||||
'wrong_current_password' => 'Wrong current password, nothing has changed',
|
||||
'error_during_encryption' => 'Encryption failed, your database remains unprotected',
|
||||
'error_during_decryption' => 'Decryption failed, your database is still protected',
|
||||
|
||||
];
|
@ -56,6 +56,10 @@
|
||||
'label' => 'Auto lock',
|
||||
'help' => 'Log out the user automatically in case of inactivity'
|
||||
],
|
||||
'use_encryption' => [
|
||||
'label' => 'Protect sensible data',
|
||||
'help' => 'Sensitive data, the 2FA secrets and emails, are stored encrypted in database. Be sure to backup the APP_KEY value of your .env file (or the whole file) as it serves as key encryption. There is no way to decypher encrypted data without this key.',
|
||||
],
|
||||
'never' => 'Never',
|
||||
'on_token_copy' => 'On security code copy',
|
||||
'1_minutes' => 'After 1 minute',
|
||||
@ -66,6 +70,5 @@
|
||||
'1_hour' => 'After 1 hour',
|
||||
'1_day' => 'After 1 day',
|
||||
],
|
||||
|
||||
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user