ฉันจะแฮรหัสผ่านใน Java ได้อย่างไร


176

ฉันต้องแฮรหัสผ่านสำหรับการจัดเก็บในฐานข้อมูล ฉันจะทำสิ่งนี้ใน Java ได้อย่างไร

ฉันหวังว่าจะใช้รหัสผ่านแบบข้อความธรรมดาเพิ่มเกลือแบบสุ่มจากนั้นเก็บเกลือและรหัสผ่านที่ถูกแฮชไว้ในฐานข้อมูล

จากนั้นเมื่อผู้ใช้ที่ต้องการเข้าสู่ระบบฉันสามารถใช้รหัสผ่านที่ส่งเพิ่มเกลือแบบสุ่มจากข้อมูลบัญชีของพวกเขาแฮชมันและดูว่ามันเท่ากับรหัสผ่านแฮชที่เก็บไว้กับข้อมูลบัญชีของพวกเขา


11
@YGL นี่ไม่ใช่การรวมตัวกันอีกครั้งในปัจจุบันด้วยการโจมตีของ GPU ที่มีราคาถูกมากครอบครัว SHA เป็นตัวเลือกที่ไม่ดีอย่างมากสำหรับการแฮ็กรหัสผ่าน (เร็วเกินไป) แม้จะเป็นเกลือก็ตาม ใช้ bcrypt, scrypt หรือ PBKDF2
Eran Medan

11
ทำไมคำถามนี้ปิด นี่เป็นคำถามสำหรับปัญหาทางวิศวกรรมจริงและคำตอบนั้นมีค่ามาก OP ไม่ได้ขอห้องสมุดเขากำลังถามวิธีแก้ปัญหาทางวิศวกรรม
stackoverflowuser2010

12
น่าอัศจรรย์มาก คำถามนี้มี 52 upvotes และบางคนตัดสินใจที่จะปิดเป็น "นอกหัวข้อ"
stackoverflowuser2010

1
ใช่ฉันโพสต์ใน Meta เกี่ยวกับปัญหาการปิดครั้งนี้มาก่อน
Chris Dutrow

8
คำถามนี้ควรจะเปิดอีกครั้ง มันเป็นคำถามเกี่ยวกับวิธีการเขียนโปรแกรมเพื่อแก้ปัญหาที่อธิบายไว้ (การตรวจสอบรหัสผ่าน) ด้วยการแก้ปัญหารหัสสั้น ๆ การเห็นคำที่เรียกว่า "ไลบรารี่" ไม่ได้แสดงให้เห็นถึงการปิดคำถาม เขาไม่ได้ขอคำแนะนำจากห้องสมุดเขาถามว่าจะแฮรหัสผ่านอย่างไร แก้ไข: ตรงนั้นแก้ไขมัน
erickson

คำตอบ:


157

คุณสามารถใช้สิ่งอำนวยความสะดวกที่มีอยู่ใน Java runtime เพื่อทำสิ่งนี้ SunJCE ใน Java 6 รองรับ PBKDF2 ซึ่งเป็นอัลกอริธึมที่ดีที่จะใช้สำหรับการแฮชรหัสผ่าน

byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 65536, 128);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
System.out.printf("salt: %s%n", enc.encodeToString(salt));
System.out.printf("hash: %s%n", enc.encodeToString(hash));

นี่คือคลาสยูทิลิตี้ที่คุณสามารถใช้สำหรับการตรวจสอบรหัสผ่าน PBKDF2:

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

/**
 * Hash passwords for storage, and test passwords against password tokens.
 * 
 * Instances of this class can be used concurrently by multiple threads.
 *  
 * @author erickson
 * @see <a href="http://stackoverflow.com/a/2861125/3474">StackOverflow</a>
 */
public final class PasswordAuthentication
{

  /**
   * Each token produced by this class uses this identifier as a prefix.
   */
  public static final String ID = "$31$";

  /**
   * The minimum recommended cost, used by default
   */
  public static final int DEFAULT_COST = 16;

  private static final String ALGORITHM = "PBKDF2WithHmacSHA1";

  private static final int SIZE = 128;

  private static final Pattern layout = Pattern.compile("\\$31\\$(\\d\\d?)\\$(.{43})");

  private final SecureRandom random;

  private final int cost;

  public PasswordAuthentication()
  {
    this(DEFAULT_COST);
  }

  /**
   * Create a password manager with a specified cost
   * 
   * @param cost the exponential computational cost of hashing a password, 0 to 30
   */
  public PasswordAuthentication(int cost)
  {
    iterations(cost); /* Validate cost */
    this.cost = cost;
    this.random = new SecureRandom();
  }

  private static int iterations(int cost)
  {
    if ((cost < 0) || (cost > 30))
      throw new IllegalArgumentException("cost: " + cost);
    return 1 << cost;
  }

  /**
   * Hash a password for storage.
   * 
   * @return a secure authentication token to be stored for later authentication 
   */
  public String hash(char[] password)
  {
    byte[] salt = new byte[SIZE / 8];
    random.nextBytes(salt);
    byte[] dk = pbkdf2(password, salt, 1 << cost);
    byte[] hash = new byte[salt.length + dk.length];
    System.arraycopy(salt, 0, hash, 0, salt.length);
    System.arraycopy(dk, 0, hash, salt.length, dk.length);
    Base64.Encoder enc = Base64.getUrlEncoder().withoutPadding();
    return ID + cost + '$' + enc.encodeToString(hash);
  }

  /**
   * Authenticate with a password and a stored password token.
   * 
   * @return true if the password and token match
   */
  public boolean authenticate(char[] password, String token)
  {
    Matcher m = layout.matcher(token);
    if (!m.matches())
      throw new IllegalArgumentException("Invalid token format");
    int iterations = iterations(Integer.parseInt(m.group(1)));
    byte[] hash = Base64.getUrlDecoder().decode(m.group(2));
    byte[] salt = Arrays.copyOfRange(hash, 0, SIZE / 8);
    byte[] check = pbkdf2(password, salt, iterations);
    int zero = 0;
    for (int idx = 0; idx < check.length; ++idx)
      zero |= hash[salt.length + idx] ^ check[idx];
    return zero == 0;
  }

  private static byte[] pbkdf2(char[] password, byte[] salt, int iterations)
  {
    KeySpec spec = new PBEKeySpec(password, salt, iterations, SIZE);
    try {
      SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
      return f.generateSecret(spec).getEncoded();
    }
    catch (NoSuchAlgorithmException ex) {
      throw new IllegalStateException("Missing algorithm: " + ALGORITHM, ex);
    }
    catch (InvalidKeySpecException ex) {
      throw new IllegalStateException("Invalid SecretKeyFactory", ex);
    }
  }

  /**
   * Hash a password in an immutable {@code String}. 
   * 
   * <p>Passwords should be stored in a {@code char[]} so that it can be filled 
   * with zeros after use instead of lingering on the heap and elsewhere.
   * 
   * @deprecated Use {@link #hash(char[])} instead
   */
  @Deprecated
  public String hash(String password)
  {
    return hash(password.toCharArray());
  }

  /**
   * Authenticate with a password in an immutable {@code String} and a stored 
   * password token. 
   * 
   * @deprecated Use {@link #authenticate(char[],String)} instead.
   * @see #hash(String)
   */
  @Deprecated
  public boolean authenticate(String password, String token)
  {
    return authenticate(password.toCharArray(), token);
  }

}

11
คุณอาจต้องการระวังการแปลงไบต์เป็น hex ด้วยBigInteger: นำเลขศูนย์จะถูกลบออก ไม่เป็นไรสำหรับการดีบักอย่างรวดเร็ว แต่ฉันเห็นข้อบกพร่องในรหัสการผลิตเนื่องจากผลกระทบนั้น
โทมัส

24
@ thomas-pornin ไฮไลท์ว่าทำไมเราต้องมีห้องสมุดไม่ใช่บล็อกโค้ดที่ใกล้จะถึงแล้ว น่ากลัวว่าคำตอบที่ยอมรับไม่ตอบคำถามในหัวข้อที่สำคัญ
Nilzor

9
ใช้อัลกอริทึม PBKDF2WithHmacSHA512 ที่ขึ้นต้นด้วย Java 8 มันแข็งแกร่งขึ้นเล็กน้อย
iwan.z

1
หมายเหตุ ALGs ที่มีอยู่จะไม่ถูกลบในรุ่นที่ใหม่กว่า: java_4: PBEWithMD5AndDES, DESede, DES java_5 / 6/7: PBKDF2WithHmacSHA1, PBE (เฉพาะใน Java 5), PBEWithSHA1AndRC2_40, PBEWithSHA1And, PBEWithMD5AndTriple java_8: PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA512AndAES_128, RC4_40, PBKDF2WithHmacSHA256 , PBEWithHmacSHA1AndAES_128, RC4_128, PBKDF2WithHmacSHA224, PBEWithHmacSHA256AndAES_256, RC2_128, PBEWithHmacSHA224AndAES_256, PBEWithHmacSHA384AndAES_256, PBEWithHmacSHA512AndAES_256, PBKDF2WithHmacSHA512, PBEWithHmacSHA256AndAES_128, PBKDF2WithHmacSHA384, PBEWithHmacSHA1AndAES_256
iwan.z

4
@TheTosters ใช่เวลาดำเนินการจะนานขึ้นสำหรับรหัสผ่านที่ไม่ถูกต้อง โดยเฉพาะอย่างยิ่งรหัสผ่านที่ผิดจะใช้เวลาเดียวกันกับรหัสผ่านที่ถูกต้อง มันป้องกันการโจมตีตามจังหวะเวลาแม้ว่าฉันจะยอมรับว่าฉันไม่สามารถคิดวิธีที่ใช้ประโยชน์ได้ในการหาช่องโหว่ในกรณีนี้ แต่คุณไม่ตัดมุม เพียงเพราะฉันมองไม่เห็นมันไม่ได้หมายความว่าจิตใจที่คดเคี้ยวมากกว่าจะไม่
erickson

97

นี่คือการดำเนินการที่สมบูรณ์ด้วยสองวิธีที่ทำสิ่งที่คุณต้องการ:

String getSaltedHash(String password)
boolean checkPassword(String password, String stored)

ประเด็นก็คือแม้ว่าผู้โจมตีจะสามารถเข้าถึงทั้งฐานข้อมูลและซอร์สโค้ดของคุณรหัสผ่านยังคงปลอดภัย

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base64;

public class Password {
    // The higher the number of iterations the more 
    // expensive computing the hash is for us and
    // also for an attacker.
    private static final int iterations = 20*1000;
    private static final int saltLen = 32;
    private static final int desiredKeyLen = 256;

    /** Computes a salted PBKDF2 hash of given plaintext password
        suitable for storing in a database. 
        Empty passwords are not supported. */
    public static String getSaltedHash(String password) throws Exception {
        byte[] salt = SecureRandom.getInstance("SHA1PRNG").generateSeed(saltLen);
        // store the salt with the password
        return Base64.encodeBase64String(salt) + "$" + hash(password, salt);
    }

    /** Checks whether given plaintext password corresponds 
        to a stored salted hash of the password. */
    public static boolean check(String password, String stored) throws Exception{
        String[] saltAndHash = stored.split("\\$");
        if (saltAndHash.length != 2) {
            throw new IllegalStateException(
                "The stored password must have the form 'salt$hash'");
        }
        String hashOfInput = hash(password, Base64.decodeBase64(saltAndHash[0]));
        return hashOfInput.equals(saltAndHash[1]);
    }

    // using PBKDF2 from Sun, an alternative is https://github.com/wg/scrypt
    // cf. http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html
    private static String hash(String password, byte[] salt) throws Exception {
        if (password == null || password.length() == 0)
            throw new IllegalArgumentException("Empty passwords are not supported.");
        SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        SecretKey key = f.generateSecret(new PBEKeySpec(
            password.toCharArray(), salt, iterations, desiredKeyLen));
        return Base64.encodeBase64String(key.getEncoded());
    }
}

เรากำลังเก็บ'salt$iterated_hash(password, salt)'ของ เกลือมีขนาด 32 ไบต์แบบสุ่มและมีวัตถุประสงค์คือถ้าคนสองคนเลือกรหัสผ่านเดียวกันรหัสผ่านที่เก็บไว้จะยังคงดูแตกต่างกัน

iterated_hashที่เป็นพื้นhash(hash(hash(... hash(password, salt) ...)))ทำให้มันแพงมากสำหรับผู้บุกรุกที่มีศักยภาพที่มีการเข้าถึงฐานข้อมูลของคุณรหัสผ่านเดากัญชาพวกเขาและมองขึ้น hashes ในฐานข้อมูล คุณต้องคำนวณสิ่งนี้iterated_hashทุกครั้งที่ผู้ใช้ลงชื่อเข้าใช้ แต่ไม่เสียค่าใช้จ่ายเท่าไรเมื่อเปรียบเทียบกับผู้โจมตีที่ใช้เวลาเกือบ 100% ในการคำนวณแฮช


14
ขออภัยที่จู้จี้ แต่ทำไมฉันต้องเลือกสิ่งนี้มากกว่าห้องสมุดที่มีอยู่? ห้องสมุดอาจมีโอกาสสูงกว่าในการตรวจสอบอย่างละเอียด ฉันสงสัยว่าทุกคนใน 14 คะแนนโหวตขึ้นไปวิเคราะห์รหัสสำหรับปัญหาใด ๆ
Joachim Sauer

2
@JoachimSauer นี่เป็นเพียงแค่การใช้ไลบรารี (javax.crypto) แต่คุณก็ถูกต้อง - ไม่สนับสนุนรหัสผ่านที่ว่างเปล่า เพิ่มข้อยกเว้นเพื่อให้ชัดเจน ขอบคุณ!
Martin Konicek

3
คุณอาจจะมีการเปลี่ยนแปลงวิธีการที่ลายเซ็นแทนchar[] password String password
assylias

2
แม้ว่าดูเหมือนว่าการอ้างอิงไม่ได้รับข้อตกลงเป็นเอกฉันท์ ดูเพิ่มเติมที่: security.stackexchange.com/a/20369/12614
assylias

3
คุณแน่ใจหรือไม่ว่า. Equals () บนสตริงนั้นไม่ทำให้เกิดการลัดวงจร (เช่น: หยุดการวนซ้ำเมื่อพบสองไบต์ที่ไม่เท่ากัน) ถ้ามันมีความเสี่ยงของการโจมตีเวลารั่วไหลข้อมูลเกี่ยวกับแฮรหัสผ่าน
bobpoekert


7

คุณสามารถใช้Shiroห้องสมุด (เดิมJSecurity ) การดำเนินงานของสิ่งที่จะอธิบายโดยOWASP

นอกจากนี้ยังมีลักษณะเช่นห้องสมุด JASYPT มียูทิลิตี้ที่คล้ายกัน


นั่นคือสิ่งที่ฉันใช้ แต่เนื่องจากเราตัดสินใจที่จะไม่ใช้ Shiro มีความกังวลเกี่ยวกับความไม่มีประสิทธิภาพของการรวมไลบรารี Shiro ทั้งหมดไว้ในแพ็คเกจเดียว
Chris Dutrow

ฉันไม่รู้จักห้องสมุดที่ประกอบด้วยยูทิลิตี้การแฮชรหัสผ่าน คุณน่าจะดีกว่าการกลิ้งของคุณเองถ้าการอ้างอิงเป็นเรื่องที่น่ากังวล คำตอบโดย erickson ดูดีสำหรับฉัน หรือเพียงแค่คัดลอกรหัสจากลิงก์ OWASP นั้นฉันอ้างอิงถ้าคุณต้องการใช้ SHA ในลักษณะที่ปลอดภัย
laz

7

คุณสามารถคำนวณการใช้แฮชได้MessageDigestแต่นี่เป็นความผิดพลาดในแง่ของความปลอดภัย ไม่ควรใช้แฮชในการจัดเก็บรหัสผ่านเนื่องจากสามารถแตกหักได้ง่าย

คุณควรใช้อัลกอริทึมอื่นเช่น bcrypt, PBKDF2 และ scrypt เพื่อจัดเก็บรหัสผ่านของคุณ ดูที่นี่


3
คุณจะแฮรหัสผ่านที่เข้าสู่ระบบโดยไม่ต้องเก็บเกลือในฐานข้อมูลได้อย่างไร
ZZ Coder

9
การใช้ชื่อผู้ใช้เป็นเกลือไม่ใช่ข้อบกพร่องที่ร้ายแรง แต่ก็ไม่ได้ใกล้เคียงกับการใช้เกลือจากการเข้ารหัส RNG และไม่มีปัญหาในการจัดเก็บเกลือลงในฐานข้อมูล เกลือไม่ได้เป็นความลับ
erickson

1
ชื่อผู้ใช้และอีเมลจะไม่ถูกจัดเก็บในฐานข้อมูลด้วยหรือไม่
Chris Dutrow

@ZZ Coder @erickson ถูกต้องฉันคิดว่าจะเป็นเกลือสำหรับรหัสผ่านทั้งหมดซึ่งจะนำไปสู่ตารางรุ้งที่คำนวณได้ง่าย
Bozho

13
ปัญหาหนึ่งในการใช้ชื่อผู้ใช้ (หรือ ID อื่น ๆ เช่นอีเมล) เป็นเกลือคือคุณไม่สามารถเปลี่ยน ID โดยไม่ต้องให้ผู้ใช้ตั้งรหัสผ่านใหม่
Lawrence Dol

6

นอกจาก bcrypt และ PBKDF2 ที่กล่าวถึงในคำตอบอื่น ๆ ฉันขอแนะนำให้ดูscrypt

ไม่แนะนำให้ใช้ MD5 และ SHA-1 เนื่องจากค่อนข้างเร็วดังนั้นจึงใช้การคำนวณแบบกระจาย "เช่าต่อชั่วโมง" (เช่น EC2) หรือ GPU ระดับไฮเอนด์ที่ทันสมัยสามารถ "ถอดรหัส" รหัสผ่านโดยใช้การโจมตีแบบดุเดือด / พจนานุกรมในราคาค่อนข้างต่ำและสมเหตุสมผล เวลา.

หากคุณต้องใช้พวกเขาอย่างน้อยก็วนซ้ำอัลกอริทึมเป็นจำนวนที่สำคัญที่กำหนดไว้ล่วงหน้า (1000+)


6

เห็นด้วยอย่างยิ่งกับ Erickson ว่าPBKDF2คือคำตอบ

หากคุณไม่มีตัวเลือกนั้นหรือต้องใช้แฮชเท่านั้น Apache Commons DigestUtils นั้นง่ายกว่าการรับรหัส JCE: https://commons.apache.org/proper/commons-codec/apidocs/org/apache /commons/codec/digest/DigestUtils.html

หากคุณใช้แฮชให้ใช้ sha256 หรือ sha512 หน้านี้มีคำแนะนำที่ดีเกี่ยวกับการจัดการรหัสผ่านและการแฮช (โปรดทราบว่าไม่แนะนำการแฮชสำหรับการจัดการรหัสผ่าน): http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html


เป็นที่น่าสังเกตว่า SHA512 นั้นไม่ดีกว่า SHA256 (สำหรับจุดประสงค์นี้) เพียงเพราะจำนวนนั้นใหญ่กว่า
Azsgy

5

คุณสามารถใช้Spring Security Crypto (มีการพึ่งพาการคอมไพล์ทางเลือกเพียง2 ตัว ) ซึ่งรองรับการเข้ารหัสรหัสผ่านPBKDF2 , BCrypt , SCryptและArgon2

Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();
String aCryptedPassword = argon2PasswordEncoder.encode("password");
boolean passwordIsValid = argon2PasswordEncoder.matches("password", aCryptedPassword);
SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder();
String sCryptedPassword = sCryptPasswordEncoder.encode("password");
boolean passwordIsValid = sCryptPasswordEncoder.matches("password", sCryptedPassword);
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String bCryptedPassword = bCryptPasswordEncoder.encode("password");
boolean passwordIsValid = bCryptPasswordEncoder.matches("password", bCryptedPassword);
Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder();
String pbkdf2CryptedPassword = pbkdf2PasswordEncoder.encode("password");
boolean passwordIsValid = pbkdf2PasswordEncoder.matches("password", pbkdf2CryptedPassword);

4

ในขณะที่คำแนะนำ NIST PBKDF2ได้ถูกกล่าวถึงแล้วฉันอยากจะชี้ให้เห็นว่ามีการแข่งขันแฮ็ชรหัสผ่านสาธารณะที่วิ่งจาก 2013-2015 ในท้ายที่สุดArgon2ได้รับเลือกเป็นฟังก์ชั่นแฮชรหัสผ่านที่แนะนำ

มีการผูก Java ที่นำมาใช้ค่อนข้างดีสำหรับไลบรารีดั้งเดิม (เนทีฟ C) ที่คุณสามารถใช้ได้

ในกรณีการใช้งานโดยเฉลี่ยฉันไม่คิดว่ามันจะสำคัญจากมุมมองด้านความปลอดภัยหากคุณเลือก PBKDF2 ผ่าน Argon2 หรือในทางกลับกัน หากคุณมีข้อกำหนดด้านความปลอดภัยที่แข็งแกร่งฉันขอแนะนำให้พิจารณา Argon2 ในการประเมินผลของคุณ

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการรักษาความปลอดภัยของฟังก์ชั่นการแปลงแป้นพิมพ์รหัสผ่านดูsecurity.se


@zaph ฉันแก้ไขคำตอบเพื่อให้มีวัตถุประสงค์มากขึ้น โปรดทราบว่าคำแนะนำของ NIST อาจไม่ใช่ตัวเลือกที่ดีที่สุดเสมอ (ดูที่นี่สำหรับตัวอย่าง) - แน่นอนว่านี่เป็นเรื่องจริงสำหรับทุกสิ่งที่แนะนำให้ใช้ในที่อื่นเช่นกัน ดังนั้นฉันคิดว่าคำตอบนี้ให้คุณค่ากับคำถามนี้
คำถามที่ 3

2

ที่นี่คุณมีสองลิงก์สำหรับ MD5 hashing และวิธีแฮชอื่น ๆ :

Javadoc API: https://docs.oracle.com/javase/1.5.0/docs/api/java/security/MessageDigest.html

บทช่วยสอน: http://www.twmacinta.com/myjava/fast_md5.php


3
เพียงจำไว้ว่าการแฮ็กรหัสผ่านช้าลงจะดีกว่า คุณควรใช้ฟังก์ชันแฮชซ้ำหลายพันครั้งเป็นเทคนิค "การเพิ่มความแข็งแกร่งของคีย์" นอกจากนี้เกลือยังมีความจำเป็น
erickson

ฉันอยู่ภายใต้ความประทับใจที่อัลกอริทึมการแฮ็กซ้ำหลายครั้งจะสร้างความปลอดภัยเช่นเดียวกับการวนซ้ำหนึ่งครั้งเนื่องจากความยาวของไบต์จะยังคงเท่าเดิมหรือไม่
Chris Dutrow

@erickson มันจะดีกว่าถ้าจะชะลอการโจมตีของผู้โจมตีอย่างชัดเจน
deamon

6
เกี่ยวกับการเสริมกำลังหลัก: มีเกลือเพื่อทำให้แฮชที่คำนวณไว้ล่วงหน้าใช้ไม่ได้ แต่ผู้โจมตีไม่ต้อง precompute ผู้โจมตีสามารถแฮชสตริง + เกลือ "ได้ทันที" จนกว่าพวกเขาจะพบสิ่งที่ถูกต้อง แต่ถ้าคุณทำซ้ำหลายพันครั้งสำหรับแฮชของคุณพวกเขาจะต้องทำแบบเดียวกัน เซิร์ฟเวอร์ของคุณจะไม่ได้รับผลกระทบมากนักจากการทำซ้ำ 10k เนื่องจากไม่ได้เกิดขึ้นบ่อยครั้ง ผู้โจมตีจะต้องการพลังการประมวลผล 10k เท่า
zockman

2
@Simon วันนี้ MD5 นั้นไม่มีประโยชน์อะไรเลยสำหรับการแฮ็กรหัสผ่านเนื่องจากสามารถถอดรหัสได้ในไม่กี่วินาทีโดยใช้การโจมตีด้วย GPU / การโจมตีด้วยอาวุธเดรัจฉาน ดูที่นี่: codahale.com/how-to-safely-store-a-password
Eran Medan

1

ในบรรดารูปแบบแฮชมาตรฐานทั้งหมด LDAP ssha เป็นหนึ่งที่ปลอดภัยที่สุดที่จะใช้

http://www.openldap.org/faq/data/cache/347.html

ฉันเพิ่งจะทำตามอัลกอริทึมที่ระบุไว้ที่นั่นและใช้ MessageDigest ทำแฮช

คุณต้องเก็บเกลือไว้ในฐานข้อมูลของคุณตามที่คุณแนะนำ


1
เนื่องจาก SSHA ไม่ได้ทำซ้ำฟังก์ชันแฮชจึงเร็วเกินไป วิธีนี้ช่วยให้ผู้โจมตีสามารถลองใช้รหัสผ่านได้เร็วขึ้น อัลกอริทึมที่ดีกว่าเช่น Bcrypt, PBBKDF1 และ PBKDF2 ใช้เทคนิค "การเพิ่มความแข็งแกร่งของคีย์" เพื่อชะลอการโจมตีของผู้โจมตีจนถึงจุดที่รหัสผ่านควรหมดอายุก่อนที่พวกเขาจะสามารถเดรัจฉานแม้กระทั่งพื้นที่รหัสผ่าน 8 ตัวอักษร
erickson

ปัญหาของกลไกเหล่านี้คือคุณไม่ได้รับการสนับสนุนลูกค้า ปัญหาของรหัสผ่านที่แฮชคือคุณไม่สามารถรองรับรหัสผ่านที่แฮชกับอัลกอริทึมอื่นได้ ด้วย ssha อย่างน้อยไคลเอ็นต์ LDAP ทั้งหมดก็สนับสนุน
ZZ Coder

ไม่ใช่ "ปลอดภัยที่สุด" เป็นเพียง "ใช้งานได้ดี" bcrypt / scrypt เป็นวิธีที่รุนแรงยิ่งขึ้น ressource
eckes

1

ตั้งแต่ปี 2020 อัลกอริทึมที่น่าเชื่อถือและยืดหยุ่นที่สุดในการใช้งาน

สิ่งที่น่าจะเป็นไปได้มากที่สุดในการเพิ่มความแข็งแรงของฮาร์ดแวร์

เป็นArgon2idหรือArgon2i

มันมีเครื่องมือการสอบเทียบที่จำเป็นในการค้นหาพารามิเตอร์ความแข็งแรงที่ได้รับการปรับให้เหมาะสมที่สุดตามเวลาการแฮชเป้าหมายและฮาร์ดแวร์ที่ใช้

  • Argon2iเป็นผู้เชี่ยวชาญในหน่วยความจำโลภ hashing
  • Argon2dเป็นผู้เชี่ยวชาญด้าน CPU โลภ hashing
  • Argon2idใช้ทั้งสองวิธี

การแปลงหน่วยความจำโลภหน่วยความจำจะช่วยต่อต้านการใช้ GPU ในการถอดรหัส

การรักษาความปลอดภัยในฤดูใบไม้ผลิ / การใช้งานปราสาท Bouncy ไม่ได้รับการปรับให้เหมาะสม cf: เอกสารเกี่ยวกับฤดูใบไม้ผลิ

การนำไปใช้ในปัจจุบันใช้ปราสาท Bouncy ซึ่งไม่ใช้ประโยชน์จากการขนาน / การปรับให้เหมาะสมที่แคร็กเกอร์รหัสผ่านจะทำดังนั้นจึงมีความไม่สมมาตรที่ไม่จำเป็นระหว่างผู้โจมตีและผู้พิทักษ์

การดำเนินงานที่น่าเชื่อถือมากที่สุดในการใช้งาน Java เป็นmkammerer 's หนึ่ง

wrapper jar / library ของการใช้งานภาษาพื้นเมืองอย่างเป็นทางการที่เขียนใน Rust

มันเขียนได้ดีและใช้งานง่าย

เวอร์ชันแบบฝังมีการสร้างดั้งเดิมสำหรับ Linux, Windows และ OSX

ตัวอย่างเช่นมันถูกใช้โดย jpmorganchase ในโครงการความปลอดภัยของtessera ที่ใช้เพื่อรักษาความปลอดภัยQuorumซึ่งเป็นการนำ Ethereum cryptocurency ไปปฏิบัติ

นี่คือตัวอย่างโค้ดจาก tessera

การปรับเทียบอาจทำได้โดยใช้ de.mkammerer.argon2.Argon2Helper # findIterations

อัลกอริทึม SCRYPT และ Pbkdf2 อาจถูกปรับเทียบโดยการเขียนเกณฑ์มาตรฐานบางอย่าง แต่ค่าการทำซ้ำที่ปลอดภัยน้อยที่สุดในปัจจุบันจะต้องใช้เวลาในการแฮชที่สูงขึ้น


0

ฉันเอนกายลงจากวิดีโอบน udemy และแก้ไขให้เป็นรหัสผ่านแบบสุ่มที่แรงขึ้น

}

private String pass() {
        String passswet="1234567890zxcvbbnmasdfghjklop[iuytrtewq@#$%^&*" ;

        char icon1;
        char[] t=new char[20];

         int rand1=(int)(Math.random()*6)+38;//to make a random within the range of special characters

            icon1=passswet.charAt(rand1);//will produce char with a special character

        int i=0;
        while( i <11) {

             int rand=(int)(Math.random()*passswet.length());
             //notice (int) as the original value of Math>random() is double

             t[i] =passswet.charAt(rand);

             i++;
                t[10]=icon1;
//to replace the specified item with icon1
         }
        return new String(t);
}






}

ฉันเปิดให้มีการแก้ไข แต่ฉันคิดว่าคุณไม่ควรใช้ตัวเลขแบบสุ่มเมื่อทำการแฮช นี่คือฟังก์ชันแฮชของคุณที่กำหนดไว้ นั่นคือถ้าคุณแฮ็คสตริงหลาย ๆ ครั้งคุณจะได้ค่าแฮชเดียวกันกลับมาสำหรับสตริงนั้น
duldi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.