65.9K
CodeProject 正在变化。 阅读更多。
Home

CSecureEditEx - 更安全的编辑控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (11投票s)

2005年4月17日

6分钟阅读

viewsIcon

89070

downloadIcon

1960

CSecureEditEx 控件能抵御密码查看器。密码不会在进程内存中可见。

目录

  • 引言
  • 特性和局限性
  • 使用控件
  • 技术背景/工作原理?
  • 参考文献
  • 版本历史

    引言

    几乎所有涉及密码的 Windows 应用程序都使用标准的 Windows 编辑控件,并设置了密码标志。不幸的是,这些控件默认并不那么安全。本文旨在改进其安全性。

    在 Windows 95 和 98 中,应用程序可以通过发送 WM_GETTEXT 消息来获取编辑控件中的密码,就像获取普通编辑控件中的文本一样。这本身并没有什么问题,但 Windows 允许您将 WM_GETTEXT 消息发送到其他应用程序。您只需将此类消息发送到所有其他窗口,即可轻松获取所有密码。

    在 Windows NT/2000/XP 中,此 bug 已被修复。您无法再使用 WM_GETTEXT 消息从其他应用程序中获取密码(当控件所属的应用程序发送该消息时仍然有效)。但是,有些人开发了新的方法来获取敏感文本。例如,一种方法是通过 Windows 钩子将某些代码注入目标进程。注入的代码随后就位于目标应用程序的进程内存中,并能够调用 WM_GETTEXT 消息。然后,代码可以对密码执行任何它想要的操作。有关此方法的实现细节,请参阅 [1]。因此,Windows NT/2000/XP 中的密码编辑控件也不够安全。

    在我之前的文章 [2] 中,我已经解决了这个问题。现在,CSecureEditEx 控件更进一步:进程内存保护。

    Windows 标准编辑控件和我之前的 CSecureEdit 控件都将密码以纯文本形式存储在进程内存中。我之前的类保护了控件的窗口:WM_GETTEXT 消息无法工作。但是,即使是改进后的控件,仍然将密码以纯文本形式存储在内存中。

    如果 Windows 将您的进程缓存到交换文件中,那么对于这些控件来说,您就运气不佳了:密码会以纯文本形式写入交换文件,可能在那里停留数天。当然,攻击者要从交换文件中找到密码并不容易,但为什么不让他更难呢?新的 CSecureEditEx 控件做到了这一点:即使 Windows 将您的进程缓存到磁盘,密码也不会以纯文本形式可见。

    您会发现 CSecureEditEx 控件不仅引入了新的进程内存保护,还带来了一些限制。这是我发布新文章而不是仅仅更新 CSecureEdit 控件的主要原因。

    特性和局限性

    CSecureEditEx 控件的特性

    • 许多可用的窗口监视器都无法读取输入的密码。
    • 删除 ES_PASSWORD 样式不会影响 CSecureEditEx 控件。
    • CSecureEditEx 控件看起来像普通的 Windows 编辑控件。
    • 用户可以在任何位置插入字符(例如,使用光标键)。
    • 用户可以删除字符。
    • 用户可以粘贴文本到 CSecureEditEx 控件中。
    • 用户无法从 CSecureEditEx 中复制文本。
    • 按下 Shift-HomeShift-End 会清除控件的内容。
    • 实现和使用非常简单。

    限制

    • 无法进行选择。用户无法在编辑控件中选择字符范围。
    • 您的 CString DDX 函数到该控件将不再工作。

    选择的限制是内部处理按键和编辑控件更改的自然结果。如果允许选择,控件就无法确定发生了哪个部分文本的更改或删除。

    但是,既然我们是在制作密码编辑控件,选择也不是那么必需的。用户通常会在注意到输入了错误的按键时删除所有输入的文本。为此,大多数人使用 Shift-Home 来选择整个文本。正是这个组合会清除整个控件,所以实际上没有实际可用性的缺点。

    使用控件

    使用 CSecureEditEx 控件非常简单。只需按照以下步骤操作:

    1. SecureEditEx.cppSecureEditEx.h 文件集成到您的项目中。
    2. 在应该使用新控件的对话框/视图等的头文件中包含 SecureEditEx.h
    3. 将控件的 CEdit 变量替换为 CSecureEditEx
    4. 确保您的密码编辑控件设置了 ES_PASSWORD(在资源编辑器中)。

    完成!这就是您需要做的所有事情!不需要额外的初始化或其他操作。

    最后一步是可选的。如果您不设置此标志,安全性不会降低,但某些上下文菜单操作(如复制文本)将可能可用(它们将无法工作,因此最好将这些项目显示为灰色)。我建议启用 ES_PASSWORD 样式。

    那么,如果 WM_GETTEXT 消息不再有效,如何从控件中获取输入的密码?DDX 到 CString 也不再有效。您必须使用以下成员函数:

    LPTSTR CSecureEditEx::GetPassword()

    这会将输入的文本作为 LPTSTR 指针返回。要对其进行操作时请务必小心!不要仅仅将其复制到其他位置,否则所有安全改进都会丢失!当然,您可以例如使用单向哈希函数(SHA-1)对文本进行哈希处理并存储哈希值。

    处理完密码后,您需要释放指针,即安全地擦除并释放内存!使用以下成员函数来完成此操作:

    void CSecureEditEx::DeletePassword(LPTSTR lpPassword)

    GetPassword() 函数返回的 LPTSTR 指针传递给此函数。它将安全地擦除并释放内存。lpPassword 指针在函数执行后将无效。

    大多数情况下,您会想设置一个默认密码。WM_SETTEXT 消息不起作用,您需要调用以下函数来设置当前密码:

    void CSecureEditEx::SetPassword(LPCTSTR lpPassword)

    这将当前密码设置为 lpPassword 指针指向的字符串。如果将 NULL 作为参数传递,则编辑控件会被清除(即没有文本)。

    技术背景/工作原理?

    CSecureEditEx 内部维护一个指向单个 TCHAR 的指针数组。这些 TCHAR 与一个在类构造函数中随机生成的固定字符进行 XOR 运算。因此,字符不仅分布在进程内存中,还额外与某个随机字符进行了 XOR 运算。

    当用户按下按键时,控件会查看发生了什么变化。

    如果控件的内容比之前短,则用户删除了某些内容。在这种情况下,它会获取当前光标位置并计算删除了多少个字符。然后它会释放内部内存指针(首先将单个字符设置为 0)并从指针列表中删除它们。

    如果字符比之前多,则提取并添加这些字符:首先为字符分配内存,与相应的值进行 XOR 运算,然后将指向此位置的指针插入指针列表。

    正如您所看到的,控件使用光标位置来查看发生了什么。为了使这正常工作,用户不得选择任何字符范围。如果他这样做了,我们就无法正确确定操作和更改的字符。因此,控件会阻止所有选择方法(使用 Shift 和光标键进行选择,使用鼠标进行选择等)。

    要清除整个控件,用户只需按下 Shift-HomeShift-End。这会清除控件。

    参考文献

    1. Brian Friesen:PasswordSpy - 使用 Windows 钩子检索丢失的密码
    2. Dominik Reichl:安全编辑控件 - 能够抵御密码查看器的安全编辑控件。

    版本历史

    • 2005-04-16:v1.0

      初始发布。

  • © . All rights reserved.