方法返回的值有多安全?

时间:2012-11-16 20:48:59

标签: c# security .net-3.5

我正在为应用程序做一些基本的安全工作。用户登录并通过活动目录验证其凭据。在程序中某些时候用户请求更改导致程序重新启动。由于这不是单个实例程序,我只需启动另一个实例并关闭当前实例,一切都很好。

但是,用户不满意每次重新启动时都必须重新登录。因此,我使用SecureString将一些基本安全性拼凑在一起以将密码存储在应用程序中,然后如果应用程序重新启动,它会解密密码并使用Rijndael算法的实现重新加密(来自codeproject的非常基本的)。然后它将用户名和加密密码作为命令行参数传递给正在启动的新实例。 (需要进行加密,因为在任何“wmic进程”调用时,他们都会显示密码。)新实例对其进行解密并再次以静默方式对活动目录进行验证,然后将其作为正常情况下的SecureString存储。

我对一般安全实践不太熟悉,但我对从解密方法返回密码的部分感到有点紧张。它本身并没有存储在任何变量中,而是在活动目录验证请求中进行调用。但是我仍然不确定它是否可以在内存中访问,或者它只是挂在寄存器中。

这不需要是有史以来最大的安全性,对于任何能够轻松访问内存内容并查看以明文形式保存的密码的人来说,这只是一种沮丧。

非常感谢!

3 个答案:

答案 0 :(得分:4)

你的攻击模型是什么?

  1. 如果您认为服务器归攻击者所有,那么您就输了 所有情况。
  2. 如果您认为服务器是安全的,则不需要任何服务器 加密。
  3. 所以我想我的建议是放弃你正在做的所有服务器端加密,因为你认为服务器是安全的。没有人可以访问你的服务器的内存,如果有人你可能会被拥有。

答案 1 :(得分:1)

在.NET应用程序中,函数返回值被推送到"评估堆栈",它位于进程内的受保护内存中。但是,您正在谈论一个字符串,而这是一个引用类型,因此评估堆栈上的内容是指向该字符串在堆上的位置的指针。堆内存相对不安全,因为它可以共享,并且因为只要GC不认为需要收集它就会存在,这与高度不稳定的评估或调用堆栈不同。但是,要访问堆内存,必须共享该内存,并且您的攻击者必须拥有一个拥有操作系统和CLR权限的应用程序才能访问该内存,并知道该在哪里查看。

如果攻击者具有此类访问权限,则可以通过 更轻松的方式从计算机获取明文密码。键盘记录程序可以查看输入的密码,或者另一个窥探者可以在GDI UI的非托管侧观察实际句柄,并查看实际显示在Windows GUI中的文本框获取明文值(' s只在显示屏上混淆了)。所有这些都没有试图破解.NET的代码访问安全性或受保护的内存。

如果攻击者拥有这种控制权,那么你就输了。因此,这应该是第一道防线;确保客户端计算机上没有此类恶意软件,并且用户尝试登录的客户端应用程序实例未被破解的类似替换。

就实例之间的模糊密码存储而言,如果你担心mem-snooping,像Rijndael这样的对称算法就无法防范。如果您的攻击者可以看到客户端计算机的内存,他就知道用于加密它的密钥,因为您的应用程序需要知道它才能解密它;因此它将被硬编码到客户端应用程序中,或者它将被存储在安全字符串附近。同样,如果您的攻击者具有这种控制权,那么如果您在客户端进行身份验证,则会丢失。

相反,我会在物理和电子安全的计算机上使用服务层来提供您的应用程序的任何功能,如果被攻击者误用(主要是数据检索/修改),这些功能对您有害。该服务层既可用于验证,也可用于授权用户执行客户端应用程序允许的任何内容。

请考虑以下事项:

  • 用户将其凭据输入您的客户端应用程序。这些凭据可以与AD凭证相同,但不会这样使用。防止键盘记录程序或其他恶意软件看到此问题的唯一方法是通过强制执行良好的AV软件,确保计算机上不存在此类恶意软件。
  • 客户端应用程序通过WCF连接到您的服务端点。端点可以使用X.509证书进行签名;不是NSA级别的安全性,但至少你可以确信你正在和你控制的服务器通话。
  • 然后,客户端应用程序会使用生成大型摘要的内容(如SHA-512)对用户密码进行哈希处理。这本身并不安全;它太快了,用户密码的熵太低,以防止攻击者破解哈希。但是,他们必须再次控制计算机以查看哈希值,并且我们将进一步混淆它。
  • 客户端应用通过WCF频道传输客户端计算机的用户名,密码和硬件ID。
  • 服务器获取这些凭据。请注意,服务器没有获得明文密码;这是有原因的。
  • 服务器将散列密码切换为256位一半。然后,前半部分是BCrypted(使用配置为适当慢的实现; 10或11"轮次"通常会这样做),并与用户数据库中的散列值进行比较。如果它们匹配,则DB返回用户的AD凭证,其使用密码散列的另一半对称加密。这就是永远不会发送明文密码的原因;服务器不必知道它,但是攻击者为了从用户数据库的被盗副本中获取任何有意义的东西。
  • 服务器解密AD凭证,将它们提交给AD,并接收表示该用户的身份和安全上下文的IPrincipal。 IPrincipal实施将包含零信息,可用于破解用户的帐户。
  • 服务器生成一个加密随机的128位值,连接128位硬件GUID,并将其与SHA512进行哈希处理。它使用该哈希的一半来对称地加密用于解密AD凭证的密钥值。然后它将另一半BC BC,并将该哈希存储在加密密钥旁边。
  • 然后,服务器通过安全WCF信道发回三条信息; AD生成的IPrincipal,未散列的128位随机值("传输令牌"),以及另一个任意长度的加密随机值("会话令牌")。
  • 客户端应用程序现在在客户端进行身份验证,这意味着您可以通过询问ADrincipal for AD角色成员身份来控制用户对代码的访问,并且服务器现在也确信拥有会话令牌的用户是真实用户。在进一步调用服务(数据检索/持久性)时,客户端应使用协商的WCF通道,并传递其会话令牌。 WCF通道和会话令牌的组合是一次性且唯一的;在新通道上使用旧令牌,或在同一通道上传递错误令牌,表示会话已被泄露。最重要的是,客户端或服务器中随时随地存储的持久数据都不能用于获取AD凭据和进行身份验证。

现在,当您的客户端应用程序关闭时,所有"会话状态&#34>在客户端和服务器之间丢失;会话令牌对任何其他协商通道无效。所以,你已经失去了身份验证;连接的下一个客户可以是任何人,无论他们是谁。这就是"转移令牌"进来:

  • "转移令牌"是一个免费回传到系统。这是一次性的,如果在发布后18小时未使用,则到期。
  • 客户端应用程序在关闭时会将两条信息传递给新实例(但是它会选择这样做);登录的用户名和"转移令牌"。
  • 客户端应用程序的新实例获取这两个信息,并获取客户端计算机的硬件ID。它与WCF服务协商安全连接,并传递这三条信息。
  • 如果用户上次登录的时间超过18小时(不是24小时,那么他们无法在昨天之前显示一分钟并重新启动应用程序),或者如果您想要真正偏执,那么更多超过8小时前,该应用程序立即返回错误,该帐户的转移令牌已过期。
  • 服务获取传输令牌,连接硬件ID,SHA-512s,BCrypts一半,并将结果与​​存储的第二个验证值进行比较。只有传输令牌和上次登录的计算机的正确组合才会产生正确的哈希值。如果匹配,则使用散列的另一半来解密密钥,然后解密AD信息。
  • 然后服务就像用户提供了应用程序密码哈希,解密AD信息,检索IPrincipal,生成新的传输令牌,会话令牌以及重新加密AD数据的密钥一样。
  • 如果此进程的任何部分失败(尝试使用不正确的令牌,包括使用相同的令牌两次,或使用来自其他计算机或其他用户的令牌),该服务将报告该凭据无效。然后,客户端应用程序将回退到标准用户密码验证。

在这里擦;这个系统依靠一个秘密密码,除了用户的心灵之外没有任何持久密码,没有后门;管理员无法检索客户端应用程序丢失的密码。此外,AD凭证(必须更改时)只能在客户端应用程序中更改;在Windows登录时,用户无法通过AD本身强制更改其密码,因为这样做会破坏他们进入客户端应用程序所需的身份验证方案(加密的凭据将不再起作用,并且客户端应用程序需要凭据来重新加密新的凭据)。如果您以某种方式在AD中拦截此验证,并且客户端的应用凭据是AD凭据,您可以自动更改用户应用中的凭据,但现在您使用一组凭据来混淆了同一套凭证,如果知道了这个秘密,你就会被清除。

最后,这种安全系统的变体仅仅依据一个原则;服务器当前没有被攻击者攻陷。有人可以进入,下载离线数据,然后他们就会卡住;但是如果他们可以安装一些内容来监控内存或流量,那么你就会受到冲击,因为当凭据(用户名/密码哈希或传输令牌/硬件ID)进入并经过验证时,攻击者现在有了解密密钥用户的AD凭据。通常情况下,客户端从不发送解密密钥,只发送散列密码的验证一半,然后服务器发回加密的凭证;但是,您正在考虑客户端比服务器更大的安全风险,所以只要这是真的,最好尽可能在客户端保留尽可能少的明文。

答案 2 :(得分:0)

看看这个: Password Salt (Wikipedia)

总结方法:

  1. 如果要将密码P存储到磁盘,请生成随机字符串S(称为salt),然后存储元组(S,SHA1Hash(P + S))。
  2. 如果要根据存储的密码检查密码尝试P',请将SHA1Hash(P'+ S)与存储的哈希值进行比较。
  3. 当您传递此密码尝试时,您只能传递散列版本,即SHA1Hash(P'+ S)。