Auth0安全防护全指南:抵御身份攻击的最佳实践

本指南深入探讨Auth0安全防护的五大关键领域,包括配置管理、账户欺诈防护、凭证攻击防御、MFA绕过防护和会话令牌劫持防护,提供详细的技术配置和代码示例。

抵御身份攻击的Auth0安全综合指南

在本综合指南中,我们收集了大量安全建议,以强化Auth0租户,应对当前Auth0客户(及其他用户)日常面临的身份威胁环境。

五大重点领域

当今身份领域存在五个主要领域需要Auth0客户解决:

  • 错误配置
  • 账户创建欺诈
  • 针对凭证的攻击
  • MFA绕过
  • 会话和令牌劫持

在AI时代,这些攻击向量大多数时候是由机器人和僵尸网络、住宅代理以及网络钓鱼基础设施即服务驱动的高速自动化攻击。

作为主要原则,从身份防御的角度,我们鼓励客户应用两种有效实践:

  • 应用来自身份行业和Auth0特定建议的正确配置和最佳实践
  • 提高检测防护能力,以便在安全态势变化时能够响应

错误配置:Auth0防御的第一道防线

导致安全风险的错误配置既适用于租户/身份提供商级别,也适用于应用程序级别。不适当的会话管理、令牌验证和令牌存储都是应用程序端错误配置的例子。

获取当前安全相关配置快速快照的最佳方法是运行Checkmate for Auth0,该工具根据标准、指南和最佳Auth0安全实践审查您的租户配置。

授权码授予与PKCE 应该是您OAuth2.1授予的默认选择,即使对于常规Web应用程序也是如此,以减轻授权码注入攻击的风险。

1
2
3
4
5
6
exports.onExecutePostLogin = async (event, api) => {
  // 强制执行PKCE - 如果未定义code_challenge则撤销
  if (!event.request.query.code_challenge) {  
    api.session.revoke("PKCE is enforced");
  }
};

隐式授予流 已弃用,应避免使用,转而使用带有PKCE的授权码流。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
exports.onExecutePostLogin = async (event, api) => {
  const responseType = event.request.query.response_type;
  // 当响应模式未在请求中设置时,默认为url片段
  const responseMode = event.request.query.response_mode || 'url';

  // 不允许没有form_post的隐式授予
  if((responseType && (responseType.includes('token') ||
       responseType.includes('id_token'))) && 
       (!responseMode || responseMode !== 'form_post')){
          api.session.revoke('Login is not allowed. Contact support.');
          console.log(`Application ${event.client.client_id} applies insecure grant:
                       response_type=${responseType} and 
                       response_mode=${responseMode}`);
       }
};

攻击防护:解决账户创建欺诈和针对凭证攻击的共同点

Auth0提供的重要防御层是一组由Auth0机器人检测主导的攻击防护功能。

账户创建欺诈

欺诈性注册通常伴随着直接的金融欺诈,如操纵信用卡信息、消耗/收集注册激励或注册SMS MFA以执行收费欺诈。

在大多数情况下,电子邮件是您的业务与用户之间的主要连接和通信渠道。我们也可以利用它来诊断可疑行为。

1
2
3
4
5
exports.onExecutePostLogin = async (event, api) => {
  if(!event.user.email_verified){
    api.session.revoke("You cannot login without verified your email");
  }
};

处理低信誉电子邮件域:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
exports.onExecutePreUserRegistration = async (event, api) => {
  const DENY_DOMAIN_LIST = ["drrieca.com", "dependity.com"];
  // 提取域名
  const emailDomain = event.user.email?.split('@').pop();

  if (DENY_DOMAIN_LIST.includes(emailDomain)) {
    api.access.deny('Registration denied. Contact our support center.');
    console.log(`Registration denied for user ${event.user.email}. 
                  Domain '${emailDomain}' is on the deny list.`);
  }
};

针对凭证的攻击

针对凭证的攻击主要风险是账户接管,导致消费者直接财务损失。

密码重置流

当然,当密码用作主要身份验证方法时,强密码策略对整体安全性贡献很大。

默认密码重置流的弱点是将100%的责任委托给电子邮件箱,因此电子邮件成为最薄弱的环节。

为应对此风险,Auth0提供了密码重置流触发器和自定义MFA的强大组合。

启用通行密钥

针对凭证攻击的首要缓解措施是通过启用通行密钥来最小化凭证。第二步是用MFA备份密码,最好是强MFA,如WebAuthn。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
exports.onExecutePostLogin = async (event, api) => {
  // 可选的标志来组合两种方法
  const denyPwdForAll = false;
  const denyPwdPerUser = true;

  const nameOfPasskeyEnabledConnection = 'Username-Password-Authentication';
  // 获取用于身份验证的方法
  const methods = event.authentication?.methods;
  const ifPwdUsed = methods?.some(method => method.name === 'pwd')

  // 方法1:对整个连接拒绝
  if (denyPwdForAll && 
      event.connection.name == nameOfPasskeyEnabledConnection){
    if(ifPwdUsed){
      api.session.revoke('Please, login with passkeys');
    }
  }

  // 方法2:基于每个用户拒绝
  if (denyPwdPerUser && ifPwdUsed){
    const ManagementClient = require('auth0').ManagementClient;

    const management = new ManagementClient({
        domain: event.secrets.domain,
        clientId: event.secrets.clientId,
        clientSecret: event.secrets.clientSecret,
    });

    const params =  { include_totals: true};
    const id = event.user.user_id;

    try {
      const res = await management.users.authenticationMethods.list(id, params)
      if (res.response.total > 0){ 
          const authenticators = res.response?.authenticators
          
          const ifPasskeyPresent = authenticators?.some(authenticator => 
                authenticator.type === 'passkey');
          // 如果用户注册了通行密钥,拒绝使用密码登录
          if(ifPasskeyPresent){
            api.session.revoke('Please, login with passkeys');
          }
      }
    } catch (e) {
      console.log(e)
      // 处理错误
    }
  }
};

MFA绕过

SIM交换、推送通知疲劳和OTP网络钓鱼——一旦启用了防网络钓鱼因素,这些都会消失。

客户身份领域面临的一个重大挑战是向用户引入额外摩擦的逻辑障碍。为应对此,Auth0支持两种MFA实现范式:

  • 逐步认证基于资源保护理念
  • 自适应MFA保护会话

会话和令牌劫持

会话和令牌劫持可能导致冒充,从而提供未经授权访问和账户接管的机会。

现代身份验证流中使用了三种主要类型的令牌:ID令牌、访问令牌和刷新令牌。

令牌劫持

在令牌方面,首要防御是遵循OAuth2.1最佳实践,例如默认使用带有PCKE的授权码授予以避免ID和访问令牌泄漏,并应用刷新令牌轮换来保护相应的刷新令牌。

会话劫持

在我们继续讨论会话劫持的响应之前,重要的是要理解涉及的不只是一个会话:

  • 应用程序会话:完全在您的控制之下
  • Auth0会话:在Auth0控制下,通过会话管理API控制
  • 身份提供商(IdP)会话:当涉及外部IdP时为可选
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
exports.onExecutePostLogin = async (event, api) => {
  const sessionInitialASN = event.session?.device?.initial_asn;
  const sessionCurrentASN = event.request.asn;

  const created = Date.parse(event.session?.created_at ?? "");
  // 读取应用程序元数据中定义的计时器
  const lifetimeWhenRisky = event.client.metadata.session_lifetime_when_risky;
  const inactivityWhenRisky = event.client.metadata.session_inactivity_when_risky;

  // 如果有ASN变化
  if (sessionInitialASN &&
      sessionCurrentASN &&
      sessionInitialASN != sessionCurrentASN) {
        if (event.user.app_metadata.isAdmin){
          // 对管理员严格 - 直接撤销会话
          api.session.revoke('Invalid ASN change')
        }else {
          // 否则最小化会话持续时间并禁用持久性
          api.session.setCookieMode('non-persistent');
          if (event.session?.id && 
              lifetimeWhenRisky && 
              inactivityWhenRisky) {
              api.session.setExpiresAt(created + Number(lifetimeWhenRisky));
              api.session.setIdleExpiresAt(created + Number(inactivityWhenRisky));
          }
        }
  }
};

后续步骤:这只是开始

保护客户环境不是目的地,而是持续的旅程。随着威胁环境的发展,我们鼓励Auth0客户探索新功能。

此外,探索Auth0检测目录以构建您自己丰富的安全中心。

本指南到此结束,但您的安全旅程不会停止!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计