此WebService安全方案有哪些潜在问题?
我们有一项基于用户名和密码来处理授权的服务。代替将用户名和密码作为调用的一部分,我们将其放置在SOAP标头中。
在典型情况下,Web服务在执行开始时会调用授权服务,以检查是否允许调用者调用它。问题在于这些Web服务中的某些服务相互调用,这意味着在每个子调用中都要检查用户的权限,这可能会非常昂贵。
我想做的是让授权服务在第一个调用之后返回安全令牌。然后,Web服务可以不必在本地调用安全标头,而不必每次都调用授权服务。
安全标题看起来像这样(修剪了Ccode以说明基本概念):
public sealed class SecurityHeader : SoapHeader { public string UserId; // Encrypted public string Password; // Encrypted; Just realized this field isn't necessary [thanks CJP] public DateTime TimeStamp; // Used for calculating header Expiry public string SecurityToken; }
总体思路是,每次调用都会检查SecurityHeader。如果存在,并且尚未过期,并且SecurityToken有效,则Web方法将正常进行。否则,它将返回错误,或者将尝试重新授权并生成新的SecurityHeader
SecurityToken基于UserId,Password和TimeStamp的盐化哈希值。每天都要更换盐,以防止重播。
我确实看到的一个问题是,用户可能有权访问Web Service A,但没有访问Web Service B的权限。如果他呼叫A并收到一个安全令牌,就目前的情况而言,这意味着B如果他使用,将允许他通过相同的令牌。我必须对其进行更改,以便安全令牌仅在Web Service到Web Service之间有效,而在User to Web Service范围内有效。如果用户调用A调用B,则应该确定,但如果用户先调用Service A然后再调用Service D,则应该确定。一种解决方法是将一个公用密钥(或者一组密钥)分配给逻辑相关的服务。 (即,如果客户可以做A,那么在逻辑上他也可以做B)。
或者,我必须将用户的整个权限集编码为安全标头的一部分。我将不得不调查开销是多少。
编辑:
好几个人提到过要研究其他安全方案,例如WS-Security和SAML等。我已经有了。实际上,我是从WS-Security获得这个想法的。问题在于其他方案没有提供我需要的功能(在没有中间数据库的情况下缓存授权信息并防止重放)。如果有人知道这样做的方案,那么我会乐意使用它。另外,这与身份验证无关。这是由我无法控制的另一种机制处理的。
如果事实证明没有办法缓存授权数据,则意味着我只需要承担每个级别的授权开销。
解决方案
就个人而言,只要我们有一个集中化的基础框架来支持SecurityToken值的验证,我就看不到我们有任何问题。
好的,希望将我的旧答案替换为更好的答案。
如果我们有办法在服务之间安全地共享数据,那么我们所描述的内容应该会起作用。例如,如果服务与授权服务共享一个秘密密钥,则可以使用此密钥获取帮助。
顺便说一句,我不知道足够的密码学来说明添加秘密盐+散列是否足够安全(尽管看起来还不错);我敢肯定,使用秘密或者私有密钥对HMAC来说是安全的。旋转密钥是一个好主意,因此我们仍将拥有一个主密钥并传播一个新的签名密钥。
方法存在的其他问题是(a)我们正在对每个服务中的哈希逻辑进行硬编码,并且(b)服务可能希望从授权服务中获取更详细的数据,而不仅仅是"是/否"答案。例如,我们可能希望授权服务插入该用户属于角色A和B但不属于角色C的标头中。
或者,我们可以让授权服务使用其拥有的任何有趣信息创建一个新的标头,然后对该块进行签名。
在这一点上,我们正在讨论单一登录实施。我们已经了解WS-Security规范。我描述的标头听起来很像SAML断言。
这是有关使用WS-Security和SAML进行单点登录的文章。
现在,我不知道我们是否需要所有这些……还有中间的解决方案。例如,授权服务可以对原始用户名块进行签名;如果我们担心公钥/私钥的加密性能,并且可以共享私钥,则也可以使用私钥代替公钥/私钥进行签名。
方案的根本问题是我们没有使用标准框架或者实现。这与方案本身的任何优点无关。
原因很简单,安全性(尤其是密码学)非常非常复杂,几乎不可能正确解决。使用可靠,易于理解和证明的通用工具。
有关WS-Security的更多信息,请参见:http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wss。
大多数框架(.NET / JavaEE等)都将在一定程度上为WS-Security提供内置支持。
如果我们认为方案在某种程度上要比标准更好,我建议我们将其编写为论文,并提交给同行评审(以及参考实现),但不要使用它来保护应用程序的安全。
编辑以响应OP编辑:
我认为我们有点混淆了身份验证和授权的角色,这很容易实现...
方案中的安全令牌(或者类似证书)的作用基本上是对消息的发件人进行身份验证,即他们应该是谁。正如我们正确指出的那样,身份验证并不暗示要授予发送者访问哪些基础资源。
授权是我们采用经过身份验证的发件人并应用一组权限以便可以限制访问范围的过程。通常,框架默认情况下不会进行授权,我们必须通过创建某种形式的ACL或者通过扩展某种"安全管理器"类型的界面来启用它。
实际上,其想法是"身份验证"层告诉我们谁在尝试访问页面A,然后由我们决定该人是否有权访问页面A。
我们绝不应该在消息本身中存储有关权利和权限的信息,接收者应该针对每条消息针对其ACL(或者数据库或者其他)验证权限。如果有人弄清楚如何修改邮件,这将限制曝光。
首先,阅读@CJP的帖子,他提出了一个很好的观点。
如果我们想继续自己动手制作(也许我们确实有充分的理由),我将提出以下几点:
- 我们在谈论的是身份验证服务,而不是授权服务。只是为了确保我们知道自己在说什么...?
- 其次,我们需要将盐(不是秘密)和密钥(是)分开。我们应该有一个带键的哈希(例如HMAC)以及盐(或者带键的带盐的哈希。听起来像个三明治)。如前所述,盐不是秘密的,但应针对每个令牌进行更改,并且可以将其包含在标头中;钥匙必须是秘密的,每天都要更换是件好事。当然,请确保我们使用的是强哈希(例如SHA-256),适当的密钥管理技术等。
再次,我敦促我们重新考虑自己滚动,但是如果我们必须自己出去...