Check update#1
This logic is a candidate for a authentication procedure, done by simple HTTP requests:
I'm sending: userName + encrypted_userName (encrypted_userName is actually the encrypted result of userName, done using AES & as key i use the md5 hash of the password). NOTE: I'm not sending the md5 hashed Password.
on the server I'm comparing: encrypted_userName with own_encrypted_userName (since on server i have access to full info on user, i calculate own encrypted_userName).
Question: is this a security flaw? Say bad guy captures full HTTP request, can he extract password from this 2 infos?
CODE DETAILS, if needed:
private static Cipher getCipher(String key, int mode) throws Exception{
byte[] rawKey = getRawKey(key.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Key key2 = skeySpec;
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(mode, key2);
return cipher;
}
private static byte[] getRawKey(byte[] seed) throws Exception {
/* BEFORE:
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
*/
byte[] raw = MD5Util.getMD5HashRaw(seed);
return raw;
}
(NOTE: reason why i use the hash of the password is that code is compatible among platforms (client is Android device), while the commented version is NOT)
UPDATE#1
Short answer:
Presented logic is not even close to be considered a secure authentication mecanism
(for Why? check Michael's answer bellow)
Decided to use Kerberos (AND not https, since I am not familiar + seems complicated to setup):
It is not a true version of Kerberos (like v4 or v5), it is just my own implementation so lets call it "similar with Kerberos" (I know, I know: DONT "roll your own encryption"!!!),
Here are some details:
it works on UDP (now)
authentication is done only once, by:
client sending a Authenticator message (contains: [userId] in plain text & [something_ecrypted] with [entered_user_password] (curently [something_ecrypted] contains just a timestamp, call it [authenticator_creation_timestamp])) NOTE: password is not transmited
server upon receiving message, tryies to decrypt the [something_ecrypted] with [actual_user_password] -> if SUCCESS then client is who it pretends to be, so i send him back a OK response (as in Kerberos this response contains some stuff, like a [public_key] (a RSA key, but encrypted with user_password) + ticket granting ticket (call it [TGT], encrypted with a password known only by server, currently it doenst expire, this [TGT] also contains some stuff, like these 2 timestamps: [TGT_creation_time_stamp] + [authenticator_creation_timestamp] (the one received in the Authenticator message))
after receiving this OK message, client has procured a valid [public_key].. so nice!
protection agains "reply attack" is not a 100% guarantee, but I see it "safe enought":
on each next HTTP reaquest, i attach as headers these 2 guys [new_request_creation_timestamp] (encrypted with [public_key], procured above) + the [TGT] (untouched, as received above)
on server I just need to validate [new_request_creation_timestamp] agains some math (obvious [TGT] needs to be valid too):
** i expect that the following variables to be almost equal
delta1 = [TGT_creation_time_stamp] - [authenticator_creation_timestamp]
delta2 = now()-[new_request_creation_timestamp]
(I actually allow a difference between them of 5 seconds, but from my tests, its just a matter of some 10-20 millis,
** So initial delta (calculated when creating OK response to Authenticator) should perpetuate on next interactions.
I do find this new approach quite trust-worthy, but if you have an opinion or see a BUG in logic, please share.. Thanks
Yes, this is a weak security mechanism.
Anyone who captures the information sent to the server can easily replay it to authenticate themselves (replay attack).
It is vulnerable to offline password guessing - anyone who captures the information sent to the server can then very quickly test a password list to find what password your user has chosen (by encrypting the observed username using the hash of each password in turn). The hashing can even be precomputed, speeding the attack up further.
Password based authentication protocols should be resistant to replay attacks and offline password guessing attacks.
Simply using an HTTPS (TLS) connection to your server and sending the username and password in plaintext would normally be a better solution.
In response to your update 1:
I strongly advise using HTTPS. It is used everywhere for a reason - it has undergone immense security review and been found to be (largely) secure - far better than what you can get through a SO post.
I haven't considered your updated scheme thoroughly, but as it is based on Kerberos it is also subject to offline password guessing attacks as I described above.
Having successfully authenticated, don't forget about then actually protecting your data - you'll likely need to derive a shared symmetric key then use authentication + encryption on your data...
What i understand is : you are sending Username + Encrypted Username to the server.
Ans:
Since you are sending the Username and the encrypted Username which is : UserName + AES(UserName + MD5 Hashed Password)
If anyone knows or find that you give the Username and also gets the Username from your data to server: No worries. There you stand with AES. If you have doubt in AES encryption check this. Your data is secure.
I don't think this is a security flaw per se because even knowing both the plaintext message and the encrypted one, it is practically impossible to get the AES key. But I still wouldn't recommend to store the passwords hashed with MD5.
Related
I wrote an app that queries a Jira API which requires authentication that I provide through Basic Authentication (base64 in the header). The password was stored in the code which has to stop now because I want to hand over the code.
When the users changes their passwords due to the password schedule, the app should prompt the user for the new Jira password, save it securely, and pass it to the Jira API via Basic Authentication.
What's the best way to do this?
Normally, we would hash it but that's not possible because hashing is one-way direction and we need to pass in the real password to Jira instead of a hash.
In case of storing a string which needs to be protected in case of breaches or as a general software data security concern, encryptions should be done. For example, in your case, when the password is taken by the user then it shall be encrypted by the software before storing. While retrieving, the password is decrypted and converted to the hash(or base64) which Jira accepts for the login handshake.
Apart from the simply encrypting and decrypting, a better approach will be to use salts while encrypting and using multiple encryptions in the loop to avoid brute force attempts.
Pseudocode:
unsafe_password = getPasswordFromUser()
salt = getRandomString();
safePassword = encrypt(unsafe_password, salt, key)
// Store the password
putEntryInDB(user, safePassword, salt)
// Retrieve password
[passwordSalt, encryptedPassword] = getSaltAndEncryptedPasswordFromDB()
unsafePassword = decrypt(encryptedPassword, passwordSalt, key)
// Now login into Jira with the actual user's password (unsafePassword)
P.S. You'll be needing to store a key in the code or in some software's configuration.
Source: Attempt 4&5 https://nakedsecurity.sophos.com/2013/11/20/serious-security-how-to-store-your-users-passwords-safely/
I am making an server API which will return some confidential keys to my app.
Then the app will use these key to perform a particular action. I would be sending the Keys over SSL so that any Man In the Middle attack could not read them.
To start first I will be first everything the Package name and then I also want to verify the something which assures me that my app has not been decompiled and recompiled and the package is not fake.
Basically I want to avoid these issues:
1) Someone is not creating a fake package name and then sending the request
2) Someone has not recompiled my app and then sending the request
3) Someone if not tracking the response of the server via MIM
Till now I have thought the best way would be to use a HASH key and then compare it within my server to see if the POST key is the same as stored in my server.
But I have not been able to find a key which is attached to the signing key of the app and which cannot be accessed by anyone having the APK of my app.
Any help would be grateful.
You can add extra layer of protection if you create keys in your app using C++ code available on android's NDK libraries. Here's an amazing tutorial for that. Basically, this protects your app from de-compiling tools which commonly de-compiles java files. Also, I recommend adding AES encryption on your keys before sending it through the post request of your SSL server.
On your onCreate() method, get the key from native C++ implementation:
String nativeKey = invokeNativeFunction()
then encrypt it:
byte[] keyStart = nativeKey.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
// encrypt
byte[] encryptedData = encrypt(key,b);
Encrypt method:
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
Edit for CSRF:
There's an interesting answer from here: Authenticity_token in Rails + Android, also on Wikipedia, there are quite suggestions as to how to counter cross site request forgery. which includes:
Synchronizer token pattern
Cookie-to-header token
to name a few.
Here's a layer of extra security to identify the authenticity of the app request as well.
I'm trying to do a login method that keeps the client password secure and encrypted in my server.
The library I'm using from Angular, is https://github.com/middleout/angular-cryptography
The idea is, to follow these steps:
I set a salt in my module.config:
app.config(['$cryptoProvider', function($cryptoProvider){
$cryptoProvider.setCryptographyKey('thisismysalt');
}]);
I encrypt the password with itself:
user.pw = $crypto.encrypt(user.pw, user.pw);
If I'm registering an user, I re-encrypt the password with itself (repeat step 2) and save it in the DB. If I'm logging, I just send the result from the last step, to the server.
When you decypher the double-encrypted string with the single-encrypted one, you get the single encrypted string again. So if your password was correct, you just compare the result with the single-encrypted string, and you validate the user.
Ok, this method should work (I already did it in Node some time ago), works great with SSL to protect user's passwords even in your server!
But I can't find any library or snippet in Java that can do it. I tried many of them, but they are hard to understand and when I adapt them to my procedure, they just won't work. I tried the following method:
static String IV = "AAAAAAAAAAAAAAAA";
static String plaintext = "test text 123\0\0\0"; /*Note null padding*/
static String encryptionKey = "0123456789abcdef";
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
I passed it as first argument, the double-encrypted password from the DB, and as second argument, the single-encrypted password from the frontend:
java.security.InvalidKeyException: Invalid AES key length: 44 bytes
Am I doing something wrong? Should I use a different algorithm?
middleout/angular-cryptography uses CryptoJS 3.1.2 under the hood with the least effort possible.
So
return $crypto.encrypt(plaintext, password);
is the same as
$cryptoProvider.setCryptographyKey(password);
return $crypto.encrypt(plaintext, password);
and the same as
return CryptoJS.AES.encrypt(plaintext, password).toString();
I describe in my answer here how to do the same thing in Java.
If you're using SSL/TLS, there is not much benefit to doing this encryption additionally. The password is already sent in an encrypted way over the internet. Even worse, since the password must be available at the server side, you must store the password in cleartext. That's not how it is done.
You need to use hashing instead with some strong ones being PBKDF2, bcrypt, scrypt and Argon2. Since hash functions are one-way function, you won't be able to "decrypt" the hashes. In order to authenticate your user, you can run the password through the hash function again in order to compare with the hash that is stored in the database. Since you're already using SSL/TLS, the password is already secured during transmission. See more: How to securely hash passwords?
I have an access-controlled PHP system that HTTP redirects clients to a Java-based reporting system with a unique security model (it's awful). To get around the report security model I'm using a Tomcat filter to validate all requests before they reach the reporting system. I'm using an encrypted token passed from PHP to Java which tells the reporting system who the client is. The filter checks the requested report name against a restricted list and returns a 403 if the client's role is insufficient.
The encrypted token stores a timestamp and the user's role, e.g.
1365549482|SysAdmin
When encrypted it looks something like this
vSEFgBYd30Ik5p4PZlG968cvdg==
The PHP system acts as a proxy for all reporting requests. When a user requests a report the request goes to PHP, which generates an encrypted token, URL encodes it, then appends it to the report URL and makes the GET request to the reporting system. My Java filter decrypts the token, pulls it apart, and figures out what to do.
9 times out of 10 this is fine, but occasionally the token cannot be properly decrypted. The above (unencrypted) example is converted to something like this
1365549482q??YZ7
And everything goes wrong.
I'm a bit out of my depth with encryption, decryption, and the particulars of character encoding, but unfortunately I'm the only developer available to work on this. Any thoughts at all on what might be going wrong here would be hugely appreciated. I don't expect any big code changes as it works most of the time, but there is clearly a time-sensitive component in the mix that I don't understand. Code snippets below
EDIT
I've spent a while debugging this now and it just got stranger. I wrote a small Java program to request a token from PHP via HTTP GET. The PHP script returns the same (URL-encoded) value that is passed to Java via a URL parameter in the normal workflow. The Java program decodes and decrypts this in the same way as the code snippet below and checks the result. Over thousands of iterations (so far, and counting) it is working as expected. However, while this test is going on I can see the same failures happening in the filter's log file.
Whatever's causing this intermittent problem is seemingly related the Java class being a Tomcat filter or the data being passed by URL via Tomcat. Does this give anyone a hint as to what could be going on here? I'm so very confused right now.
PHP
$presentAsSeconds = time();
$message = strval($presentAsSeconds + Configure::read('Reporting.Authentication.ExpireInSeconds')) . '|' . $userDetails['role'];
return base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
md5(Configure::read('Reporting.Authentication.Key')), // matches "the key" in Java function
$message,
MCRYPT_MODE_CFB,
Configure::read('Reporting.Authentication.IVector') // matches "the vector" in Java function
)
);
Java
private String decrypt(String initial) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(md5("the key").getBytes("UTF-8"), "AES");
IvParameterSpec initialVector = new IvParameterSpec("the vector".getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, initialVector);
byte[] encryptedByteArray = (new org.apache.commons.codec.binary.Base64()).decode(initial.getBytes("UTF-8"));
byte[] decryptedByteArray = cipher.doFinal(encryptedByteArray);
return (new String(decryptedByteArray, "UTF8"));
}
private String md5(String input) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes("UTF-8"));
BigInteger number = new BigInteger(1, messageDigest);
return number.toString(16);
}
The problem may be with your getBytes() methods - these use the default system character encoding, which is NOT the same across every JVM. Use getBytes("UTF-8") instead.
I think the issue is in how you've used the mcrypt library in PHP and then you're base64 encoding the encrypted data? We've definitily had some issues doing something similar and dropped the base64 encode and it worked after that.
Can you use another method to pass the token instead of the url? Such as a cookie or auth header?
Here's a snippet of my encryption/decryption in PHP (I'm not great with Java) because I think your mcrypt isn't right.
ENCODING:
$userObjectJson = json_encode($this);
//encrypt the user session object
$mcrypt = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CFB, '');
$_SESSION['iv'] = mcrypt_create_iv(mcrypt_enc_get_iv_size($mcrypt), MCRYPT_RAND);
$keySize = mcrypt_enc_get_key_size($mcrypt);
$key = substr(MYAWESOME_KEY, 0, $keySize);
mcrypt_generic_init($mcrypt, $key, $_SESSION['iv']);
$_SESSION['user'] = mcrypt_generic($mcrypt, $userObjectJson);
mcrypt_generic_deinit($mcrypt);
mcrypt_module_close($mcrypt);
DECODING:
//decrypt the user session object
$mcrypt = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CFB, '');
$keySize = mcrypt_enc_get_key_size($mcrypt);
$key = substr(MYAWESOME_KEY, 0, $keySize);
mcrypt_generic_init($mcrypt, $key, $_SESSION['iv']);
$userObjectJson = mdecrypt_generic($mcrypt, $_SESSION['user']);
mcrypt_generic_deinit($mcrypt);
mcrypt_module_close($mcrypt);
Users log in to my BlackBerry app with a username and password provided at registration. My app connects to a Java Web Service that does all the logic.
How do I go about storing the password and username in a safe manner on my server? Everybody says salting and hashing, but I have no idea how to do this since I've never worked with it. How can I do this in Java?
How do I manage sending the password securely from the app to the server?
To store the credentials, one possibility is to use PBKDF2. A Java implementation (that I have not used) is available here. Run the password with the salt value through that and store the resulting hash data. The salt value is typically a newly generated random value (one for each password). This helps prevent dictionary attacks via rainbow tables (pre-computed tables of hashed passwords). Using java.security.SecureRandom is a possibility for generating those.
The client application should probably connect to the server using SSL/TLS. That will provide the encryption to protect the credentials when passed from client to your server application.
Edit Based on our conversation in the comments, it sounds as if the goal is not to use SSL. Assuming that is true and no other end-to-end communications encryption is planned, then it seems to imply that the security of the communications is not a high priority. If that is true, then maybe the described scheme for authenticating is sufficient for the application. Nonetheless, it seems worth pointing out the potential issues so you can consider them.
The proposed scheme (I think) is to send from the client to the server this value: Hash(Hash(password,origsalt),randomsalt). What this really means is that the password is effectively Hash(password,origsalt). If the attacker can get that information, then they can login as that user because they take that value and hash it with the new salt value to authenticate. In other words, if the database of hashed passwords is compromised, then the attacker can easily gain access. That somewhat defeats the purpose of salting and hashing the passwords in the first place.
Without SSL (or some other end-to-end encryption), there is the possibility of a man-in-the-middle attack. They can either listen in or even impersonate one end of the conversation.
Seems like your question has a few parts...
The most secure way to store the password in the database is to use a hash with a Salt + Pepper seed as described here. If you want to find a good way of implementing that specific technique in Java, try opening a new question.
I can see why it would make sense to encrypt a username/password hash prior to sending to the server, since SSL proxies can be a man-in-the-middle for that operation.
As a solution try creating a token in JSON or XML format that has the following properties:
Username.ToUpper() // Dont want this to be case sensitive
ExpiryDate (Say now plus 5 minutes)
Nonce (a random number that is saved on the backend to prevent replay attacks)
SHA 256 signature
Use the locally entered username and password to create a SHA256 signature, as it will be a constant. Use this signature to sign the JSON or XML you send to the server with each request.
In other words you're using a symmetric key based on the username and password, without sending it across the wire. Of course you may want to salt and pepper the generation of that symmetric key for more security.
That's all I got for a high level design, since I'm not intimately familiar with Java. Do share your links/code when you do find the answers.
So here's what I ended up doing:
package Utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
/**
*
* #author octavius
*/
public class SalterHasher {
private String salt;
private String pepper = "******************";
private String hash;
private String password;
public SalterHasher(String password, String username)
{
this.password = password;
salt = RandomStringUtils.random(40, username);
hash = DigestUtils.md5Hex(password + salt + pepper);
}
public String getHash(){
return hash;
}
/**
* #return the salt
*/
public String getSalt() {
return salt;
}
public String makeHash(String salt){
return DigestUtils.md5Hex(password + salt + pepper);
}
}
A very simple class that generates a salt and the hash for me and has a pepper included for added security, the makeHash() function I use for verification when the user logs in. In view of what I previously mentioned in the comments above I didn't end up using the verification process I proposed and chose to simply add the pepper to my server side code since hashing I believe would prove to be heavy on the BlackBerry device. Thanks again to those who helped me. Good discussions were had :)