簡體   English   中英

驗證用戶名和密碼

[英]Verify username and a salted password

我有一個使用JDBC Jquery和MYSQL的應用程序,該應用程序列出了帶引號的表,用戶可以添加引號。

我有一個登錄頁面,我在其中檢查用戶名的輸入並將其與數據庫中存在的內容進行比較。 這樣,它工作正常

public Boolean findByUsername(String username) throws SQLException {

        ResultSet rs = null;
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {

            conn = Database.getConnection();
            conn.setAutoCommit(false);
            String query = "SELECT * FROM user WHERE username = ?";
            pstmt = conn.prepareStatement(query);
            pstmt.setString(1, username);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                return true;

            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            conn.close();
        }
        return false;
    }

但是,當我想比較密碼時,我遇到了問題,即我用PBKDF2對該密碼加鹽,它會生成一些隨機字符,例如1000:485b5808b4786d6aa6e5360ad157945ee927d85e49e96312:60d76b0ef1b742cfa462d84eb7fd7c37eb361717179cc0a45。 當我想將密碼輸入與數據庫中的內容進行比較時,我使用此方法。

public Boolean findByPassword(String password) throws SQLException,
            NoSuchAlgorithmException, InvalidKeySpecException {

        ResultSet rs = null;
        Connection conn = null;
        PreparedStatement pstmt = null;
        PasswordHash p = new PasswordHash();
        String hash = p.createHash(password);
        try {

            conn = Database.getConnection();
            conn.setAutoCommit(false);
            String query = "SELECT * FROM user WHERE passwd = ?";
System.out.println("password: " +password);
System.out.println("hashpassword " +hash);
            pstmt = conn.prepareStatement(query);
            pstmt.setString(1, hash);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                if(p.validatePassword(password, hash)){ 
                    return true;

                }


            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            conn.close();
        }
        return false;
    }

我使用此類來哈希密碼

public class PasswordHash {
    public final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";

    // The following constants may be changed without breaking existing hashes.
    public final int SALT_BYTE_SIZE = 24;
    public final int HASH_BYTE_SIZE = 24;
    public final int PBKDF2_ITERATIONS = 1000;

    public final int ITERATION_INDEX = 0;
    public final int SALT_INDEX = 1;
    public final int PBKDF2_INDEX = 2;

    /**
     * Returns a salted PBKDF2 hash of the password.
     *
     * @param password
     *            the password to hash
     * @return a salted PBKDF2 hash of the password
     */
    public String createHash(String password) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        return createHash(password.toCharArray());
    }

    /**
     * Returns a salted PBKDF2 hash of the password.
     *
     * @param password
     *            the password to hash
     * @return a salted PBKDF2 hash of the password
     */
    public String createHash(char[] password) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        // Generate a random salt
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_BYTE_SIZE];
        random.nextBytes(salt);

        // Hash the password
        byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
        // format iterations:salt:hash
        return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash);
    }

    /**
     * Validates a password using a hash.
     *
     * @param password
     *            the password to check
     * @param correctHash
     *            the hash of the valid password
     * @return true if the password is correct, false if not
     */
    public boolean validatePassword(String password, String correctHash)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        return validatePassword(password.toCharArray(), correctHash);
    }

    /**
     * Validates a password using a hash.
     *
     * @param password
     *            the password to check
     * @param correctHash
     *            the hash of the valid password
     * @return true if the password is correct, false if not
     */
    public boolean validatePassword(char[] password, String correctHash)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        // Decode the hash into its parameters
        String[] params = correctHash.split(":");
        int iterations = Integer.parseInt(params[ITERATION_INDEX]);
        byte[] salt = fromHex(params[SALT_INDEX]);
        byte[] hash = fromHex(params[PBKDF2_INDEX]);
        // Compute the hash of the provided password, using the same salt,
        // iteration count, and hash length
        byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
        // Compare the hashes in constant time. The password is correct if
        // both hashes match.
        return slowEquals(hash, testHash);
    }

    /**
     * Compares two byte arrays in length-constant time. This comparison method
     * is used so that password hashes cannot be extracted from an on-line
     * system using a timing attack and then attacked off-line.
     * 
     * @param a
     *            the first byte array
     * @param b
     *            the second byte array
     * @return true if both byte arrays are the same, false if not
     */
    private boolean slowEquals(byte[] a, byte[] b) {
        int diff = a.length ^ b.length;
        for (int i = 0; i < a.length && i < b.length; i++)
            diff |= a[i] ^ b[i];
        return diff == 0;
    }

    /**
     * Computes the PBKDF2 hash of a password.
     *
     * @param password
     *            the password to hash.
     * @param salt
     *            the salt
     * @param iterations
     *            the iteration count (slowness factor)
     * @param bytes
     *            the length of the hash to compute in bytes
     * @return the PBDKF2 hash of the password
     */
    private byte[] pbkdf2(char[] password, byte[] salt, int iterations,
            int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
        SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
        return skf.generateSecret(spec).getEncoded();
    }

    /**
     * Converts a string of hexadecimal characters into a byte array.
     *
     * @param hex
     *            the hex string
     * @return the hex string decoded into a byte array
     */
    private byte[] fromHex(String hex) {
        byte[] binary = new byte[hex.length() / 2];
        for (int i = 0; i < binary.length; i++) {
            binary[i] = (byte) Integer.parseInt(
                    hex.substring(2 * i, 2 * i + 2), 16);
        }
        return binary;
    }

    /**
     * Converts a byte array into a hexadecimal string.
     *
     * @param array
     *            the byte array to convert
     * @return a length*2 character string encoding the byte array
     */
    private String toHex(byte[] array) {
        BigInteger bi = new BigInteger(1, array);
        String hex = bi.toString(16);
        int paddingLength = (array.length * 2) - hex.length();
        if (paddingLength > 0)
            return String.format("%0" + paddingLength + "d", 0) + hex;
        else
            return hex;
    }

    /**
     * Tests the basic functionality of the PasswordHash class
     *
     * @param args
     *            ignored
     */

}

我在這里叫方法

@POST
    @Path("/login")
    @Produces(MediaType.TEXT_PLAIN)
    public String LogInUser(String username, String password)
            throws NoSuchAlgorithmException, InvalidKeySpecException,
            SQLException {
        Gson gson = new Gson();
        User theuser = gson.fromJson(username, User.class);
        if (!u.findByUsername(theuser.getUsername())
                && u.findByPassword(theuser.getPasswd())) {
            return theuser.getUsername();
        }
        return null;

    }

我如何將密碼輸入與數據庫中存在的內容進行比較?

您可以使用SELECT * FROM user WHERE username = ?findByUsername方法中查詢數據庫SELECT * FROM user WHERE username = ? 該查詢的結果也應返回密碼哈希。 因此,只需從此查詢中獲取密碼哈希,然后調用validatePassword

忘記您的findByPassword方法。 它不會工作。 除了它是完全錯誤的。 (如果存儲在數據庫中的任何用戶都提供了密碼,它將返回true)

有關在SQL中存儲密碼(或實際上是任何數據庫,或更確切地說是任何存儲)的一些提示:

  • 始終存儲由強哈希值哈希的密碼。 (您可以這樣做,但是任何人談論存儲密碼時都應重復此操作)
  • 總是給哈希加鹽 (您也可以這樣做,但與上面相同)
    • 並且請確保您的鹽是隨機的
  • 始終用戶名或您的唯一標識符查詢
  • 在此標識符上具有唯一的密鑰。
  • 切勿使用任何可能包含重復項的列來查找用戶記錄。
    • 這意味着也不要通過密碼 (或哈希) 查詢
  • 切勿在查詢中使用密碼。 切勿以明文形式。
    • 更好的是,嘗試避免在SELECT中使用散列。
  • 使用多輪算法。 但是要小心。
    • 除非您知道自己在做什么,否則不要自己實現算法。
    • 保持回合數合理。 將密碼哈希100ms可能會使您輕松獲得DoSed。
    • 不要做for(int i=0;i<rounds;i++) md5(previous_hash)for(int i=0;i<rounds;i++) md5(previous_hash)事情。 它可能會破壞哈希。
  • 切勿自己散列。 (當然,除非您非常清楚自己在做什么)
  • 不要合並哈希。
    • md5(sha256(sha512(password)))似乎是個好主意,但請相信我,事實並非如此。
  • 始終存儲散列的密碼。 始終使用強哈希。 總是用隨機鹽對哈希值加鹽。

(如果有人提示缺少提示,我很樂意添加...)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM