Security of Seeded PRNGs - javascript

I wanted to create an application where a login password could be the seed to a pseudo random number generator to recreate an encryption key. This encryption key would then be used to encrypt all data sent to and from the application's database, making user data out of reach of even the host.
Can this use of a PRNG even be cryptographically secure? If so, what PRNG algorithms are best for this application? Is important to enforce a decent length minimum password length? Any other concerns with this setup?

What you need is a key derivation function such as PBKDF2 (Password Based Key Derivation Function 2) which is designed to do exactly what you need.
You pass the password, a random seed from a CSPRNG and a repetition count. The random seed does not need to be secret and can be saved with the encrypted data. The count should be chosen such that the derivation takes about 100ms.

Related

AES encryption option for offline usage

My objective is to perform data encryption/decryption locally for some data such as personal information and so on, instead of password, within a mobile app. Stumbled upon this library, and now considering two options which I can have
Option 1
Using user's password as secret passphrase, instead of hardcoding a passphrase, encryption and decryption key are "customized"
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
​
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
Option 2 Generating a key based on user's password such as below
const CryptoJS = require("crypto-js");
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.PBKDF2("password", salt, {
keySize: 512 / 32,
iterations: 10000,
});
var encrypted = CryptoJS.AES.encrypt("Message", key.toString());
var decrypted = CryptoJS.AES.decrypt(encrypted, key.toString());
console.log(decrypted.toString(CryptoJS.enc.Utf8));
Given my use case, I'm wondering if there is any advantages of one option over another?
Neither of these two options can be recommended:
The first option should definitely not be used unless compatibility reasons enforce this.
The main reason for this is that the built-in key derivation applies the deprecated key derivation function EVP_BytesToKey(), which derives a 32 bytes key (AES-256) and a 16 bytes IV.
EVP_BytesToKey() and the pararmeters used for key derivation (MD5, iteration count of 1) are considered insecure today, s. e.g. here.
In addition, as already mentioned in the comment, EVP_BytesToKey() is a proprietary and non-standard implementation of OpenSSL, and is therefore not available on many platforms, which can be a problem in a cross-platform architecture.
The clearly more secure alternative is to apply a standardized key derivation function such as PBKDF2 to derive a key that is then used for encryption/decryption.
The second option uses PBKDF2 as key derivation function though, but performs a hexadecimal encoding of the key with key.toString(), i.e. converts the key to a string, with far-reaching consequences:
CryptoJS just uses the data type to interpret the second parameter as password or key. In case of a string the data is interpreted as password and the key derivation with EVP_BytesTokey() is performed (as for the first option), in case of a WordArray the data is interpreted as key which is directly applied.
The current implementation passes the key as string, i.e. the key derived with PBKDF2 is interpreted as password and the built-in key derivation with EVP_BytesTokey() is unnecessarily performed in addition to the key derivation with PBKDF2.
The correct approach:
Change the second option so that the key is passed as WordArray, i.e. key instead of key.toString().
This change has the consequence that salt and IV must be handled explicitly (in contrast to the built-in key derivation with EVP_BytesToKey(), where this happens under the hood):
The current code already generates a random salt, which is perfectly correct. Similarly, a random IV must be generated (CryptoJS uses CBC by default) and passed (s. here).
As an alternative to explicit generation with random(), the IV can be derived together with the key via PBKDF2.
Since salt and IV are needed for decryption and are not secret, both are usually concatenated with the ciphertext: salt|IV|ciphertext (if the IV is derived together with the key, of course only the salt needs to be concatenated). On the decryption side, the portions can be separated based on the known lengths of salt and IV.
Regarding the key size mentioned in the comment: All AES variants are considered secure today, even the smallest key size AES-128.
Whether AES-128, AES-192 or AES-256 should be used depends on the respective requirements. If the highest possible security is needed e.g. with regard to future quantum computers, AES-256 is probably the better choice. However, this requirement may not always exist. See e.g. also the post Why most people use 256 bit encryption instead of 128 bit?.
You should never hard code a passphrase, it turns cryptography into encoding.
The use of hashing instead of standard human readable text stings is preferable, especially as it adds what i assume are iteration hashing which increase workload against brute force attacks. 1000 iterations adds quite some time, 10k even more, but everything that consume CPU cycles need to be balanced against performance.
Also make sure that any encoding you use (like Utf8) when reading/writing data is explicitly set so you don't mix and match types, that can cause some problems.

Encrypting and decrypting data on client side using cryptojs

I am working on chrome extension to save some user data to local storage. I encode data with crypto-js.
However I am facing one minor issue/dilemma - if I encode user data using secret key abc123 I get something like this 2FsdGVkX19v0LNG0FKFv1SxAQj4AqdNvWWMGyi9yVI=. However if I decrypt it using another secret key like abc I get empty string. Only the correct secret key return non empty string.
So my question how do I need to encode data, if decoding with wrong key it would still return some string? Is there some configuration for this or different encoding?
If I am a hacker and I am using brute force to crack data it looks pretty obvious, what secret key user is using to encode data.
Fidller to fiddle with configuration.
Since you're using Crypto.js you're limited to popular and battle-tested algorithms. Let's say that you're using AES-256-CBC with a key derived from a password ("abc123").
If you encrypt something that a human uses then it likely has low entropy and therefore some structure to it. Think about some JSON string that you're encrypting. If the attacker tries to decrypt the ciphertext with some random key they might get random bytes. If they know they have to get JSON back, they have an oracle whether the decryption worked. They try again with a different key and get different random bytes. They repeat this until they find a plaintext that has a valid JSON structure. Even when they don't know that it is JSON, they might utilize statistical methods in order to deduce whether they got the right key.
You might need to use gzip before encryption in order to make that deduction harder but then the attacker might just incorporate an ungzip procedure in their bruteforcer and just do the statistical analysis.
AES is a block cipher where changing a single bit in the key changes roughly half the bits in the ciphertext with a constant plaintext. That means the attacker will not be able to see that they are getting closer to the correct key. They will have to try all of them.
The usual way of making it harder for an attacker is to increase the work factor. Crypto.js uses by default EVP_bytestokey with a single iteration of a MD5 hash of the password in order to produce the key. This operation is quite fast. You could change that to something like PBKDF2 with a million iterations but today's computers are so fast that this doesn't solve your problem...
The main issue is that your password is too short. "abc123" is a password that can be bruteforced in milliseconds when using Crypto.js defaults (1xMD5) and maybe minutes when using PBKDF2 with a million iterations. Adding a single character to a password multiplies the bruteforce effort by at least 50 (depending on class: upper, lower, digits, special).
Now you can calculate how long your password should be in order interfere with an attacker that corresponds to your risk appetite.
Note that just having encryption doesn't solve all your problems. Usually, the threat still exists.
You only can decrypt your data with the encryption key.
I personally wouldn't encrypt data in a frontend application.
Maybe you should have a look at the documentation:
https://cryptojs.gitbook.io/docs/

Is it safe to use a javascript sha1?

I need to do a SHA1 hash, the only option is to use Javascript (due to my client using a 3rd party product)
An SHA1 is required to pass some information to a payment gateway. so the following info will need to be passed to a SHA1 to convert to a hash (username, password, amount), then passed to a payment gateway
My question is is it safe? ie can't people just view source and see the things I am hashing? is that recommended? is there any other way to do it?
thanks
Regardless of the hashing algorithm used, with browser-side Javascript, your users will always be able to view the page source and see the things you are hashing.
If you're hashing on the server-side, then the source text is not revealed to the browser. It's extremely difficult to "reverse" a SHA-1 hash, but not "impossible" - with enough brute-force power, you could at least find a hash collision (another string which produces the same hash).
SHA-1 is considered secure enough for most purposes. However, it is not considered secure against major, well-funded opponents - with a few million dollars one can hire enough cloud computing resources to break a SHA-1 hash relatively quickly, and this will become progressively cheaper as computing power increases.
If passwords are involved, you should look into BCrypt.
No, don't do this!
Password hashing is supposed to make somebody who copies your database unable to use the login information. By hashing on the client side you have exactly the value in the database, that needs to be sent in the request. So an attacker would just disable the hashing and use the database value as the password.
Quick overview of password hashing:
Each user has a password, that your service knows. They use it to login. Imagine you stored those in plain text. That would be bad in case someone copies your database. So you hash each password with an algorithm like md5 or sha1. The hash can not be used to recreate the password, but you can check a password if it's valid. Problem solved?
Not quite. It's simple to create a rainbow table for a hashing algorithm. This way you can quickly look up possible input values for each hash. So you salt. This means that each user has a random string that gets appended to the password before hashing, creating a unique hashing function per user. Perfect solution?
Again not quite. If your database is copied, with enough computing power this can be cracked. So your last step is using iterations. You don't just append the salt and hash, but you take that result, append the salt again and hash again. You do this a couple hundred times. This way it becomes more expensive for an attacker to brute force attacks. This is what bcrypt does. Over time computing power increases, so you should increase the number of iterations.

Crypto function that always decrypts to plain text, even with the incorrect key [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
I'm working on a javascript function to encrypt and store passwords on the browser using a 6 digit PIN. While this could easily be brute forced, the server side code prevents this by locking out the account after 3 incorrect attempts.
The example below using AES, only decrypts to plain text when the pin/key is correct. This allows the attacker to try the 99,9999 combinations and pick out the only plain text result, bypassing the server side restrictions.
Can someone recommend a javascript crypto function/library that always decrypts to plain text, even with the incorrect key?
var encrypted = CryptoJS.AES.encrypt("password:abcdefg", "pin:123456");
$('#1').text(encrypted);
var decryptedCorrect = CryptoJS.AES.decrypt($('#1').text(), "pin:123456")
$('#3').text(decryptedCorrect.toString(CryptoJS.enc.Utf8));
var decryptedInCorrect = CryptoJS.AES.decrypt($('#1').text(), "pin:112233")
$('#4').text(decryptedInCorrect.toString(CryptoJS.enc.Utf8));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<div>
Encrypted text: <span id="1"></span>
</div>
<br />
<div>
Decrypted with correct pin: <span id="3"></span>
</div>
<br />
<div>
Decrypted with incorrect pin: <span id="4"></span>
</div>
(Ideally the above should be some plain text value)
In general, this cannot work.
To prevent brute-force guessing attacks like these, not only would you need every key (or at least a fairly large fraction of the keys) to decrypt the ciphertext into valid plaintext, but you'd somehow have to arrange for every key to decrypt the ciphertext into plausible plaintext, convincing enough to pass for the real plaintext at least on a cursory inspection.
In particular, the "fake" plaintext produced on incorrect encryption would need to at least be syntactically valid, so that your own code will accept it, and it would also need to have the same general structure and the same statistical properties as your real plaintext, so that the attacker cannot just use some regexps or letter frequency analysis to guess which of the plaintexts is most likely the correct one.
Basically, if you were encrypting passwords, your decryption method would have to generate a plausible-looking password for any key. If you were encrypting JSON data, it would have to generate valid JSON. If you were encrypting poems, it would have to generate a poem.
And they'd have to be good poems, because who'd bother encrypting bad poetry?
Obviously, no general-purpose encryption algorithm can do this.
So, how can you achieve something like what you want? Basically, you have two options:
You can increase the length of your keys so that they cannot be practically enumerated. If you're using random decimal numbers (actually chosen randomly, not by the user!), then around 25 to 30 digits should be the minimum safe length.
You can cut down the length a bit by using key stretching. For example, if you hash each key 100,000 times before using the result to decrypt the data, then you can cut down your keyspace size by a factor of 100,000, i.e. from 25 digits to 20.
You can also make your keys easier to remember by encoding them as phrases. For example, you could compile a list of 1000 short, common English words (or use an existing list), and replace each group of three digits in your password with the corresponding word in the list. It's a lot easier for most people to remember, say, a sequence of five random words than a random 25-digit number.
The other option, of course, is to make use of the server-side rate limiting you already have. To do that, you need to ensure that the client has to check the key with the server before it can do anything else with it.
That is, you should store the actual encryption key (which could be, say, a random 128-bit binary string) on the server, and have the server only send it to the client after the client has successfully authenticated with its own "short key".
You should also make sure that neither the short nor the long key can be captured by an eavesdropper, e.g. by using something like SRP for the authentication, or simply by doing the authentication over TLS/SSL.
Neither of these solutions is perfect: the first may require inconveniently long keys, even with key stretching, while the second method won't work if the client is offline, and can fail catastrophically is the server is ever hacked. In general, though, those are pretty much the best you can do.
(There are ways to combine the two methods for extra security, so that the attacker would have to both compromise the server and guess the client's password in order to break the system. But then you'd also have the disadvantages of both methods.)
There is format preserving encryption, which directly answers your question. You can look for a library function, but FPE is not commonplace. Instead you should be decreasing your PIN retry counter before you start to decrypt/verify and then increase it again if the PIN decrypts and is correct.
Note that your haphazard scheme is no replacement for TLS and may - and probably has - other vulnerabilities.

Random function in Javascript considered insecure. Does it really matter?

I am writing some code that shall run in a browser, which uses Javascript random function(s) for key generation.
Now reading in several forums that this is considered to be not secure, due to bad random number generation.
Now, what if I only create one key pair per browser/computer. So, having a distributed scenario where there is actually no sequence of random numbers per browser. Will this fundamentally change the problematic situation? Thanks for your thoughts.
Yes it matters. If an attacker generates random numbers at the same time as a genuine user, they can predict what is generated and retrieve they key. Even if the clocks aren't fully synchronised, an attacker could generate a range around the UNIX timestamp when the key was known to have been generated and then try each one in turn.
Solution: Use window.crypto to generate a secure random number using the crypto library.
The Crypto interface represents basic cryptography features available
in the current context. It allows access to a cryptographically strong
random number generator and to cryptographic primitives.
Random numbers are generated in Javascript by the amount of milliseconds since 1st of January 1970(UNIX Timestamp). Then Javascript just takes the first few values and that is your random number. i.e.
Math.floor((Math.random() * 10) + 1);
Makes a random number between 1 and 10.

Categories

Resources