gpt4 book ai didi

delphi - 在 Windows 中存储密码的安全方法

转载 作者:行者123 更新时间:2023-12-03 14:38:19 27 4
gpt4 key购买 nike

我正在尝试保护包含敏感信息的本地数据库(类似于 this question,仅适用于 delphi 2010)

我正在使用DISQLite component ,它确实支持 AES 加密,但我仍然需要保护我用来解密和读取数据库的密码。

我最初的想法是生成一个随 secret 码,使用 DPAPI 之类的东西存储它(Crypt32.dll 中的 CryptProtectDataCryptUnprotectData 函数),但我不能找到 Delphi 的任何示例

我的问题是:如何安全地存储随机生成的密码?或者,假设DPAPI道路是安全的,我如何在Delphi中实现这个DPAPI?

最佳答案

最好使用Windows'DPAPI 。它比使用其他方法安全得多:

  • CryptProtectData/CryptProtectMemory
  • CryptUnprotectData/CryptUnprotectMemory

CryptProtectMemory/CryptUnprotectMemory 提供更大的灵 active :

  • CRYPTPROTECTMEMORY_SAME_PROCESS:只有您的进程才能解密您的数据
  • CRYPTPROTECTMEMORY_CROSS_PROCESS:任何进程都可以解密您的数据
  • CRYPTPROTECTMEMORY_SAME_LOGON:只有使用同一用户且在同一 session 中运行的进程才能解密数据

优点:

  1. 无需 key - Windows do it for you
  2. 精细控制:每个进程/每个 session /每次登录/每台机器
  3. CryptProtectData 存在于 Windows 2000 及更高版本中
  4. DPAPI Windows 比使用由你、我和那些相信 Random() 返回绝对随机数的人编写的“安全”相关代码更安全:) 事实上,微软在安全领域拥有数十年的经验,受到最多的攻击曾经的操作系统:o)

缺点:

  1. 对于 CRYPTPROTECTMEMORY_SAME_PROCESS One* 可以在您的进程中注入(inject)一个新线程,并且该线程可以解密您的数据
  2. 如果有人重置(而不是更改)用户密码,您将无法解密您的数据
  3. 对于 CRYPTPROTECTMEMORY_SAME_LOGON:如果用户*运行被黑客入侵的进程,它就可以解密您的数据
  4. 如果您使用 CRYPTPROTECT_LOCAL_MACHINE - 该计算机上的每个用户*都可以解密数据。 This is why it's not recommended to save passwords in .RDP files
  5. Known issues

注意:“每个用户”是指拥有使用 DPAPI 的工具或技能的用户

无论如何 - 你有选择。

请注意,@David-Heffernan 是对的 - 存储在计算机上的任何内容都可以解密 - 从内存中读取它,在进程中注入(inject)线程等。

另一方面……我们为什么不让 cookies 的生活变得更艰难呢? :)

经验法则:使用后清除所有包含敏感数据的缓冲区。这并不会让事情变得 super 安全,但会降低你的内存包含敏感数据的可能性。当然,这并不能解决另一个主要问题:其他 Delphi 组件如何处理您传递给它们的敏感数据:)

Security Library by JEDI采用面向对象的 DPAPI 方法。此外,JEDI 项目还包含 DPAPI (JWA IIRC) 的已翻译 Windows header

更新:以下是使用 DPAPI 的示例代码(使用 JEDI API ):

Uses SysUtils, jwaWinCrypt, jwaWinBase, jwaWinType;

function dpApiProtectData(var fpDataIn: tBytes): tBytes;
var
dataIn, // Input buffer (clear-text/data)
dataOut: DATA_BLOB; // Output buffer (encrypted)
begin
// Initializing variables
dataOut.cbData := 0;
dataOut.pbData := nil;

dataIn.cbData := length(fpDataIn); // How much data (in bytes) we want to encrypt
dataIn.pbData := @fpDataIn[0]; // Pointer to the data itself - the address of the first element of the input byte array

if not CryptProtectData(@dataIn, nil, nil, nil, nil, 0, @dataOut) then
RaiseLastOSError; // Bad things happen sometimes

// Copy the encrypted bytes to RESULT variable
setLength(result, dataOut.cbData);
move(dataOut.pbData^, result[0], dataOut.cbData);
LocalFree(HLOCAL(dataOut.pbData)); // http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261(v=vs.85).aspx
// fillChar(fpDataIn[0], length(fpDataIn), #0); // Eventually erase input buffer i.e. not to leave sensitive data in memory
end;

function dpApiUnprotectData(fpDataIn: tBytes): tBytes;
var
dataIn, // Input buffer (clear-text/data)
dataOut: DATA_BLOB; // Output buffer (encrypted)
begin
dataOut.cbData := 0;
dataOut.pbData := nil;

dataIn.cbData := length(fpDataIn);
dataIn.pbData := @fpDataIn[0];

if not CryptUnprotectData(
@dataIn,
nil,
nil,
nil,
nil,
0, // Possible flags: http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx
// 0 (zero) means only the user that encrypted the data will be able to decrypt it
@dataOut
) then
RaiseLastOSError;

setLength(result, dataOut.cbData); // Copy decrypted bytes in the RESULT variable
move(dataOut.pbData^, result[0], dataOut.cbData);
LocalFree(HLOCAL(dataOut.pbData)); // http://msdn.microsoft.com/en-us/library/windows/desktop/aa380882%28v=vs.85%29.aspx
end;

procedure testDpApi;
var
bytesClearTextIn, // Holds input bytes
bytesClearTextOut, // Holds output bytes
bytesEncrypted: tBytes; // Holds the resulting encrypted bytes
strIn, strOut: string; // Input / Output strings
begin

// *** ENCRYPT STRING TO BYTE ARRAY
strIn := 'Some Secret Data Here';

// Copy string contents to bytesClearTextIn
// NB: this works for STRING type only!!! (AnsiString / UnicodeString)
setLength(bytesClearTextIn, length(strIn) * sizeOf(char));
move(strIn[1], bytesClearTextIn[0], length(strIn) * sizeOf(char));

bytesEncrypted := dpApiProtectData(bytesClearTextIn); // Encrypt data

// *** DECRYPT BYTE ARRAY TO STRING
bytesClearTextOut := dpApiUnprotectData(bytesEncrypted); // Decrypt data

// Copy decrypted bytes (bytesClearTextOut) to the output string variable
// NB: this works for STRING type only!!! (AnsiString / UnicodeString)
setLength(strOut, length(bytesClearTextOut) div sizeOf(char));
move(bytesClearTextOut[0], strOut[1], length(bytesClearTextOut));

assert(strOut = strIn, 'Boom!'); // Boom should never booom :)

end;

注释:

  • 该示例是使用 CryptProtectData/CryptUnprotectData 的轻量级版本;
  • 加密是面向字节的,因此使用 tBytes 更容易(tBytes = 字节数组);
  • 如果输入和输出字符串都是UTF8String,则删除“* sizeOf(char)”,因为UTF8String的char只有1个字节
  • 使用CryptProtectMemory/CryptUnProtectMemory类似

关于delphi - 在 Windows 中存储密码的安全方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13145112/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com