Decrypt sensitive data in database on access - sql

I am working with a database that contains sensitive information (SSN, credit card details, etc.). I am looking for a method to secure the data.
I would like to encrypt the data when I bring it in from the outside source but also decrypt it when my users access the data via an application that uses the database as its back-end.
I know of the two methods below but I am curious to hear if there is another method I could use.
Examples:
ENCRYPTEDBYPASSPHRASE({PASSWORD}, {FIELD})/DECRYPTBYPASSPHRASE({PASSWORD},{FIELD})
-- This allows me to set a custom password but could be read through the stored procedures.
HASHBYTES('SHA_512', {PASSWORD}+CAST({SALT} AS NVARCHAR(36)))
-- This seems the most secure but I do not know how to decrypt the data from here.
Is ENCRYPTEDBYPASSPHRASE as secure as it gets in this case?

Encryption turns data into a series of unreadable characters, that aren't of a fixed length.
A hash is a string or number generated from a string of text. The resulting string or number is a fixed length.
The key difference between encryption and hashing is that encrypted strings can be reversed back into their original decrypted form if you have the right key and hashing is good to store passwords.
1) If you want to use hashing for security of your data then there are many types of algorithms but SHA and MD4/5 is wildely used algorithms.
For example, as demonstrated below, hashed output with MD5 algorithm produces a 16 bytes long value whereas SHA1 algorithm produces a 20 bytes long value:
SELECT HASHBYTES('MD5', 'Test String') AS Col1, HASHBYTES('MD5', 'Test String') AS Col2 GO
SELECT HASHBYTES('SHA1', 'Test String') AS Col1, HASHBYTES('SHA1', 'Test String') AS Col2 GO
2) and if you want to use Encryption then there are two primary types of encryption, symmetric key encryption and public key encryption.
Example:
To create a symmetric key, we first need to setup our database with a master key and a certificate, which act as protectors of our symmetric key store.
Create a Database Master Key:
CREATE MASTER KEY ENCRYPTION BY PASSWORD = ‘myStrongPassword’
Create a Certificate:
CREATE CERTIFICATE MyCertificateName WITH SUBJECT = 'A label for this certificate'
Create a Symmetric Key:
CREATE SYMMETRIC KEY MySymmetricKeyName WITH IDENTITY_VALUE = 'a fairly secure name', ALGORITHM = AES_256, KEY_SOURCE = 'a very secure strong password or phrase' ENCRYPTION BY CERTIFICATE MyCertificateName;
Encrypting and Decrypting Data:
Open the Key:
Before you can start encrypting or decrypting data, you must first initialize the key. This is done with the following piece of code.
OPEN SYMMETRIC KEY MySymmetricKeyName
DECRYPTION BY CERTIFICATE MyCertificateName
Encrypting data
You can encrypt data by using the EncryptByKey function, like so:
DECLARE #Result varbinary(256)
SET #Result = EncryptByKey(Key_GUID('MySymmetricKeyName'), #ValueToEncrypt)
Note that the result of the above encryption is of type varbinary(256), and if you would like to store the value in a column to use this type.
Decrypting Data:
You can decrypt data by using the DecryptByKey function, like so:
DECLARE #Result varchar(max)
SET #Result = DecryptByKey(#ValueToDecrypt)

You can not decrypt a hash (edit: Unless it has been compromised as a whole), that's the point of a hash. If you were to only compare a hashed value against your hash (such would be the case for logging into your app, for example - never store passwords in cleartext) this would be an easy app-side job.
I found this very handy article over on security:
https://security.stackexchange.com/questions/16939/is-it-generally-a-bad-idea-to-encrypt-database-fields
that should help you on your way.

In re: 2nd option: A hash is a one way operation. It generally doesn't get done with the intent to unhash it. (Consider a password. It gets hashed and a 256 byte string produced. Rather than decode the hash produced and comparing it to the naked user input, the user's input is hashed and the two hashes are compared.)
I think you're looking for a data access layer written with knowledge of your encryption method. That's something you'll have to create on your own. (That is use stored procedures, functions, and views to read data from it's encrypted at rest state, decrypt it, and return it to the caller. Deny access to the users to the underlying tables. Create a stored procedure GetAccountNumber, etc. [You'll note that in these cases the primary key has to be unencrypted so you can find it. Other data will necessarily need to be stored in plain text so you can properly index and search it. You don't want to find yourself in a situation where you have to cycle through every record in a table, decrypting each record, to find a matching address.])
There is transparent data encryption (TDE) available in Enterprise editions of Microsoft SQL Server. With TDE the data is encrypted at rest and anyone who can access the database will have access to the unencrypted data. This is also true for the data access layer method. At some point the secret gets exposed. Where that occurs is up to the design. TDE can be configured in many different ways.
If for PCI requirements I'd go the route of TDE. Don't have Enterprise? Pony up for the upgrade.

Related

Initialization vector - best practices (symmetric cryptography)

I would like to ask about best practices regarding a usage of an initialization vector (IV) and a key for symmetric cryptography algorithms.
I want to accept messages from a client, encrypt them and store in a backend. This will be done over a time, and there will be requests coming at a later time for pooling out the messages and return them in a readable form.
According what I know, the key can be the same during the encryption of multiple separate messages. The IV should change with every new encryption. This however, will cause problems, because every message will need a different IV for de-cryption at a later time.
I’d like to know if this is the best way of doing it. Is there any way to avoid storing IV with every message, which would simplify entire process of working with encryption/decryption?
IV selection is a bit complicated because the exact requirements depend on the mode of operation. There are some general rules, however:
You can't go wrong¹ with a random IV, except when using shorter IVs in modes that allow this.
Never use the same IV with the same key.
If you only ever encrypt a single message with a given key, the choice of IV doesn't matter².
Choose the IV independently of the data to encrypt.
Never use ECB.
Of the most common specific modes of operation:
CBC requires the IV to be generated uniformly at random. Do not use a counter as IV for CBC. Furthermore, if you're encrypting some data that contains parts that you receive from a third party, don't reveal the IV until you've fully received the data, .
CTR uses the IV as the initial value of a counter which is incremented for every block, not for every message, and the counter value needs to be unique for every block. A block is 16 bytes for all modern symmetric ciphers (including AES, regardless of the key size). So for CTR, if you encrypt a 3-block message (33 to 48 bytes) with 0 as the IV, the next message must start with IV=3 (or larger), not IV=1.
Modern modes such as Chacha20, GCM, CCM, SIV, etc. use a nonce as their IV. When a mode is described as using a nonce rather than an IV, this means that the only requirement is that the IV is never reused with the same key. It doesn't have to be random.
When encrypting data in a database, it is in general not safe to use the row ID (or a value derived from it) as IV. Using the row ID is safe only if the row is never updated or removed, because otherwise the second time data is stored using the same ID, it would repeat the IV. An adversary who sees two different messages encrypted with the same key and IV may well be able to decrypt both messages (the details depend on the mode and on how much the attacker can guess about the message content; note that even weak guesses such as “it's printable UTF-8” may suffice).
Unless you have a very good reason to do otherwise (just saving a few bytes per row does not count as a very good reason) and a cryptographer has reviewed the specific way in which you are storing and retrieving the data:
Use an authenticated encryption mode such as GCM, CCM, SIV or Chacha20+Poly1305.
If you can store a counter somewhere and make sure that it's never reset as long as you keep using the same encryption key, then each time you encrypt a message:
Increment the counter.
Use the new value of the counter as the nonce for the authenticated encryption.
The reason to increment the counter first is that if the process is interrupted, it will lead to a skipped counter value, which is not a problem. If step 2 was done without step 1, it would lead to repeating a nonce, which is bad. With this scheme, you can shave a few bytes off the nonce length if the mode allows it, as long as the length is large enough for the number of messages that you'll ever encrypt.
If you don't have such a counter, then use the maximum nonce length and generate a random counter. The reason to use the maximum nonce length is that due to the birthday paradox, a random n-bit nonce is expected to repeat when the number of messages approaches 2n/2.
In either case, you need to store the nonce in the row.
¹ Assuming that everything is implemented correctly, e.g. random values need to be generated with a random generator that is appropriate for cryptography.
² As long as it isn't chosen in a way that depends on the key.

TSQL Text Field Encryption

very new to TSQL encryption.. could someone please tell me what data type would be best to use to store encrypted text data (e.g. Encrypted storage of secret passwords, password hints, etc.)
Assuming you are going to use ENCRYPTBYKEY to encrypt your data, you can see that its return value is VARBINARY:
varbinary with a maximum size of 8,000 bytes.
Returns NULL if the key
is not open, if the key does not exist, or if the key is a deprecated
RC4 key and the database is not in compatibility level 110 or higher.
So, it is clear that you need to use VARBINARY to store the encrypted values. The length of the VARBINARY column will vary based on the length of the data you are encrypting and the type of algorithm being used.
Some people generate the maximum possible value that can be encrypted to check the maximum possible encryption value length. For example, using the ALGORITHM = AES_256 and encrypting NVARCHAR(128) field:
CREATE SYMMETRIC KEY StackOverflow WITH ALGORITHM = AES_256
ENCRYPTION BY PASSWORD = 'pass123_#';
GO
OPEN SYMMETRIC KEY StackOverflow
DECRYPTION BY PASSWORD = 'pass123_#'
go
DECLARE #MaximumColumnValue NVARCHAR(128);
DECLARE #EncrpytionValue VARBINARY(8000);
SET #MaximumColumnValue = replicate (N'A', 128)
PRINT DATALENGTH(#MaximumColumnValue);
SET #EncrpytionValue = EncryptByKey( key_guid('StackOverflow'), #MaximumColumnValue )
PRINT DATALENGTH(#EncrpytionValue);
DROP SYMMETRIC KEY StackOverflow;
GO

Identifying password encryption in database

Long story short I want to be able to read passwords stored in our database to be able to query weak passwords for our employees as there are currently no restrictions. What I've been doing in the past is changing it from the front end to see what it looks like on the backend. For instance this is what "password" looks like on the back end JXm7CJyoCBnURIrneTtflA== .
I'm not sure if this is possible, or what type of encryption is used. Any help would be great!!
Thanks
This particular field is Base64 encoded, and has 16 bytes if you decode it (Convert.FromBase64String). This smells like a MD5 (hash algorithm) - especially if other fields have also 16 bytes when decoded. There is no way in hell how to decrypt hash (there are some options like rainbow tables but you can't be 100% sure). Algorithm works like this: you hash password in db, you hash whatever user puts in as password when he logs in and hash it as well - if hash matches user has entered correct password.

SHA1 Decryption in VB.Net

Is it possible to decrypt a SHA1 string in VB.Net, knowing the key?
I have seen "decryption" of credentials before, however - in Java: http://pastebin.com/P0LuN00P
The entire point of SHA1 is to make this impossible.
However, SHA1 has weaknesses which make this less impossible.
You should use SHA512 to make it more impossible.
You might be looking for Rijndael, a (good) symmetric encryption algorithm.
I think you got SHA1 wrong.
SHA1 is not an encryption algorithm, it is a hash function.
A hash function is a function taking some unconditionally long argument string and transform that string to a much smaller string, called the hash. It is very hard to get from a hash to the string used to generate the hash. Actually, since the input are arbitrarily long, there are multiple such inputs that give the same hash. Two such inputs are called collisions. Therefore you really cant "decrypt" a hash, you can find a input which gives the same hash though.
Commonly hashing functions are used to hash a user password, store it in a database on the server. When the server is supplied a password from a user, the server checks to see if the password is correct by checking that hashing the password gives the same result as stored in the database.
If a malicious user grabs what is stored in the database, he is unable to know the actual password since it is very hard to go from hash to the string used to generate the hash.
SHA1 isn't encrypted, it's hashed. So no, it's not possible to decrypt it. You might try a Rainbow Tables: http://www.freerainbowtables.com/

How do I convert password hashing from MD5 to SHA?

I've got an old application that has user passwords stored in the database with an MD5 hash. I'd like to replace this with something in the SHA-2 family.
I've thought of two possible ways to accomplish this, but both seem rather clunky.
1) Add a boolean "flag" field. The first time the user authenticates after this, replace the MD5 password hash with the SHA password hash, and set the flag. I can then check the flag to see whether the password hash has been converted.
2) Add a second password field to store the SHA hash. The first time the user authenticates after this, hash the password with SHA and store it in the new field (probably delete their MD5 hash at the same time). Then I can check whether the SHA field has a value; this essentially becomes my flag.
In either case, the MD5 authentication would have to remain in place for some time for any users who log in infrequently. And any users who are no longer active will never be switched to SHA.
Is there a better way to do this?
Essentially the same, but maybe more elegant than adding extra fields: In the default authentication framwork in Django, the password hashes are stored as strings constructed like this:
hashtype$salt$hash
Hashtype is either sha1 or md5, salt is a random string used to salt the raw password and at last comes the hash itself. Example value:
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
You can convert all your MD5 Strings to SHA1 by rehashing them in your DB if you create your future passwords by first MD5ing them. Checking the passwords requires MD5ing them first also, but i dont think thats a big hit.
php-code (login):
prev:
$login = (md5($password) == $storedMd5PasswordHash);
after:
$login = (sha1(md5($password)) == $storedSha1PasswordHash);
Works also with salting, got the initial idea from here.
I think you've already got the best possibilities. I like #1 more than #2, since there's no use for the md5 once the sha is set.
There's no way to reverse the MD5, so you have to wait for the user to authenticate again to create a new hash.
No - basically you'll have to keep the MD5 in place until all the users you care about have been converted. That's just the nature of hashing - you don't have enough information to perform the conversion again.
Another option in-keeping with the others would be to make the password field effectively self-describing, e.g.
MD5:(md5 hash)
SHA:(sha hash)
You could then easily detect which algorithm to use for comparison, and avoid having two fields. Again, you'd overwrite the MD5 with SHA as you went along.
You'd want to do an initial update to make all current passwords declare themselves as MD5.
Your second suggestion sounds the best to me. That way frequent users will have a more secure experience in the future.
The first effectively "quirks-mode"'s your codebase and only makes sure that new users have the better SHA experience.
If the MD5's aren't salted you can always use a decryption site/rainbow tables such as: http://passcracking.com/index.php to get the passwords. Probably easier to just use the re-encode method though.
Yes you should know the real password first before you convert it into sha-1..
If you want to find the real password from md5 encrypted string, you can try md5pass.com