#c# #.net #sql-server #tsql #encryption
#c# #.net #sql-сервер #tsql #шифрование
Вопрос:
У меня есть база данных, которая в настоящее время использует AES 128. База данных содержит около 8 миллионов записей, и клиент хочет вместо этого расшифровать пароли и хэшировать их, чтобы пароли не могли быть расшифрованы. Это веб-приложение с данными, хранящимися на удаленном сервере. Я пытался использовать веб-приложение для выполнения преобразования, но время ожидания истекает. Поскольку это 8 миллионов, потребуется некоторое время, чтобы просмотреть все элементы, поэтому моей следующей идеей было заставить SQL выполнять расшифровку и хеширование. Я мог бы позволить ему работать в течение следующих нескольких дней.
Проблема, с которой я сталкиваюсь, заключается в том, что каждый столбец содержит зашифрованный пароль с уникальной солью. Я не могу найти функцию для расшифровки пароля с использованием зашифрованного пароля и соли. Есть ли функция? Даже сторонний? Есть ли лучший способ сделать это?
Спасибо!
Ответ №1:
Самый простой / единственный способ сделать это в SQL Server — написать определяемую пользователем функцию CLR (UDF) на C #. Смотрите
- SQL Server 2005: Создание вашего первого C # CLR UDF за 10 простых шагов (один из которых включает в себя вечеринки)
- SQLCLR — Создать определяемую пользователем функцию CLR (UDF) — Проверить ограничение на столбец адреса электронной почты с помощью регулярного выражения
- MSDN: пользовательские функции CLR (ADO.NET )
для получения более подробной информации. Если бы это был я, я бы добавил новый столбец, содержащий хэш нового пароля, и периодически запускал инструкцию update для создания нового хэша пароля, что-то вроде этого:
update top 10000 dbo.users
set hashedPassword = DecryptAndHash( encryptedPassword )
where hashedPassword is null
где DecryptAndHash()
находится ваш UDF CLR. Как только преобразование будет завершено, вы должны быть свободны удалить старый столбец и развернуть обновление, чтобы использовать новую логику аутентификации.
Вероятно, вы хотите поместить триггер в таблицу, чтобы синхронизировать хэш с зашифрованным паролем на случай, если кто-нибудь изменит свой пароль, пока все это происходит.
ЧЕРТ возьми, код не должен быть намного сложнее, чем
using System;
using Microsoft.SqlServer.Server;
namespace Sandbox
{
public static class EncryptionFunctions
{
/// <summary>
/// Encrypts a string
/// </summary>
/// <param name="plainText"></param>
/// <returns>varbinary</returns>
[SqlFunction]
public static byte[] Encrypt( string plainText )
{
byte[] cipherText ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
cipherText = cipher.Encrypt( plainText ) ;
}
return cipherText ;
}
/// <summary>
/// Decrypts a previously encrypted varbinary
/// </summary>
/// <param name="cipherText"></param>
/// <returns>string</returns>
[SqlFunction]
public static string Decrypt( byte[] cipherText )
{
string plainText ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
plainText = cipher.Decrypt( cipherText ) ;
}
return plainText ;
}
/// <summary>
/// Compute the secure hash of a [plaintext] string
/// </summary>
/// <param name="plainText"></param>
/// <returns> varbinary </returns>
[SqlFunction]
public static byte[] SecureHash( string plainText )
{
byte[] hash ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
hash = cipher.ComputeSecureHash( plainText ) ;
}
return hash ;
}
/// <summary>
/// Convenience wrapper method to take a previously encrypted string, decrypt it and compute its secure hash
/// </summary>
/// <param name="cipherText"></param>
/// <returns>varbinary</returns>
[SqlFunction]
public static byte[] DecryptAndHash( byte[] cipherText )
{
byte[] hash ;
using ( EncryptionEngine cipher = EncryptionEngine.GetInstance() )
{
hash = cipher.ComputeSecureHash( cipher.Decrypt( cipherText ) ) ;
}
return hash ;
}
/// <summary>
/// The core encrypt/decrypt/hash engine
/// </summary>
private class EncryptionEngine : IDisposable
{
/// <summary>
/// get an instance of this class
/// </summary>
/// <returns></returns>
public static EncryptionEngine GetInstance()
{
return new EncryptionEngine() ;
}
#region IDisposable Members
/// <summary>
/// Dispose of any unmanaged resources
/// </summary>
public void Dispose()
{
throw new NotImplementedException();
}
#endregion
/// <summary>
/// Encrypt a plaintext string
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
internal byte[] Encrypt( string plainText )
{
throw new NotImplementedException();
}
/// <summary>
/// Decrypt an encrypted string
/// </summary>
/// <param name="cipherText"></param>
/// <returns></returns>
internal string Decrypt( byte[] cipherText )
{
throw new NotImplementedException();
}
/// <summary>
/// Compute the secure hash of a string
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
internal byte[] ComputeSecureHash( string plainText )
{
throw new NotImplementedException();
}
}
}
}
Реализация внутренних компонентов EncryptionEngine
оставлена в качестве упражнения для читателя.
Комментарии:
1. Даже не знал, что они существуют. Я думаю, что это отличное решение. Спасибо!
2. Однако вам нужен SQL Server 2005 или 2008. Если вам не повезло работать с устаревшими версиями SQL Server, вы SOL.
Ответ №2:
Вы можете взглянуть на аутентификацию вашего приложения и увидеть из исходного кода, как оно проверяет подлинность пароля. Там вы должны увидеть, что приложение шифрует пароль и сравнивает его с зашифрованным значением в базе данных. Функцию шифрования там должно быть легко отменить. Соль обычно не используется вместе с шифрованием, она используется при генерации хэша для защиты от атак поиска.
Я не думаю, что SQL может выполнять расшифровку на AES128, во всяком случае, не простым способом. Но вы можете написать простой .NET app использует стандартные API, которые расшифровывают каждый пароль, хэшируют его с помощью соли и записывают обратно в базу данных.
Ответ №3:
Суть хранения зашифрованных паролей в том, что они не могут быть расшифрованы. Шифрование фактически выполняется на некоторой константе ( соль) с использованием пароля в качестве ключа.
Итак, в основном цель уже достигнута, вы не можете расшифровать «пароли», чтобы получить их текстовые версии.
Комментарии:
1. Зашифрованные пароли могут быть расшифрованы (если использовалось симметричное шифрование). Вы путаете хэшированный пароль с зашифрованным паролем.