放弃使用电子邮件声明进行用户标识或授权
本文旨在为其应用程序当前正在使用不安全模式的开发人员提供指导。在该模式下,电子邮件声明用于授权,这可能会导致其他用户完全接管帐户。 继续阅读,以详细了解应用程序是否受到影响,以及修正步骤。
如何知道我的应用程序是否收到影响?
Microsoft 建议查看应用程序源代码并确定是否存在以下模式:
- 可变声明,例如使用
email
来唯一标识用户 - 可变声明,例如使用
email
来授权用户访问资源
这些模式被认为是不安全的,因为没有预配邮箱的用户可以为其邮件(主要 SMTP)属性设置任何电子邮件地址。 此属性不能保证来自经过验证的电子邮件地址。 使用未经验证的域所有者的电子邮件声明进行授权时,任何没有预配邮箱的用户都可能通过更改其邮件属性来模拟其他用户以获得未经授权的访问权限。
在以下情况下,电子邮件被视为经过域所有者验证:
- 域属于用户帐户所在的租户,并且租户管理员已对域进行验证
- 电子邮件来自 Microsoft 帐户 (MSA)
还应注意,SAML/WS-Fed 帐户没有已验证的域。
这种未经授权的访问风险仅在多租户应用中发现,因为一个租户的用户可以通过修改其邮件属性来提升其权限,以访问其他租户中的资源。
如何立即保护应用程序?
为了保护应用程序避免发生未经验证的电子邮件地址方面的错误,所有新的多租户应用程序都将自动选择采用新的默认行为,即自 2023 年 6 月起从令牌中删除未经验证域所有者的电子邮件地址。 以前使用域所有者未经验证的电子邮件地址进行登录活动的单租户应用程序和多租户应用程序不会启用此行为。
根据你的情况,你可以确定应用程序的令牌应继续接收未经验证的电子邮件。 你可以通过在 Microsoft Graph 中的应用程序 API 的 authenticationBehaviors 对象中设置 removeUnverifiedEmailClaim
属性来禁用默认行为,虽然对于大多数应用程序都不建议这样做。
通过将 removeUnverifiedEmailClaim
设置为 false
,应用程序将收到可能未经验证的 email
声明,并且使用户面临帐户接管风险。 如果为了不中断用户登录流而禁用此行为,强烈建议尽快迁移到唯一标识令牌声明映射,如以下指南中所述。
识别不安全的配置和执行数据库迁移
切勿将可变声明(例如 email
、preferred_username
等)用作标识符,以执行授权检查或在数据库中为用户编制索引。 这些值可以重复使用,并且可能导致应用程序收到特权升级攻击。
以下伪代码示例有助于说明用户标识/授权的不安全模式:
// Your relying party (RP) using the insecure email claim for user identification (or authorization)
MyRPUsesInsecurePattern()
{
// grab data for the user based on the email (or other mutable) attribute
data = GetUserData(token.email)
// Create new record if no data present (This is the anti-pattern!)
if (data == null)
{
data = WriteNewRecords(token.email)
}
insecureAccess = data.show // this is how an unverified user can escalate their privileges via an arbirarily set email
}
确定应用程序依赖于此不安全属性后,需要更新业务逻辑,以根据全局唯一标识符 (GUID) 对用户重新编制索引。
多租户应用程序应根据两个唯一标识声明的映射 tid
+ oid
编制索引。 这将按 tid
细分租户,并按 oid
细分用户。
使用 xms_edov
可选声明确定电子邮件验证状态并迁移用户
为了帮助开发人员完成迁移过程,我们引入了可选声明 xms_edov
,这是一个布尔值属性,用于指示是否已验证电子邮件域所有者。
xms_edov
可用于帮助验证用户的电子邮件,然后再将其主键迁移到唯一标识符,例如 oid
。 以下伪代码示例演示了如何在迁移过程中使用此声明。
// Verify email and migrate users by performing lookups on tid+oid, email, and xms_edov claims
MyRPUsesSecurePattern()
{
// grab the data for a user based on the secure tid + oid mapping
data = GetUserData(token.tid + token.oid)
// address case where users are still indexed by email
if (data == null)
{
data = GetUserData(token.email)
// if still indexed by email, update user's key to GUID
if (data != null)
{
// check if email domain owner is verified
if (token.xms_edov == false)
{
yourEmailVerificationLogic()
}
// migrate primary key to unique identifier mapping (tid + oid)
data.UpdateKeyTo(token.tid + token.oid)
}
// new user, create new record with the correct (secure) key
data = WriteNewRecord(token.sub)
}
secureAccess = data.show
}
迁移到全局唯一映射可确保主要使用不能重复使用或滥用来模拟其他用户的值对每个用户编制索引。 根据全局唯一标识符为用户编制索引后,即可修复使用 email
声明的任何潜在授权逻辑。
使用适当的声明验证更新授权逻辑
如果应用程序出于授权目的使用 email
(或任何其他可变声明),则应阅读通过验证声明保护应用程序和 API 并实施适当的检查。
后续步骤
- 要详细了解如何安全地使用基于声明的授权,请参阅《通过验证声明保护应用程序和 API》
- 有关可选声明的详细信息,请参阅可选声明参考