CFML encryption Best Practices

I am adding hashing and encryption to ofusticate the data passed in query
strings and forms. My goal isn’t deep security but simply to make is as
difficult as possible to figure what passeds data values are and represent.
As a i add a few new features, it would be possible to scrape through the
site figure out the relationship between different entities and all but
recreate the underlying tables.I have aready had a couple of situations of
other sites scraping data from the site so I know it is an issue.

I have looks at the handy guides at CFDocs to understanfd the mechanics of
using the tags and functions.

http://cfdocs.org/security-encryption
http://cfdocs.org/security-obfuscation

But I am wondering about architectural questions about where to store the
secret keys, using multple keys, etc.

Any advice or references to online resources are appreciated.

Thanks,
Magnus

If you want detailed industrial strength answers to encryption questions
then look here:

  • The VISA / Credit Card industry standards, while most likely overkill for
    your situation it is good reading if you want to understand the problems
    associated with encryption and key management.

If you just want to encrypt some data points on the URL then where you
store the keys should be somewhere outside of the webroot. You don’t want
your keys accessible from the web. You will need to make it so you can
change keys semi-regularly. You can decide the timeframe for switching
keys based on your needs.

For one job I did based on VISA standards I created a in house encryption
server. You could only access that box internally (secured at the network
layers). That server created the keys and handled all of the key storage.
All of the other servers just asked for today’s key from that box.

Andrew PenhorwoodOn Monday, September 5, 2016 at 6:30:51 PM UTC-4, Magnus Thyvold wrote:

I am adding hashing and encryption to ofusticate the data passed in query
strings and forms. My goal isn’t deep security but simply to make is as
difficult as possible to figure what passeds data values are and represent.
As a i add a few new features, it would be possible to scrape through the
site figure out the relationship between different entities and all but
recreate the underlying tables.I have aready had a couple of situations of
other sites scraping data from the site so I know it is an issue.

I have looks at the handy guides at CFDocs to understanfd the mechanics of
using the tags and functions.

http://cfdocs.org/security-encryption
http://cfdocs.org/security-obfuscation

But I am wondering about architectural questions about where to store the
secret keys, using multple keys, etc.

Any advice or references to online resources are appreciated.

Thanks,
Magnus

Ooops… typo’d one line in generateKeyRing():

keyStruct[‘alg’] = variables.algorithm & ‘/CBC/PKCS5Padding’;

should be:

keyStruct[‘alg’] = algorithm & ‘/CBC/PKCS5Padding’;

Hey Magnus,

As Andrew noted key management depends on your security requirements,
but for most of my apps that are not specifically handling PCI compliance
data I simply store the key data outside of the webroot and accessible only
to the user the application server (ACF, Lucee, etc.) runs as, and an
admin/root account. I encrypt the file using a ‘master key’ generated from
a hash of the application name, store the encrypted data in binary format
and hash the filename for further obscurity. I then load the keys in
onApplicationStart() into my security service and have a go at it. Relevant
code snippet below:

// load and initialize the SecurityService with keyring path and master key
application.securityService = new model.services.SecurityService(
keyRingPath = ‘/opt/secure/keyrings/’ & hash( this.name, ‘MD5’, ‘UTF-8’,
108 ) & ‘.bin’,
masterKey = mid( lCase( hash( this.name, ‘SHA-512’, ‘UTF-8’, 512 ) ), 32,
22 ) & ‘==’
);

// use the SecurityService to read the encryption keys from disk
application.keyRing = application.securityService.readKeyRingFromDisk();

// check if the keyring is a valid array of keys
if( !arrayLen( application.keyRing ) ) {
// it isn’t, try
try {
// to generate a new keyring file (for new application launch only)
application.keyRing = application.securityService.generateKeyRing();
// catch any errors
} catch ( any e ) {
// and dump the error
writeDump( e );
abort;
}
}

// (re)initialize the SecurityService with the keyring
application.securityService = application.securityService.init(
encryptionKey1 = application.keyRing[1].key,
encryptionAlgorithm1 = application.keyRing[1].alg,
encryptionEncoding1 = application.keyRing[1].enc,
encryptionKey2 = application.keyRing[2].key,
encryptionAlgorithm2 = application.keyRing[2].alg,
encryptionEncoding2 = application.keyRing[2].enc,
encryptionKey3 = application.keyRing[3].key,
encryptionAlgorithm3 = application.keyRing[3].alg,
encryptionEncoding3 = application.keyRing[3].enc,
hmacKey = generateSecretKey( ‘HMACSHA512’ ),
hmacAlgorithm = ‘HMACSHA512’,
hmacEncoding = ‘UTF-8’
);

// clear the keyring from the application scope
structDelete( application, ‘keyRing’ );

I then have three relevant functions within my SecurityService.cfc that
handle reading, writing and generating keys, as follows:

    /**
  • @displayname readKeyRingFromDisk
  • @description I read the keyRing from disk
  • @return array
    */
    public array function readKeyRingFromDisk() {

if( !fileExists( variables.keyRingPath ) ) {

return arrayNew(1);

}

var binaryJson = fileReadBinary( variables.keyRingPath );

var encJson = charsetEncode( binaryJson, “utf-8” );

var jsonArray = dataDec( encJson, ‘master’ );

return deserializeJSON( jsonArray );

}

/**

  • @displayname generateKeyRing
  • @description I generate a new random keyring and save it to disk
  • @return array
    */
    public array function generateKeyRing( numeric keyLength = 128 ) {

var keyRing = arrayNew(1);
var keyStruct = {};
var algorithm = ‘’;

for( i=1; i<=3; i++ ) {
algorithm = ( ( randRange( 0, 1 ) ) ? ‘AES’ : ‘BLOWFISH’ );

keyStruct = {};
keyStruct[‘key’] = generateSecretKey( algorithm, arguments.keyLength );
keyStruct[‘alg’] = variables.algorithm & ‘/CBC/PKCS5Padding’;
keyStruct[‘enc’] = ‘HEX’;

keyRing[i] = keyStruct;
}

saveKeyRingToDisk( keyRing );

return keyRing;

}

/**

  • @displayname saveKeyRingToDisk
  • @description I save the keyRing to disk
  • @param keyRing {Array} I am the Key Ring array
  • @return void
    */
    private void function saveKeyRingToDisk( required array keyRing ) {

var jsonArray = serializeJSON( arguments.keyRing );

var encJson = application.securityService.dataEnc( jsonArray, ‘master’ );

var binaryJson = charsetDecode( encJson, “utf-8” );

fileWrite( variables.keyRingPath, binaryJson );

}

My encryption (dataEnc()) and decryption (dataDec()) functions in the same
SecurityService.cfc take an optional argument (e.g. url, form, cookie, db
or master) and use the appropriately defined keys passed from the keyring
and/or initialization - in this case the ‘master’ key specified in the
Application.cfc is used and runs a multi-pass encryption/decryption on the
keyring file.

Though I don’t have these functions in the SecurityService.cfc I have
published online
at https://github.com/ddspringle/framework-one-secure-auth/blob/master/model/services/SecurityService.cfc,
they are easily added and variables/logic adjusted to make each application
unique in how it handles loading, generating and writing the keyring file.

There are, of course, other ways to handle this whole process, so use your
best judgement on how to generate and secure the keys you want to use. I’ll
have to write all this up as an additional security doc on cfdocs one day
:wink: Hope it helps!

– Denny