Skip to content

Commit 0c1240a

Browse files
authored
Merge pull request #27 from silverstripeltd/pulls/TMP-ss4-upgrade
2 parents bb76999 + 920ad20 commit 0c1240a

26 files changed

+1155
-452
lines changed

.upgrade.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
mappings:
2+
AtRestCryptoService: Madmatt\EncryptAtRest\AtRestCryptoService
3+
EncryptDataObjectFieldsExtension: Madmatt\EncryptAtRest\Extension\DecryptDataObjectFieldsExtension
4+
EncryptedDatetime: Madmatt\EncryptAtRest\FieldType\EncryptedDatetime
5+
EncryptedDecimal: Madmatt\EncryptAtRest\FieldType\EncryptedDecimal
6+
EncryptedEnum: Madmatt\EncryptAtRest\FieldType\EncryptedEnum
7+
EncryptedInt: Madmatt\EncryptAtRest\FieldType\EncryptedInt
8+
EncryptedText: Madmatt\EncryptAtRest\FieldType\EncryptedText
9+
EncryptedVarchar: Madmatt\EncryptAtRest\FieldType\EncryptedVarchar

README.md

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,113 @@
11
# silverstripe-encrypt-at-rest
22

3-
This module allows data to be encrypted in the database, but be decrypted when extracted from the database, using a
4-
secret key (hopefully) known only by the web server.
3+
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.
54

6-
*Note:* This does not provide significant protection except in the case of database compromise. It should be used as
7-
part of a layered security strategy. This is because the key is still available on the web server, so if remote code
8-
execution is achieved by an attacker, they will be able to read both the database *and* the encryption key, thereby
9-
decrypting the content.
105

11-
*Note:* This module is not yet ready for real use, it's currently v0.0.1 material.
6+
## Caveats to understand
7+
* 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.
8+
* Encrypting and decrypting data on a field-by-field basis has a performance overhead, which may produce undesirable results in your project.
9+
* 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.
10+
11+
12+
## Requirements
13+
* SilverStripe CMS 4.9
14+
15+
## Installation
16+
Install via Composer:
17+
18+
```
19+
composer require madmatt/silverstripe-encrypt-at-rest
20+
```
21+
22+
Once installed, you need to generate an encryption key that will be used to encrypt all data.
23+
24+
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`.
25+
2. Set this key as the environment variable `ENCRYPT_AT_REST_KEY`.
26+
27+
For development environments you can set this in your `.env` e.g:
28+
29+
```
30+
ENCRYPT_AT_REST_KEY="{generated defuse key}"
31+
```
32+
33+
For more information view SilverStripe [Environment Management](https://docs.silverstripe.org/en/4/getting_started/environment_management/).
1234

1335
## Usage
1436

15-
In your DataObject, use the various field types in this module to have it encrypted. At this point, everything is stored as encrypted text.
37+
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.
38+
39+
For example:
1640

17-
Ensure you create a key to use for encryption/decryption. You can run the following code to generate a valid key:
1841
```php
19-
$key_object = Defuse\Crypto\Key::createNewRandomKey();
20-
$key_string = $key_object->saveToAsciiSafeString();
42+
use Madmatt\EncryptAtRest\FieldType\EncryptedVarchar;
43+
44+
class SecureDataObject extends DataObject {
45+
46+
private static $db = [
47+
'NormalText' => 'Varchar'
48+
'SecureText' => EncryptedVarchar::class
49+
];
50+
51+
}
2152
```
2253

23-
This key can then be set in your `_ss_environment.php` file:
24-
25-
```
26-
define('ENCRYPT_AT_REST_KEY', 'defuse key here');
27-
```
54+
See the `src/FieldType` folder for all field types, or review the below list:
55+
56+
- `Madmatt\EncryptAtRest\FieldType\EncryptedDatetime`
57+
- `Madmatt\EncryptAtRest\FieldType\EncryptedDecimal`
58+
- `Madmatt\EncryptAtRest\FieldType\EncryptedEnum`
59+
- `Madmatt\EncryptAtRest\FieldType\EncryptedInt`
60+
- `Madmatt\EncryptAtRest\FieldType\EncryptedText`
61+
- `Madmatt\EncryptAtRest\FieldType\EncryptedVarchar`
62+
63+
**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.
2864

29-
## TODO
65+
**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`).
3066

31-
- Make sure $this->value is _always_ the unencrypted value
32-
- Clean up
33-
- EncryptedEnum needs validation
34-
- Extended testing
35-
- Test if the value is actually encrypted, before trying to decrypt
36-
- Merge in and release the SS4 upgrade
67+
Data will be automatically encrypted when values are written to the database, and decrypted whenever that data is read back from the database.
68+
69+
To use decrypted values, you just use the value like you would in any other context. For example:
70+
71+
```php
72+
// Via DataObject::get()
73+
$obj = SecureDataObject::get()->first()->SecureText; // Returns the decrypted string from the field
74+
75+
// Getting the DB field
76+
$obj = SecureDataObject::get()->first();
77+
$field = $obj->dbObject('SecureText'); // Returns an EncryptedVarchar object
78+
$uppercase = $field->UpperCase(); // Method on DBString, returns a string
79+
```
80+
81+
Usage within Silverstripe templates is also straightforward:
82+
83+
```html
84+
<% loop $SecureDataObjects %>
85+
<p>$SecureText.UpperCase</p>
86+
<% end_loop %>
87+
```
88+
89+
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.
90+
91+
### Encrypting and decrypting arbitrary text strings and files without using the ORM
92+
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.
93+
94+
```php
95+
use Madmatt\EncryptAtRest\AtRestCryptoService;
96+
use SilverStripe\Assets\File;
97+
use SilverStripe\Core\Injector\Injector;
98+
99+
$text = 'This is an unencrypted string!';
100+
101+
/** @var AtRestCryptoService $service */
102+
$service = Injector::inst()->get(AtRestCryptoService::class);
103+
$encryptedText = $service->encrypt($text); // Returns encrypted string starting with `def`
104+
$unencryptedText = $service->decrypt($encryptedText); // Returns 'This is an unencrypted string!'
105+
106+
$file = File::get()->byID(1); // Presume this is a file that contains the text string above
107+
108+
// 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
109+
$encryptedFile = $service->encryptFile($file);
110+
111+
// 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
112+
$decryptedFile = $service->decryptFile($encryptedFile);
113+
```

_config.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
11
<?php
2-
// SilverStripe 3.7 and PHP 7.2 compatibility
3-
if (!class_exists('SS_Object')) {
4-
class_alias('Object', 'SS_Object');
5-
}

_config/config.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
DataObject:
1+
# Add extension to all DataObject sub-classes
2+
SilverStripe\ORM\DataObject:
23
extensions:
3-
- EncryptDataObjectFieldsExtension
4-
EncryptDataObjectFieldsExtension:
5-
EncryptedDataObjects:
6-
- EncryptDataObject
7-
File:
4+
- Madmatt\EncryptAtRest\Extension\DecryptDataObjectFieldsExtension
5+
6+
# Add .enc files as allowed extensions to provide a way to store files in encrypted format
7+
# (see AtRestCryptoService->encryptFile() and ->decryptFile()
8+
SilverStripe\Assets\File:
89
allowed_extensions:
9-
["enc"]
10+
- enc

code/AtRestCryptoService.php

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

code/extensions/EncryptDataObjectFieldsExtension.php

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

0 commit comments

Comments
 (0)