6

I was wondering if it was possible to hold an encrypted text field within my database and have the ability to decrypt this piece of text based on a salt and authorized password?

For example:

$Salt = $_POST['Salt']; 
$Password = $Query_Results['Password'];

if ($salt == $Stored_Salt AND $Authorized_Password == $Password){
  //Perform a decryption of the stored results
   echo $Decrypted_TextField;
}

I am in the process of creating a completely encrypted/encoded database. Nothing will be plain text apart from the integer fields for the identifiers. Everything else will be encrypted/encoded.. Many will use a one way encryption, but some fields need to house a two way encryption type.

I cannot house non-encrypted text in the database. Everything needs to be encrypted before storing. But the approach to this is an unknown process to me. So I was wondering if I could get some assistance on where to start

Sophie Mackeral
  • 897
  • 4
  • 12
  • 21
  • Look up the symmetric encryption algorithm AES – Patashu Jun 06 '13 at 01:13
  • @Patashu The documentation for the decryption for the AES functions on php.net: http://www.php.net/manual/en/function.mcrypt-decrypt.php is undocumented on how to actually implement these functions. Could you shed some light if I provide a sucessful encryption function? – Sophie Mackeral Jun 06 '13 at 01:17
  • You should read up on the literature of how symmetrical encryption functions and AES work. Then all of the parameters of that function will be obvious. – Patashu Jun 06 '13 at 01:27
  • @Patashu Got any nice documentation/ebooks which are aimed at beginners for AES? – Sophie Mackeral Jun 06 '13 at 01:29
  • The first comment on this page may help: http://php.net/manual/en/book.mcrypt.php and also this topic http://stackoverflow.com/questions/10916284/how-to-encrypt-decrypt-data-in-php – edwardmp Jun 06 '13 at 01:32
  • pgp has a symetric encryption mode as well – Orangepill Jun 06 '13 at 02:53
  • Just to be a pedant, there's no such thing as one-way encryption. There's symmetric and asymmetric encryption and cryptographically secure hashing. You absolutely do _not_ want to do this yourself, even were you familiar with all the concepts, it's just too easy to make a mistake (and it's almost impossible to know when you have - it just looks like random strings, right?). If you can, use encrpytion provided by the database and just provide the keys in as secure a manner as possible. – Basic Jul 08 '13 at 13:27

2 Answers2

10

It sounds like you might want to do some more background on crypto generally, and DB encryption options specifically (you don't mention the data store, but there are MySQL and Postgres options for full encryption). In general, it is almost always a bad idea to "roll your own", and unfortunately, between mcrypt() and openssl_*() functions, there are frankly too many options presented to a novice (such as presenting EBC and CBC as equally valid options). While this thread: https://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own is mainly talking about the futility of creating "novel" cryptographic primitives, the principle also applies to naive attempts at implementing application- and database-level encryption as well.

As a practical matter, the most challenging thing you will likely have to deal with is the issue of password/key management. The code below puts all of the responsibility on the client (sender) - and unless you save the submitted password (which sort of defeats the whole purpose), if the user forgets or is otherwise unable to supply their password in the future, encrypted data in the database will be unrecoverable. (And, yes, if you really want to go down the Yak Shaving path, there are options for multi-key envelope encryption).

If you store the key/password server-side you are, at best, only putting a small road bump in the path of an adversary: if she can manage to read your key file, she can retrieve the data. But worst, by saving the password locally, you are giving a false sense of security to your users, and if this is financial, health, or otherwise protected information, you and your organization take on that burden of that liability.

Finally, there is a mature library here: http://phpseclib.sourceforge.net/crypt/examples.html but in my opinion, it offers too many options for a novice user (see, for example, the default EBC mode in the code generator). For password hashing, take a close look at the phpPass library here: http://www.openwall.com/phpass/.

All that said, here is a working start for simple two-way, reasonably strong encryption, with randomly generated initialization vectors and salts, and a 256-bit AES symmetric (e.g., non-public key) cipher. Tested on OSX Lion & CentOS/RedHat 6.

Good luck!

//$message = escapeshellarg( $_POST['message'] );
$message = 'This is my very secret data SSN# 009-68-1234';  

// Set to some reasonable limit for DB.
// Make sure to size DB column +60 chars 
$max_msg_size = 1000;
$message = substr($message, 0, $max_msg_size);

// User's password (swap for actual form post)
//$password = escapeshellarg( $_POST['password'] );
$password = 'opensesame';

// Salt to add entropy to users' supplied passwords
// Make sure to add complexity/length requirements to users passwords!
// Note: This does not need to be kept secret
$salt = sha1(mt_rand());

// Initialization Vector, randomly generated and saved each time
// Note: This does not need to be kept secret
$iv = substr(sha1(mt_rand()), 0, 16);

echo "\n Password: $password \n Message: $message \n Salt: $salt \n IV: $iv\n";

$encrypted = openssl_encrypt(
  "$message", 'aes-256-cbc', "$salt:$password", null, $iv
);

$msg_bundle = "$salt:$iv:$encrypted";
echo " Encrypted bundle = $msg_bundle \n\n ";

// Save it... (make sure to use bind variables/prepared statements!)
/* db_write( "insert into sensitive_table encrypted_msg values (:msg_bundle)",
    $msg_bundle ); */

Now retrieve it:

//  Retrieve from DB... 

//$password = escapeshellarg( $_POST['password'] );
$password = 'opensesame';

// Swap with actual db retrieval code here
//$saved_bundle = db_read( "select encrypted_msg from sensitive_table" );
$saved_bundle = $msg_bundle;

// Parse iv and encrypted string segments
$components = explode( ':', $saved_bundle );;

var_dump($components);

$salt          = $components[0];
$iv            = $components[1];
$encrypted_msg = $components[2];

$decrypted_msg = openssl_decrypt(
  "$encrypted_msg", 'aes-256-cbc', "$salt:$password", null, $iv
);

if ( $decrypted_msg === false ) {
  die("Unable to decrypt message! (check password) \n");
}

$msg = substr( $decrypted_msg, 41 );
echo "\n Decrypted message: $decrypted_msg \n";

Sample output:

 Password: opensesame 
 Message: This is my very secret data SSN# 009-68-1234 

 Salt: 3f12ce187d5c5bcc3b0d5acf1e76fad8b684ff37 
 IV: 00c1d3b4c6a6f4c3 

 Encrypted bundle = 3f12ce187d5c5bcc3b0d5acf1e76fad8b684ff37:00c1d3b4c6a6f4c3:KB6k+GlM+0EHbETUgEe8Lck0nF5qBz+51wc5LtmS4XMOm0Pfyyr2PIXMVEyzs/41 

 array(3) {
  [0]=>
  string(40) "3f12ce187d5c5bcc3b0d5acf1e76fad8b684ff37"
  [1]=>
  string(16) "00c1d3b4c6a6f4c3"
  [2]=>
  string(64) "KB6k+GlM+0EHbETUgEe8Lck0nF5qBz+51wc5LtmS4XMOm0Pfyyr2PIXMVEyzs/41"
}

 Decrypted message: This is my very secret data SSN# 009-68-1234 
Community
  • 1
  • 1
PapaK
  • 471
  • 5
  • 7
  • careful openssl method has changed, the 4th parameter is typed as an integer, null is not accepted. Sha1 is also wait for an int now, and mt_rand return string – Greco Jonathan Oct 24 '18 at 12:20
2

Not a complete answer but an expansion of how salting works that was too long for a comment..

The salt shouldn't be treated as a string to be compared, it should be a part of the password that the end user doesn't have to type but is unique to that user. It's used to prevent a single compromised password breaching multiple accounts.

Eg let's say we've got a really simple system on which Bob has the password ABCDEF.

Passing ABCDEF through our hashing algorithm results in (say) ED6522687

If an attacker gets access to the password list, they can only see the stored hash.

Of course, if Jane also uses the same password, her hash is going to be ED6522687 too - which means if you break into either account (through brute-force, social engineering, etc) you're going to get access to both accounts as you can see their hashes match.

Salting is where something is done to the password before hashing that's unique to each user and repeatable. Salt should be predictable so let's say Bob and Jane's salts are random numbers.

Now if you hash the password for bob ABCDEF123, you get a different hash to Jane's ABCDEF456.

Note that this isn't a complete explanation. Some other things to consider:

  • There's no such thing as a random number in this context, only cryptographically secure random numbers - and how random they are is complex, related to entropy and other fun stuff.
  • How quickly the hash is computed is a major factor with regards to hampering brute-forcing - hash algorithms like bcrypt are designed to be computationally expensive. unlike (say) SHA2
  • There's no reason for the user to submit (or even know) their salt.

Another observation which isn't usually emphasised enough... You should never trust anything you read on the internet about a topic like this - there are too many people who have incomplete understanding (I consider myself among them). So take this as reasons not to do it yourself, not a guide on how to do it.

Basic
  • 26,321
  • 24
  • 115
  • 201