CVE-2021-21253: Use of Password Hash With Insufficient Computational Effort (CWE-916)
value: OnlineVotingSystem is an open source project hosted on GitHub. OnlineVotingSystem before version 1.1.2 hashes user passwords without a salt, which is vulnerable to dictionary attacks. Therefore there is a threat of security breach in the voting system. Without a salt, it is much easier for attackers to pre-compute the hash value using dictionary attack techniques such as rainbow tables to crack passwords. This problem is fixed and published in version 1.1.2. A long randomly generated salt is added to the password hash function to better protect passwords stored in the voting system.
CWE description: The product generates a hash for a password, but it uses a scheme that does not provide a sufficient level of computational effort that would make password cracking attacks infeasible or expensive.
CVSS v3 base score: 5.3
Published: 2021-01-21T15:15Z
Repository: https://github.com/dbijaya/OnlineVotingSystem
Language: Java
Files changed: src/com/bijay/onlinevotingsystem/controller/AdminLoginController.java, src/com/bijay/onlinevotingsystem/controller/SHA256.java, src/com/bijay/onlinevotingsystem/controller/VoterLoginController.java, src/com/bijay/onlinevotingsystem/dao/AdminDaoImpl.java, src/com/bijay/onlinevotingsystem/dao/VoterDaoImpl.java
Commit date: 2021-01-10 15:05:50 +0000
Commit message: Add a salt to SHA-256 hash

Diff: diff --git a/src/com/bijay/onlinevotingsystem/controller/AdminLoginController.java b/src/com/bijay/onlinevotingsystem/controller/AdminLoginController.java
index c166727..a3e985e 100644
--- a/src/com/bijay/onlinevotingsystem/controller/AdminLoginController.java
+++ b/src/com/bijay/onlinevotingsystem/controller/AdminLoginController.java
@@ -16,11 +16,10 @@ import com.bijay.onlinevotingsystem.dao.AdminDaoImpl;
 
 @WebServlet("/aLoginController")
 public class AdminLoginController extends HttpServlet {
 	private static final long serialVersionUID = 1L;
 	AdminDao adminDao = new AdminDaoImpl();
-	SHA256 sha = new SHA256();
 
 	protected void doGet(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
 
 		HttpSession session = request.getSession();
@@ -34,11 +33,11 @@ public class AdminLoginController extends HttpServlet {
 	protected void doPost(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
 
 		// to get values from the login page
 		String userName = request.getParameter("aname");
-		String password = sha.getSHA(request.getParameter("pass"));
+		String password = request.getParameter("pass");
 		// String password = request.getParameter("pass");
 		String rememberMe = request.getParameter("remember-me");
 
 		// validation
 
@@ -70,6 +69,6 @@ public class AdminLoginController extends HttpServlet {
 			RequestDispatcher rd = request.getRequestDispatcher("adminlogin.jsp");
 			request.setAttribute("loginFailMsg", "Invalid Username or Password !!");
 			rd.include(request, response);
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/com/bijay/onlinevotingsystem/controller/SHA256.java b/src/com/bijay/onlinevotingsystem/controller/SHA256.java
index 48cab56..66fe987 100644
--- a/src/com/bijay/onlinevotingsystem/controller/SHA256.java
+++ b/src/com/bijay/onlinevotingsystem/controller/SHA256.java
@@ -1,37 +1,76 @@
 package com.bijay.onlinevotingsystem.controller;
 
-import java.math.BigInteger;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Base64;
 
 public class SHA256 {
+	private static final String SSHA_PREFIX = "{SSHA}";
+	private static final int SSHA_256_LENGTH = 32; // SHA-256 is 32 bytes long
+	private static final int SALT_LENGTH = 16; // Use a 16 byte salt
 
 	public String getSHA(String password) {
-
 		try {
-			// Static getInstance method is called with hashing SHA
-			MessageDigest md = MessageDigest.getInstance("SHA-256");
-
-			// digest() method called
-			// to calculate message digest of an input
-			// and return array of byte
-			byte[] messageDigest = md.digest(password.getBytes());
-
-			// Convert byte array into signum representation
-			BigInteger no = new BigInteger(1, messageDigest);
-
-			// Convert message digest into hex value
-			String hashPass = no.toString(16);
-
-			while (hashPass.length() < 32) {
-				hashPass = "0" + hashPass;
-			}
-			return hashPass;
+			byte[] salt = getSalt();
+			String cipher = getCipher(password, salt);
+			
+			return cipher;
 
 			// For specifying wrong message digest algorithms
 		} catch (NoSuchAlgorithmException e) {
 			e.printStackTrace();
 			return null;
 		}
 	}
+
+	public static boolean validatePassword(String password, String cipherText) {
+		boolean isValid = false;
+		try {
+			String cipher = cipherText.substring(SSHA_PREFIX.length());
+		
+			byte[] cipherBytes = Base64.getDecoder().decode(cipher.getBytes());
+			byte[] salt = new byte[SALT_LENGTH];
+			System.arraycopy(cipherBytes, SSHA_256_LENGTH, salt, 0, SALT_LENGTH);
+
+			String result = getCipher(password, salt);
+			//Compare the newly hashed password taking the same salt with the input hash
+			isValid = result.equals(cipherText);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+		}
+		return isValid;
+	}
+	
+	private static byte[] getSalt() throws NoSuchAlgorithmException {
+		SecureRandom random = new SecureRandom();
+		byte[] salt = new byte[SALT_LENGTH];
+		random.nextBytes(salt);
+		return salt;
+	}
+
+	private static String getCipher(String password, byte[] salt) throws NoSuchAlgorithmException {
+		// Static getInstance method is called with hashing SHA
+		MessageDigest md = MessageDigest.getInstance("SHA-256");
+		md.update(salt);
+	
+		byte[] passBytes = password.getBytes();
+		byte[] allBytes = new byte[passBytes.length + SALT_LENGTH];
+		System.arraycopy(passBytes, 0, allBytes, 0, passBytes.length);
+		System.arraycopy(salt, 0, allBytes, passBytes.length, SALT_LENGTH);
+	
+			
+		byte[] cipherBytes = new byte[SSHA_256_LENGTH + SALT_LENGTH];
+			
+		// digest() method called
+		// to calculate message digest of an input
+		// and return array of byte
+		byte[] messageDigest = md.digest(allBytes);
+	
+		System.arraycopy(messageDigest, 0, cipherBytes, 0, SSHA_256_LENGTH);
+		System.arraycopy(salt, 0, cipherBytes, SSHA_256_LENGTH, SALT_LENGTH);
+		
+		String result = SSHA_PREFIX + Base64.getEncoder().encodeToString(cipherBytes);
+		return result;
+	}
 }
diff --git a/src/com/bijay/onlinevotingsystem/controller/VoterLoginController.java b/src/com/bijay/onlinevotingsystem/controller/VoterLoginController.java
index 0db1c9f..400e130 100644
--- a/src/com/bijay/onlinevotingsystem/controller/VoterLoginController.java
+++ b/src/com/bijay/onlinevotingsystem/controller/VoterLoginController.java
@@ -19,11 +19,10 @@ import com.bijay.onlinevotingsystem.dao.VoterDaoImpl;
 
 @WebServlet("/vLoginController")
 public class VoterLoginController extends HttpServlet {
 	private static final long serialVersionUID = 1L;
 	VoterDao voterDao = new VoterDaoImpl();
-	SHA256 sha = new SHA256();
 
 	private String host;
 	private String port;
 	private String user;
 	private String pass;
@@ -74,11 +73,11 @@ public class VoterLoginController extends HttpServlet {
 		otp = 5432;
 		Random r = new Random();
 		otp = r.nextInt(max - min) + min;
 
 		String userName = request.getParameter("uname");
-		String password = sha.getSHA(request.getParameter("pass"));
+		String password = request.getParameter("pass");
 		String vemail = request.getParameter("vmail");
 
 		String recipient = vemail;
 		String subject = "otp verification";
 		String content = "your otp is: " + otp;
@@ -117,6 +116,6 @@ public class VoterLoginController extends HttpServlet {
 			 * request.getRequestDispatcher("resetPassword.jsp"); rd.forward(request,
 			 * response);
 			 */
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/com/bijay/onlinevotingsystem/dao/AdminDaoImpl.java b/src/com/bijay/onlinevotingsystem/dao/AdminDaoImpl.java
index a5747cb..15f23b9 100644
--- a/src/com/bijay/onlinevotingsystem/dao/AdminDaoImpl.java
+++ b/src/com/bijay/onlinevotingsystem/dao/AdminDaoImpl.java
@@ -4,10 +4,13 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.crypto.Cipher;
+
+import com.bijay.onlinevotingsystem.controller.SHA256;
 import com.bijay.onlinevotingsystem.dto.Admin;
 import com.bijay.onlinevotingsystem.util.DbUtil;
 
 public class AdminDaoImpl implements AdminDao {
 
@@ -96,18 +99,18 @@ public class AdminDaoImpl implements AdminDao {
 	}
 
 	@Override
 	public boolean loginValidate(String userName, String password) {
 
-		String sql = "select * from admin_table where admin_name=? and password=?";
+		String sql = "select * from admin_table where admin_name=?";
 		try {
 			ps=DbUtil.getConnection().prepareStatement(sql);
 			ps.setString(1, userName);
-			ps.setString(2,password);
 			ResultSet rs =ps.executeQuery();
 			if (rs.next()) {
-				return true;
+				String cipherText = rs.getString("password");
+				return SHA256.validatePassword(password, cipherText);
 			}
 		} catch (SQLException | ClassNotFoundException e) {
 			e.printStackTrace();
 		}
 		return false;
diff --git a/src/com/bijay/onlinevotingsystem/dao/VoterDaoImpl.java b/src/com/bijay/onlinevotingsystem/dao/VoterDaoImpl.java
index 985e4f5..02ca13f 100644
--- a/src/com/bijay/onlinevotingsystem/dao/VoterDaoImpl.java
+++ b/src/com/bijay/onlinevotingsystem/dao/VoterDaoImpl.java
@@ -5,10 +5,11 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 import java.sql.Date;
 
+import com.bijay.onlinevotingsystem.controller.SHA256;
 import com.bijay.onlinevotingsystem.dto.Voter;
 import com.bijay.onlinevotingsystem.util.DbUtil;
 
 public class VoterDaoImpl implements VoterDao {
 
@@ -78,19 +79,19 @@ public class VoterDaoImpl implements VoterDao {
 		}
 	}
 
 	@Override
 	public boolean loginValidate(String userName, String password, String email) {
-		String sql = "select * from voter_table where voter_name=? and password=? and email=?";
+		String sql = "select * from voter_table where voter_name=? and email=?";
 		try {
 			ps = DbUtil.getConnection().prepareStatement(sql);
 			ps.setString(1, userName);
-			ps.setString(2, password);
-			ps.setString(3, email);
+			ps.setString(2, email);
 			ResultSet rs = ps.executeQuery();
 			if (rs.next()) {
-				return true;
+				String cipherText = rs.getString("password");
+				return SHA256.validatePassword(password, cipherText);
 			}
 		} catch (ClassNotFoundException | SQLException e) {
 			e.printStackTrace();
 		}
 		return false;

Vulnerable code: 	SHA256 sha = new SHA256();
		String password = sha.getSHA(request.getParameter("pass"));
}
import java.math.BigInteger;

			// Static getInstance method is called with hashing SHA
			MessageDigest md = MessageDigest.getInstance("SHA-256");

			// digest() method called
			// to calculate message digest of an input
			// and return array of byte
			byte[] messageDigest = md.digest(password.getBytes());

			// Convert byte array into signum representation
			BigInteger no = new BigInteger(1, messageDigest);

			// Convert message digest into hex value
			String hashPass = no.toString(16);

			while (hashPass.length() < 32) {
				hashPass = "0" + hashPass;
			}
			return hashPass;
	SHA256 sha = new SHA256();
		String password = sha.getSHA(request.getParameter("pass"));
}
		String sql = "select * from admin_table where admin_name=? and password=?";
			ps.setString(2,password);
				return true;
		String sql = "select * from voter_table where voter_name=? and password=? and email=?";
			ps.setString(2, password);
			ps.setString(3, email);
				return true;
Fixed code: 		String password = request.getParameter("pass");
}
import java.security.SecureRandom;
import java.util.Base64;
	private static final String SSHA_PREFIX = "{SSHA}";
	private static final int SSHA_256_LENGTH = 32; // SHA-256 is 32 bytes long
	private static final int SALT_LENGTH = 16; // Use a 16 byte salt
			byte[] salt = getSalt();
			String cipher = getCipher(password, salt);
			
			return cipher;

	public static boolean validatePassword(String password, String cipherText) {
		boolean isValid = false;
		try {
			String cipher = cipherText.substring(SSHA_PREFIX.length());
		
			byte[] cipherBytes = Base64.getDecoder().decode(cipher.getBytes());
			byte[] salt = new byte[SALT_LENGTH];
			System.arraycopy(cipherBytes, SSHA_256_LENGTH, salt, 0, SALT_LENGTH);

			String result = getCipher(password, salt);
			//Compare the newly hashed password taking the same salt with the input hash
			isValid = result.equals(cipherText);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return isValid;
	}
	
	private static byte[] getSalt() throws NoSuchAlgorithmException {
		SecureRandom random = new SecureRandom();
		byte[] salt = new byte[SALT_LENGTH];
		random.nextBytes(salt);
		return salt;
	}

	private static String getCipher(String password, byte[] salt) throws NoSuchAlgorithmException {
		// Static getInstance method is called with hashing SHA
		MessageDigest md = MessageDigest.getInstance("SHA-256");
		md.update(salt);
	
		byte[] passBytes = password.getBytes();
		byte[] allBytes = new byte[passBytes.length + SALT_LENGTH];
		System.arraycopy(passBytes, 0, allBytes, 0, passBytes.length);
		System.arraycopy(salt, 0, allBytes, passBytes.length, SALT_LENGTH);
	
			
		byte[] cipherBytes = new byte[SSHA_256_LENGTH + SALT_LENGTH];
			
		// digest() method called
		// to calculate message digest of an input
		// and return array of byte
		byte[] messageDigest = md.digest(allBytes);
	
		System.arraycopy(messageDigest, 0, cipherBytes, 0, SSHA_256_LENGTH);
		System.arraycopy(salt, 0, cipherBytes, SSHA_256_LENGTH, SALT_LENGTH);
		
		String result = SSHA_PREFIX + Base64.getEncoder().encodeToString(cipherBytes);
		return result;
	}
		String password = request.getParameter("pass");
}
import javax.crypto.Cipher;

import com.bijay.onlinevotingsystem.controller.SHA256;
		String sql = "select * from admin_table where admin_name=?";
				String cipherText = rs.getString("password");
				return SHA256.validatePassword(password, cipherText);
import com.bijay.onlinevotingsystem.controller.SHA256;
		String sql = "select * from voter_table where voter_name=? and email=?";
			ps.setString(2, email);
				String cipherText = rs.getString("password");
				return SHA256.validatePassword(password, cipherText);