데이터 보호: 비밀번호 해싱

등록일시: 2017-04-07 08:00,  수정일시: 2017-08-09 10:01
조회수: 4,419
이 문서는 ASP.NET Core 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
본문에서는 PBKDF2 알고리즘을 이용해서 비밀번호 해싱을 수행하는 KeyDerivation.Pbkdf2 메서드에 관해서 살펴봅니다.

데이터 보호 코드 베이스에는 암호화 키 파생 함수를 제공해주는 Microsoft.AspNetCore.Cryptography.KeyDerivation 패키지가 포함되어 있습니다. 이 패키지는 독립적인 구성 요소로서 데이터 보호 시스템의 나머지 다른 부분들에 의존하지 않습니다. 이 패키지는 완벽히 독립적으로 사용이 가능합니다. 다만 편의상 소스가 데이터 보호 코드 베이스와 함께 위치해 있을 뿐입니다.

이 패키지는 PBKDF2 알고리즘을 이용해서 비밀번호 해싱을 수행하는 KeyDerivation.Pbkdf2 메서드를 제공합니다. 이 API는 .NET 프레임워크의 기존 Rfc2898DeriveBytes 형식과 매우 비슷하지만, 세 가지 중요한 차이점이 존재합니다:

  1. KeyDerivation.Pbkdf2 메서드는 다양한 PRF들을 사용할 수 있는 반면 (현재 HMACSHA1, HMACSHA256, 그리고 HMACSHA512를 지원합니다), Rfc2898DeriveBytes 형식은 HMACSHA1만 지원합니다.

  2. KeyDerivation.Pbkdf2 메서드는 현재 운영 체제를 감지해서 가장 최적화된 구현 루틴을 선택하기 때문에 상황에 따라 훨씬 향상된 성능을 제공해줍니다. (Windows 8에서는 Rfc2898DeriveBytes보다 약 10배에 가까운 성능을 보여줍니다.)

  3. KeyDerivation.Pbkdf2 메서드는 호출자가 모든 매개변수를 지정해야만 합니다 (솔트, PRF, 그리고 반복 횟수까지). 반면 Rfc2898DeriveBytes 형식은 이에 대한 기본값들을 제공해줍니다.

using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;

public class Program
{
    public static void Main(string[] args)
    {
        Console.Write("Enter a password: ");
        string password = Console.ReadLine();

        // generate a 128-bit salt using a secure PRNG
        byte[] salt = new byte[128 / 8];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(salt);
        }
        Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");

        // derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
        string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
            password: password,
            salt: salt,
            prf: KeyDerivationPrf.HMACSHA1,
            iterationCount: 10000,
            numBytesRequested: 256 / 8));
        Console.WriteLine($"Hashed: {hashed}");
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter a password: Xtw9NMgx
 * Salt: NZsP6NnmfBuYeJrrAKNuVQ==
 * Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
 */

실제 사용 사례를 살펴보려면 ASP.NET Core Identity의 PasswordHasher 형식의 소스 코드를 참고하시기 바랍니다.