消耗 IDataProtectionProvider 的组件必须向 CreateProtector 方法传递一个唯一的 用途 参数。 目的 参数 固有于数据保护系统的安全性,因为它在加密使用者之间提供隔离,即使根加密密钥相同。
当使用者指定用途时,目的字符串与根加密密钥一起使用,以派生该使用者特有的加密子项。 这将隔离使用者与应用程序中所有其他加密使用者:其他组件无法读取其有效负载,并且无法读取任何其他组件的有效负载。 这种隔离也使针对组件的整个攻击类别变得不可行。
在上图中, IDataProtector 实例 A 和 B 无法 读取彼此的有效负载,只能读取自己的负载。
目的字符串不必是机密。 它只需简单地是唯一的,也就是说,其他行为良好的组件永远不会提供相同的目的字符串。
小窍门
使用数据保护 API 的组件的命名空间和类型名称是一个很好的经验法则,实际上此信息永远不会发生冲突。
由 Contoso 创建的负责生成持有者令牌的组件可能会使用 Contoso.Security.BearerToken 作为其目的字符串。 或者, 甚至更好 - 它可能会使用 Contoso.Security.BearerToken.v1 作为其用途字符串。 追加版本号允许将来的版本使用 Contoso.Security.BearerToken.v2 作为其用途,并且在有效负载方面,不同的版本将彼此完全隔离。
由于目的参数 CreateProtector 是字符串数组,因此可以改为指定上述参数 [ "Contoso.Security.BearerToken", "v1" ]。 这允许建立目标的层次结构,并且使得数据保护系统可能实现多租户方案。
警告
组件不应允许不受信任的用户输入成为目的链的唯一输入源。
例如,请考虑负责存储安全消息的组件 Contoso.Messaging.SecureMessage。 如果安全消息传送组件调用 CreateProtector([ username ]),则恶意用户可能会创建用户名为“Contoso.Security.BearerToken”的帐户,试图让组件调用 CreateProtector([ "Contoso.Security.BearerToken" ]),这可能会无意中导致安全消息系统生成被视为身份验证令牌的有效负载。
消息传送组件的更好的用途链是 CreateProtector([ "Contoso.Messaging.SecureMessage", $"User: {username}" ]),它提供适当的隔离。
由IDataProtectionProvider、IDataProtector和目的提供的隔离及其行为如下:
对于给定的
IDataProtectionProvider对象,CreateProtector方法将创建一个IDataProtector对象,该对象唯一绑定到创建它的IDataProtectionProvider对象以及传入该方法的用途参数。目的参数不得为 null。 (如果目的指定为数组,则表示该数组不得为零长度,数组的所有元素必须为非 null。在技术上允许空字符串用途,但不建议这样做。
如果两个用途参数包含相同的字符串(使用序号比较器),则两个目的参数是等效的,前提是它们包含相同的字符串(使用序号比较器)。 单个用途参数等效于相应的单元素用途数组。
IDataProtector如果两个对象是从具有等效用途参数的等效IDataProtectionProvider对象创建的,则两个对象是等效的。对于给定的
IDataProtector对象,调用Unprotect(protectedData)将在且仅在protectedData := Protect(unprotectedData)对于等效的IDataProtector对象时返回原始的unprotectedData对象。
注释
我们不考虑某些组件有意选择一个已知与另一个组件冲突的用途字符串的情况。 此类组件实质上被视为恶意组件,并且此系统不打算在恶意代码已在工作进程内运行时提供安全保证。