-
Notifications
You must be signed in to change notification settings - Fork 11
Silverstripe 4 compatibility #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
madmatt
merged 12 commits into
madmatt:master
from
silverstripeltd:pulls/TMP-ss4-upgrade
Oct 26, 2023
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
a213e18
Continued work on Silverstripe CMS 4 upgrade, now all working besides…
madmatt 2eaa37f
Update checks for each fields
5353fbc
File::getFullPath() SS3 Incompatibility fixes
7adc3c7
Merge pull request #19 from ssmarco/pulls/TMP-ss4-upgrade
madmatt c8af366
Fixes to catch Exceptions when decrypting values
a5b6634
Merge pull request #20 from ssmarco/pulls/TMP-ss4-upgrade
madmatt 551e876
Update tests and additional fallback to delete source file after encr…
8caa780
Merge pull request #21 from ssmarco/pulls/TMP-ss4-upgrade
madmatt eb764c4
Bugfix when changing back to original filename
932ad86
Merge pull request #23 from ssmarco/pulls/TMP-ss4-upgrade
madmatt ef8ff75
Merge branch 'master' into pulls/TMP-ss4-upgrade
edwilde 920ad20
Update minimum requirements after PR was merged
edwilde File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
mappings: | ||
AtRestCryptoService: Madmatt\EncryptAtRest\AtRestCryptoService | ||
EncryptDataObjectFieldsExtension: Madmatt\EncryptAtRest\Extension\DecryptDataObjectFieldsExtension | ||
EncryptedDatetime: Madmatt\EncryptAtRest\FieldType\EncryptedDatetime | ||
EncryptedDecimal: Madmatt\EncryptAtRest\FieldType\EncryptedDecimal | ||
EncryptedEnum: Madmatt\EncryptAtRest\FieldType\EncryptedEnum | ||
EncryptedInt: Madmatt\EncryptAtRest\FieldType\EncryptedInt | ||
EncryptedText: Madmatt\EncryptAtRest\FieldType\EncryptedText | ||
EncryptedVarchar: Madmatt\EncryptAtRest\FieldType\EncryptedVarchar |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,113 @@ | ||
# silverstripe-encrypt-at-rest | ||
|
||
This module allows data to be encrypted in the database, but be decrypted when extracted from the database, using a | ||
secret key (hopefully) known only by the web server. | ||
This module allows Silverstripe CMS ORM data to be encrypted before being stored in the database, and automatically decrypted before using within your application. To do this, we use a secret key known only by the web server. | ||
|
||
*Note:* This does not provide significant protection except in the case of database compromise. It should be used as | ||
part of a layered security strategy. This is because the key is still available on the web server, so if remote code | ||
execution is achieved by an attacker, they will be able to read both the database *and* the encryption key, thereby | ||
decrypting the content. | ||
|
||
*Note:* This module is not yet ready for real use, it's currently v0.0.1 material. | ||
## Caveats to understand | ||
* It's important to note that this module does not guarantee the security of your data completely. You should only use this as a protection measure if you fully understand how the module operates. In most cases, encrypting the entire database is both adequate and similarly effective. Only use this module to encrypt data at-rest (on a field-by-field basis) if your layered protection strategy requires and accomodates it. To be clear - when encrypting data at rest, the data must be decrypted before being used. In almost all cases, the web server hosting the website is far more accessible to attacks than the the database server, meaning that an attacker who can compromise your web server will have access to both the database and the encryption key used to encrypt the data. | ||
* Encrypting and decrypting data on a field-by-field basis has a performance overhead, which may produce undesirable results in your project. | ||
* This module uses the `defuse/php-encryption` library under the hood, which prefers strong security over performance. Encrypting lots of fields on a `DataObject` can significantly slow down any operations that read or write large amounts of data (for example `ModelAdmin` views in the CMS that render 50+ records at once can take many seconds of processing power just to decrypt fields). Care should be taken to ensure only the minimal set of data is encrypted, and that this data does not need to be used frequently. | ||
|
||
|
||
## Requirements | ||
* SilverStripe CMS 4.9 | ||
|
||
## Installation | ||
Install via Composer: | ||
|
||
``` | ||
composer require madmatt/silverstripe-encrypt-at-rest | ||
``` | ||
|
||
Once installed, you need to generate an encryption key that will be used to encrypt all data. | ||
|
||
1. Generate a hex key with `vendor/bin/generate-defuse-key` (tool supplied by `defuse/php-encryption`). This will output a ASCII-safe key that starts with `def`. | ||
2. Set this key as the environment variable `ENCRYPT_AT_REST_KEY`. | ||
|
||
For development environments you can set this in your `.env` e.g: | ||
|
||
``` | ||
ENCRYPT_AT_REST_KEY="{generated defuse key}" | ||
``` | ||
|
||
For more information view SilverStripe [Environment Management](https://docs.silverstripe.org/en/4/getting_started/environment_management/). | ||
|
||
## Usage | ||
|
||
In your DataObject, use the various field types in this module to have it encrypted. At this point, everything is stored as encrypted text. | ||
In your `DataObject`, create new database fields using an encrypted field type. **Note:** It's not supported to convert an existing field that has data into an encrypted field. This _might_ work but is not guaranteed. You should migrate your data by creating a new field, and creating a task to map old fields to new encrypted fields if necessary. | ||
|
||
For example: | ||
|
||
Ensure you create a key to use for encryption/decryption. You can run the following code to generate a valid key: | ||
```php | ||
$key_object = Defuse\Crypto\Key::createNewRandomKey(); | ||
$key_string = $key_object->saveToAsciiSafeString(); | ||
use Madmatt\EncryptAtRest\FieldType\EncryptedVarchar; | ||
|
||
class SecureDataObject extends DataObject { | ||
|
||
private static $db = [ | ||
'NormalText' => 'Varchar' | ||
'SecureText' => EncryptedVarchar::class | ||
]; | ||
|
||
} | ||
``` | ||
|
||
This key can then be set in your `_ss_environment.php` file: | ||
|
||
``` | ||
define('ENCRYPT_AT_REST_KEY', 'defuse key here'); | ||
``` | ||
See the `src/FieldType` folder for all field types, or review the below list: | ||
|
||
- `Madmatt\EncryptAtRest\FieldType\EncryptedDatetime` | ||
- `Madmatt\EncryptAtRest\FieldType\EncryptedDecimal` | ||
- `Madmatt\EncryptAtRest\FieldType\EncryptedEnum` | ||
- `Madmatt\EncryptAtRest\FieldType\EncryptedInt` | ||
- `Madmatt\EncryptAtRest\FieldType\EncryptedText` | ||
- `Madmatt\EncryptAtRest\FieldType\EncryptedVarchar` | ||
|
||
**Note:** When saving in the database, all of these encrypted fields are stored as `TEXT` column types. This is due to the length of the encrypted data being generally much longer than the original text string. They do not take up table column space, but result in longer query execution times when many fields are included as the database needs to go retrieve all these fields from separate blob storage. | ||
|
||
## TODO | ||
**Note 2:** These fields all extend from the base data type (e.g. `EncryptedDatetime extends DBDatetime`) so most common field helper methods can be used (e.g. `$DatetimeField.Ago`). | ||
|
||
- Make sure $this->value is _always_ the unencrypted value | ||
- Clean up | ||
- EncryptedEnum needs validation | ||
- Extended testing | ||
- Test if the value is actually encrypted, before trying to decrypt | ||
- Merge in and release the SS4 upgrade | ||
Data will be automatically encrypted when values are written to the database, and decrypted whenever that data is read back from the database. | ||
|
||
To use decrypted values, you just use the value like you would in any other context. For example: | ||
|
||
```php | ||
// Via DataObject::get() | ||
$obj = SecureDataObject::get()->first()->SecureText; // Returns the decrypted string from the field | ||
|
||
// Getting the DB field | ||
$obj = SecureDataObject::get()->first(); | ||
$field = $obj->dbObject('SecureText'); // Returns an EncryptedVarchar object | ||
$uppercase = $field->UpperCase(); // Method on DBString, returns a string | ||
``` | ||
|
||
Usage within Silverstripe templates is also straightforward: | ||
|
||
```html | ||
<% loop $SecureDataObjects %> | ||
<p>$SecureText.UpperCase</p> | ||
<% end_loop %> | ||
``` | ||
|
||
If you've use the Silverstripe CMS 3 version of this module, you no longer need to rely on the `->getDecryptedValue()` method - the value will always be decrypted when accessing it. | ||
|
||
### Encrypting and decrypting arbitrary text strings and files without using the ORM | ||
You can also encrypt/decrypt arbitrary text strings as well as entire files on the filesystem without using the Silverstripe ORM (e.g. without using `DataObject`). You might want to do this to securely communicate with an API for example. | ||
|
||
```php | ||
use Madmatt\EncryptAtRest\AtRestCryptoService; | ||
use SilverStripe\Assets\File; | ||
use SilverStripe\Core\Injector\Injector; | ||
|
||
$text = 'This is an unencrypted string!'; | ||
|
||
/** @var AtRestCryptoService $service */ | ||
$service = Injector::inst()->get(AtRestCryptoService::class); | ||
$encryptedText = $service->encrypt($text); // Returns encrypted string starting with `def` | ||
$unencryptedText = $service->decrypt($encryptedText); // Returns 'This is an unencrypted string!' | ||
|
||
$file = File::get()->byID(1); // Presume this is a file that contains the text string above | ||
|
||
// This will encrypt the file contents, delete the original file from the filesystem and create a new file at the same path with .enc appended to the filename | ||
$encryptedFile = $service->encryptFile($file); | ||
|
||
// This will decrypt the file contents, delete the encrypted file from the filesystem and create a new file at the same path with .enc stripped from the filename | ||
$decryptedFile = $service->decryptFile($encryptedFile); | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1 @@ | ||
<?php | ||
// SilverStripe 3.7 and PHP 7.2 compatibility | ||
if (!class_exists('SS_Object')) { | ||
class_alias('Object', 'SS_Object'); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
DataObject: | ||
# Add extension to all DataObject sub-classes | ||
SilverStripe\ORM\DataObject: | ||
extensions: | ||
- EncryptDataObjectFieldsExtension | ||
EncryptDataObjectFieldsExtension: | ||
EncryptedDataObjects: | ||
- EncryptDataObject | ||
File: | ||
- Madmatt\EncryptAtRest\Extension\DecryptDataObjectFieldsExtension | ||
|
||
# Add .enc files as allowed extensions to provide a way to store files in encrypted format | ||
# (see AtRestCryptoService->encryptFile() and ->decryptFile() | ||
SilverStripe\Assets\File: | ||
allowed_extensions: | ||
["enc"] | ||
- enc |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.