Implementation of secure encryption algorithms for password hashing
import java.security.*;
import javax.xml.bind.DatatypeConverter;
import javax.servlet.http.*;
public class PasswordHashingServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String password = request.getParameter("password");
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte[] digest = md.digest();
String myHash = DatatypeConverter.printHexBinary(digest).toUpperCase();
// Store the hashed password in the database
// ...
} catch (NoSuchAlgorithmException e) {
// Handle the exception
}
}
}
The above Java Servlet handles a POST request to receive a password from the client. It then uses the MD5 algorithm to hash the password. The MD5 MessageDigest instance is created with
MessageDigest.getInstance("MD5")
. The password string is converted to bytes and updated to the MessageDigest with
md.update(password.getBytes())
. The hashing is done with
md.digest()
which is then converted to a hexadecimal string.
The MD5 hash is considered insecure for hashing passwords. The main reason is that it's fast. This might seem like a good thing, but when it comes to password cracking, it's a disadvantage. A faster algorithm means a brute force attack can try more possibilities in a shorter time. Additionally, MD5 is vulnerable to hash collisions, where different inputs produce the same hash output.
In this scenario, an attacker who is able to access the hashed passwords (for example, by exploiting a SQL injection vulnerability or a database breach) could use pre-computed tables of MD5 hashes (also known as rainbow tables) or even brute force attacks to reverse-engineer the passwords. This is a serious security risk and could lead to unauthorized access to user accounts.
import java.security.*;
import javax.xml.bind.DatatypeConverter;
import javax.servlet.http.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.util.Base64;
public class PasswordHashingServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String password = request.getParameter("password");
byte[] salt = new byte[16]; // Consider a secure way to generate and store salt per user
try {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
String securePassword = Base64.getEncoder().encodeToString(hash);
// Store the hashed password and salt in the database
// ...
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
// Handle the exception
}
}
}
The updated code replaces the insecure MD5 hashing algorithm with the more secure PBKDF2WithHmacSHA256 algorithm. This algorithm is currently recommended for password hashing as it is computationally expensive, which makes brute-force and rainbow table attacks more difficult.
The password is first converted to a character array, which is then combined with a salt value and passed to the
PBEKeySpec
constructor along with the iteration count and the desired key length. The salt is a random value that should be generated for each user individually and stored in the database along with the hashed password. It is used to prevent pre-computed rainbow table attacks.
The
SecretKeyFactory
instance is then used to generate a
SecretKey
from the specified key specification. The
getEncoded
method is used to retrieve the key in its primary encoding format, which is then encoded to a string using Base64 encoding.
The hashed password (and salt) should then be stored in the database. Any part of the application that interacts with the password hashing functionality should be updated to use this new algorithm.