安全注意事项:Windows 用户界面

本主题提供有关 Windows 用户界面中的安全注意事项的信息。 本主题只提供部分需要了解的安全问题。 相反,请将其用作该技术领域的起点和参考。

随着计算机互连性的增强,开发人员现在必须关注应用程序安全性。 然而,安全性也增强了一般应用程序的安全性和可靠性;因此,这是开发人员提供良好用户体验的另一种方式。 以下主题讨论使用 Windows 用户界面时的一些潜在安全问题。

字符串注意事项

许多函数、消息和宏在其参数中都会使用字符串。 但通常不会检查字符串的 null 终止字符或长度。 一个相关的问题是,错误计算字符串或缓冲区的长度。 无论哪种情况,这都可能导致缓冲区溢出或数据被截断,从而对你的应用程序产生不利影响。 有关缓冲区溢出和其他安全问题的详细信息,请参阅编写安全代码,作者:Michael Howard 和 David Leblanc,Microsoft Press,2002 年。

若要以安全方式处理字符串,应执行以下操作:

  • 根据需要检查字符串的 null 终止字符或长度是否正确。
  • 确定字符串或缓冲区的长度时要特别小心,尤其是当它包含 TCHAR 值时
  • 如果要创建字符串或使用以前使用过的字符串,请将其初始化为零或根据需要插入 null 终止符。

此外,处理字符串时请考虑使用 StrSafe 函数。 这些函数旨在安全地处理字符串。

用户输入

Windows 用户界面涉及获取和响应用户输入的信息。 但如果用户输入错误数据,则可能会破坏你的应用程序,无论他们是否有意这样做。 因此,基本规则是所有输入都必须经过验证。

字符串数据应为主要关注内容,这在字符串注意事项中进行了讨论。 然而,所有类型的输入在被应用程序使用之前都应经过验证。 另一个问题是,数据在某个时间点经过验证但在使用之前发生更改,例如,接收给出文本长度的消息时。 因此,如果数据有可能发生更改,应在使用数据之前对其进行检查。

安全警报

下表列出了如果使用不当可能会危及应用程序安全性的功能。

功能 缓解
GetAtomName 指定缓冲区的大小时请小心。
GlobalGetAtomName 全局字符串原子可供任何应用程序访问。 但是,如果另一个应用程序不小心,它可能会错误地处理它们的引用计数并删除它们。 应考虑使用全局整数原子。
ImpersonateDdeClientWindow 如果此函数失败,则后续客户端请求将在调用进程的安全上下文中发出。 如果调用进程作为高特权帐户运行,这可能会出现问题。 因此,如果调用失败或引发错误,请不要继续执行客户端请求。
DdeImpersonateClient 如果此函数失败,则后续客户端请求将在调用进程的安全上下文中发出。 如果调用进程作为高特权帐户运行,这可能会出现问题。 因此,如果调用失败或引发错误,请不要继续执行客户端请求。
GetClipboardFormatName 错误计算 lpszFormatName 缓冲区的正确大小,尤其是当应用程序同时用于 ANSI 和 Unicode 版本时,可能会导致缓冲区溢出。 另请注意,如果字符串比 cchMaxCount 参数长,则会被截断,这可能会导致信息丢失
GetMenuString lpString 参数是 TCHAR 缓冲区,nMaxCount 是 TCHAR 中菜单字符串的长度。 错误地调整这些参数的大小可能生成菜单字符串的字符长度。 错误地调整这些参数的大小可能会导致字符串被截断,从而可能导致数据丢失。
GetStringTypeAGetStringTypeExGetStringTypeW 为了避免缓冲区溢出,请正确设置 lpCharType 缓冲区的大小
LoadLibrary 错误地使用 LoadLibrary 可能会加载错误的 DLL,从而影响应用程序的安全性
LoadString 不正确的使用包括在 nBufferMax 参数中指定错误的大小。 例如,sizeof(lpBuffer) 指定缓冲区的大小(以字节为单位),这可能会导致该函数的 Unicode 版本发生缓冲区溢出。 缓冲区溢出情况是应用程序中许多安全问题的原因。 在这种情况下,使用 sizeof(lpBuffer)/sizeof(TCHAR) 可提供缓冲区的正确大小。
lstrcat 此函数使用结构化异常处理 (SEH) 来捕获访问冲突和其他错误。 当此函数捕获 SEH 错误时,它会返回 NULL,而不以 null 终止字符串,以及不返回 NULL,不以 null 终止字符串,并且不通知调用方错误。 调用方如果假设空间不足是错误条件,就不安全。 第一个参数 lpString1 必须足够大以容纳 lpString2 和结尾的“\0”,否则可能会发生缓冲区溢出。 如果发生访问冲突,缓冲区溢出可能会导致针对应用程序的拒绝服务攻击。 在最坏的情况下,缓冲区溢出可能允许攻击者将可执行代码注入到你的进程中,特别是当 lpString1 是基于堆栈的缓冲区时。 请考虑使用以下替代方法之一。 StringCbCatStringCchCat
lstrcpy 此函数使用结构化异常处理 (SEH) 来捕获访问冲突和其他错误。 当此函数捕获 SEH 错误时,它会返回 NULL,而不以 null 终止字符串,以及不返回 NULL,不以 null 终止字符串,并且不通知调用方错误。 调用方如果假设空间不足是错误条件,就不安全。 第一个参数 lpString1 必须足够大以容纳 lpString2 和结尾的“\0”,否则可能会发生缓冲区溢出。 如果发生访问冲突,缓冲区溢出可能会导致针对应用程序的拒绝服务攻击。 在最坏的情况下,缓冲区溢出可能允许攻击者将可执行代码注入到你的进程中,特别是当 lpString1 是基于堆栈的缓冲区时。 请考虑改用 StringCchCopy
lstrcpyn 此函数使用结构化异常处理 (SEH) 来捕获访问冲突和其他错误。 当此函数捕获 SEH 错误时,它会返回 NULL,而不以 null 终止字符串,以及不返回 NULL,不以 null 终止字符串,并且不通知调用方错误。 调用方如果假设空间不足是错误条件,就不安全。 如果 lpString1 不够大,无法容纳复制的字符串,则可能会发生缓冲区溢出。 另外,在复制整个字符串时,请注意 sizeof 返回字节数而不是 WCHAR,sizeof 返回字节数而不是字符数,这对于该函数的 Unicode 版本是不正确的。 如果发生访问冲突,缓冲区溢出可能会导致针对应用程序的拒绝服务攻击。 在最坏的情况下,缓冲区溢出可能允许攻击者将可执行代码注入到你的进程中,特别是当 lpString1 是基于堆栈的缓冲区时。 请考虑改用 StringCchCopy
lstrlen lstrlen 假定 lpString 是一个以 null 结尾的字符串。 如果不是,这可能会导致缓冲区溢出或针对你的应用程序的拒绝服务攻击。 请考虑使用以下替代方法之一。 StringCbLengthStringCchLength
wsprintf lpOut 中返回的字符串不保证以 null 终止。 另外,请避免使用 %s 格式,这可能导致缓冲区溢出。 如果发生访问冲突,则会导致针对你的应用程序的拒绝服务攻击。 在最糟糕的情况下,攻击者可能会注入可执行代码。 请考虑使用以下替代方法之一。 StringCbPrintfStringCbPrintfExStringCbVPrintfStringCbVPrintfExStringCchPrintfStringCchPrintfExStringCchVPrintfStringCchVPrintfEx
wvsprintf lpOutput 中返回的字符串不保证以 null 终止。 另外,请避免使用 %s 格式,这可能导致缓冲区溢出。 如果导致访问冲突,则可能导致拒绝服务攻击,或者攻击者可能会注入可执行代码。 请考虑使用以下替代方法之一。 StringCbPrintfStringCbPrintfExStringCbVPrintfStringCbVPrintfExStringCchPrintfStringCchPrintfExStringCchVPrintfStringCchVPrintfEx

 

Microsoft 安全性

安全和标识

安全性操作方法索引

Microsoft Security Response Center(Microsoft 安全响应中心)

安全性 API 的最佳做法