PostgreSQL용 해시 비밀번호 생성

3058 단어 dotnetfsharppostgres
이것은 신속하지만 매우 유용할 수 있습니다. 배경으로 혹시 모르니 비밀번호를 저장해두는 것은 매우 좋지 않은 생각입니다. 첫 번째 변경 사항은 암호의 해시를 저장하는 것입니다. 해시는 일부 바이트가 주어지고 결정론적으로 다른 일부 바이트를 생성하는 함수일 뿐이지만 다른 방식으로 진행하기는 매우 어렵습니다. 즉, password_hash = hash(password)가 있는 경우(여기서 password는 사용자의 일반 텍스트 암호임) password에서 password_hash를 쉽게 파생할 수 없습니다.

이 때문에 암호 해시만 저장하면 됩니다. 사용자가 암호를 보내면 해시하고 해시가 일치하는지 확인합니다. 좋은 해싱 알고리즘의 경우 두 개의 암호가 동일한 해시를 생성할 확률은 매우 적습니다. 데이터베이스가 손상되면 공격자는 암호가 아닌 해시만 얻습니다.

이로 인해 일반적인 해싱 기능에 대한 많은 일반적인 암호에 대한 해시를 미리 계산하는 "레인보우 테이블 공격"이 발생했습니다. 비밀번호 해시 덤프를 받으면 레인보우 테이블에 해시가 있는지 확인할 수 있습니다. 표시되는 모든 해시에 대해 이제 암호를 얻었습니다.

이에 대한 방어를 "소금"이라고합니다. salt라는 임의의 문자열을 생성하고 salted_hash = hash(password + salt)를 계산합니다(문자열/배열 연결 포함). 그런 다음 saltsalted_hash 를 저장하므로 사용자가 로그인할 때 계산을 반복할 수 있습니다. 솔트가 유출되더라도 괜찮습니다. 여기서 우리가 실제로 방어하는 유일한 것은 레인보우 테이블이기 때문입니다. 공격자는 가능한 모든 솔트와 연결된 일반 암호의 해시를 미리 계산할 수 없습니다.

어쨌든 Postgres를 사용하면 사용자를 만들 때 소금에 절인 해시 암호를 제공할 수 있습니다. 다른 사람의 시스템에 해시된 암호를 제공하는 문제는 그들이 수행하는 것과 정확히 동일한 단계를 따라야 한다는 것입니다. 이 모든 노래와 춤은 동일한 암호에서 동일한 해시를 재현할 수 있다는 것입니다. 제공한 해시가 약간 잘못되면 로그인이 실패합니다.

나는 Postgres 암호 해싱 알고리즘을 재현하는 방법(특히 Postgres 및 Npgsql 소스를 통해 파헤침)을 알아내기 위해 너무 많은 파기를 수행했기 때문에 이것이 동일한 문제에서 누군가를 구할 수 있기를 바랍니다.

let password_hash (password: string) =
    let normalized = System.Text.Encoding.UTF8.GetBytes(password.Normalize(System.Text.NormalizationForm.FormKC))
    let salt_len = 16
    let default_iterations = 4096

    let salt = System.Security.Cryptography.RandomNumberGenerator.GetBytes(salt_len)
    let mutable salt1 = Array.create (salt.Length + 4) 0uy

    let hmac = new System.Security.Cryptography.HMACSHA256(normalized)
    System.Buffer.BlockCopy(salt, 0, salt1, 0, salt.Length)
    salt1[salt1.Length - 1] <- 1uy

    let mutable hi = hmac.ComputeHash(salt1)
    let mutable u1 = hi

    for _ in 1 .. default_iterations - 1 do
        let u2 = hmac.ComputeHash(u1)
        for i in 0 .. hi.Length - 1 do
            hi[i] <- hi[i] ^^^ u2[i]
        u1 <- u2

    let client_key = (new System.Security.Cryptography.HMACSHA256(hi)).ComputeHash(System.Text.Encoding.UTF8.GetBytes("Client Key"))
    let stored_key = (System.Security.Cryptography.SHA256.Create()).ComputeHash(client_key)
    let server_key = (new System.Security.Cryptography.HMACSHA256(hi)).ComputeHash(System.Text.Encoding.UTF8.GetBytes("Server Key"))

    let builder = new System.Text.StringBuilder()
    builder.Append("'SCRAM-SHA-256$").Append(default_iterations.ToString()).Append(":").Append(System.Convert.ToBase64String(salt)).Append("$")
        .Append(System.Convert.ToBase64String(stored_key)).Append(":").Append(System.Convert.ToBase64String(server_key)).Append("'").ToString()


그런 다음 CREATE ROLE 쿼리 내에서 다음을 사용합니다.

"ENCRYPTED PASSWORD " + password_hash password

좋은 웹페이지 즐겨찾기