文档首页/ 统一身份认证服务 IAM/ 最佳实践/ IAM身份提供商安全配置最佳实践
更新时间:2026-10-10 GMT+08:00

IAM身份提供商安全配置最佳实践

本最佳实践仅针对IAM旧版控制台中基于SAML协议的虚拟用户SSO和基于OIDC协议的虚拟用户SSO这两种配置进行指导说明,不包含基于SAML协议的IAM用户SSO,也不包含IAM新版控制台中的基于SAML协议的信任委托SSO和基于OIDC协议的信任委托SSO这两种配置。

联邦登录的两个环节

IAM身份提供商的联邦登录可以分为认证和授权两个环节,共同完成登录流程的控制,详细信息请参见下表:
表1 联邦登录的两个环节

环节

发生位置

作用

认证环节

企业IdP

校验用户账号密码,确认这是一个合法的IdP用户,并签发携带用户属性的SAML断言或OIDC令牌。

授权环节

IAM SP

对SAML断言或OIDC令牌签名做验证后,按已配置的身份转换规则(虚拟用户SSO)或者外部身份ID(IAM用户SSO),对IdP用户进行身份准入和权限授予。

IAM作为服务提供方,负责授权环节的配置管理,如果使用者在授权环节配置不当,则可能导致权限范围失控。

本实践面向计划将企业身份系统(IdP)对接到IAM身份提供商(SP)的架构与安全设计人员,围绕授权环节的两个核心问题给出配置建议:
  • 身份准入:哪些IdP用户可以登录华为云。
  • 权限授予:登录华为云的IdP用户拥有哪些操作权限。
基于SAML协议与基于OIDC协议的虚拟用户SSO,在IAM身份转换规则的配置逻辑上完全一致,为避免重复,下文中的配置案例统一以基于SAML协议的虚拟用户SSO为例进行讲解。对于基于OIDC协议的虚拟用户SSO,仅需按下表将案例中的协议要素做对应替换,文中的问题分析与最佳实践同样适用:
表2 协议要素说明

SAML协议要素

OIDC协议要素

断言(Assertion)

OIDC令牌(ID Token)

断言的验签

OIDC令牌的验签

断言Subject元素中的NameID(__NAMEID__)

OIDC令牌中的sub声明

断言AttributeStatement元素中的属性(如Groups、FirstName、LastName)

OIDC令牌中的声明(如Groups、FirstName、LastName)

属性中携带多个AttributeValue(多值属性)

数组形式的多值声明

即下文出现的“SAML断言”均可替换为“OIDC令牌”理解,“断言中的属性”均可替换为“OIDC令牌中的声明”理解,不再单独给出OIDC协议的配置案例。

IAM身份提供商安全配置的常见问题与最佳实践

  • 常见问题一:身份准入配置过宽,导致任意IdP用户均可登录华为云
    假设您已经按照身份提供商概述配置好了基于SAML协议或OIDC协议的虚拟用户身份提供商,并在IAM中创建了ecs_readonly用户组,给该用户组授予了ECS ReadOnlyAccess的系统策略,其中IAM身份提供商的身份转换规则如下:
    [
        {
            "remote": [
                {
                    "type": "__NAMEID__"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "FederationUser"
                    }
                }
            ]
        },
        {
            "remote": [
                {
                    "any_one_of": [
                        "idp_ecs_readonly"
                    ],
                    "type": "Groups"
                }
            ],
            "local": [
                {
                    "group": {
                        "name": "ecs_readonly"
                    }
                }
            ]
        }
    ]
    上述身份转换规则一共有两条规则,第一条为身份准入规则,第二条为权限授予规则,表达的含义为:
    1. 在SAML断言验签通过后,只要SAML断言的Subject元素中存在NameID子元素,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为FederationUser。
    2. 如果SAML断言的AttributeStatement元素中存在Groups属性,Groups属性中只要有任意一个属性值为idp_ecs_readonly,那么这个名称为FederationUser的虚拟用户就拥有IAM中ecs_readonly用户组的权限。
    这个身份转换规则有以下两个问题:
    1. 在remote侧的映射中使用了“__NAMEID__”这个通用type。
    2. 将第一条身份准入规则单独抽取出来但是没有使用条件运算符。

    一般情况下,SAML断言的Subject元素中都会携带NameID子元素,而该规则未配置条件运算符进行校验,导致IdP中的所有用户均可通过第一条规则登录华为云。即便用户不属于IdP侧的idp_ecs_readonly用户组,也仅会影响其登录华为云后是否被授予相应权限,并不能阻止其完成登录。

  • 最佳实践一:合理配置身份准入规则,只允许目标IdP用户登录华为云
    正确的做法可以将身份准入规则和权限授予规则合并在一起,然后利用条件运算符限制身份准入:
    [
        {
            "remote": [
                {
                    "type": "FirstName"
                },
                {
                    "type": "LastName"
                },
                {
                    "any_one_of": [
                        "idp_ecs_readonly"
                    ],
                    "type": "Groups"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "{0} {1}"
                    }
                },
                {
                    "group": {
                        "name": "ecs_readonly"
                    }
                }
            ]
        }
    ]

    在上述身份转换规则中,并没有对remote中的FirstName、LastName这两个type添加条件运算符,因为需要将它们的值传递到local的user.name的占位符中,但是因为将身份准入规则和权限授予规则合并在一起了,整个身份转换规则都会受到any_one_of这个条件运算符控制,表达式的含义为:

    在SAML断言验签通过后,当断言中存在Groups属性且Groups属性中有一个属性值为idp_ecs_readonly时,如果断言中的FirstName与LastName属性也存在,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为${FirstName} ${LastName},且该虚拟用户拥有IAM中ecs_readonly用户组的权限。

    这样便可以做到只允许IdP侧idp_ecs_readonly用户组的用户登录华为云,而其他不相关的IdP合法用户无法登录华为云,即使他们也有FirstName和LastName属性。

    针对最佳实践一中的身份转换规则,如果any_one_of配置空数组,例如:"any_one_of": [],则表示拒绝所有登录,不论IdP用户在IdP侧是否加入了用户组。

  • 常见问题二:未进行最小化权限授予,导致任意登录用户获取较大权限
    [
        {
            "remote": [
                {
                    "type": "FirstName"
                },
                {
                    "type": "LastName"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "{0} {1}"
                    }
                },
                {
                    "group": {
                        "name": "admin"
                    }
                }
            ]
        }
    ]

    上述身份转换规则,在问题一的基础上又引入了权限授予过大的问题,表达的含义为:

    在SAML断言验签通过后,如果断言中的FirstName与LastName属性存在,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为${FirstName} ${LastName},且该虚拟用户拥有IAM中admin用户组的权限。

    一般来说,IdP中的所有用户都拥有FirstName与LastName这种表示姓名的基础属性,因此上述规则等价于IdP中的所有用户都拥有操作华为云资源的所有权限。

  • 最佳实践二:按照工作职能分组,进行最小化权限授予
    正确的做法是对权限授予规则进行拆分,将IdP中的不同用户按照工作职能分配IdP的不同用户组中,然后将IdP用户组映射到IAM对应的用户组中,最后根据不同的IAM用户组进行最小化权限授权。假设您已经在IAM中创建了所需的ecs_readonly、ecs_admin用户组,并分别授予了ECS ReadOnlyAccess、ECS FullAccess的系统策略,那么一个最小化权限授予的身份转换规则如下:
    [
        {
            "remote": [
                {
                    "type": "FirstName"
                },
                {
                    "type": "LastName"
                },
                {
                    "any_one_of": [
                        "idp_ecs_readonly"
                    ],
                    "type": "Groups"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "{0} {1}"
                    }
                },
                {
                    "group": {
                        "name": "ecs_readonly"
                    }
                }
            ]
        },
        {
            "remote": [
                {
                    "type": "FirstName"
                },
                {
                    "type": "LastName"
                },
                {
                    "any_one_of": [
                        "idp_ecs_admin"
                    ],
                    "type": "Groups"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "{0} {1}"
                    }
                },
                {
                    "group": {
                        "name": "ecs_admin"
                    }
                }
            ]
        }
    ]
    在上述身份转换规则中,根据工作职能拆分为两条规则,将允许ECS只读用户分配到idp_ecs_readonly用户组中,将ECS管理员用户分配到idp_ecs_admin用户组中,表达的含义为:
    1. 在SAML断言验签通过后,当断言中存在Groups属性且Groups属性中有一个属性值为idp_ecs_readonly时,如果断言中的FirstName与LastName属性也存在,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为${FirstName} ${LastName},且该虚拟用户拥有IAM中ecs_readonly用户组的权限。
    2. 在SAML断言验签通过后,当断言中存在Groups属性且Groups属性中有一个属性值为idp_ecs_admin时,如果断言中的FirstName与LastName属性也存在,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为${FirstName} ${LastName},且该虚拟用户拥有IAM中ecs_admin用户组的权限。

    这样便做到了最小化权限授予,IdP中不同用户组的用户联邦登录后分别拥有不同的权限,需要有新用户登录华为云时,只需要将其加入IdP中的相应用户组即可。

    在使用多值属性时,如果在IdP侧同时加入了idp_ecs_readonly和idp_ecs_admin用户组,那么该虚拟用户会同时映射到华为云的ecs_readonly和ecs_admin用户组。

  • 常见问题三:将IdP用户组直接映射到IAM用户组时,未配置条件运算符导致身份准入失控
    在使用身份转换规则将IdP的用户组直接映射到IAM的用户组时,如果remote侧对Groups这个type仅做存在性检查而未使用条件运算符,则身份准入效果取决于IdP对Groups属性的处理行为:
    [
        {
            "remote": [
                {
                    "type": "FirstName"
                },
                {
                    "type": "LastName"
                },
                {
                    "type": "Groups"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "{0} {1}"
                    }
                },
                {
                    "groups": "{2}"
                }
            ]
        }
    ]

    上述身份转换规则表达的含义为:

    在SAML断言验签通过后,如果断言中的FirstName、LastName、Groups属性都存在,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为${FirstName} ${LastName},并且将IdP侧的Groups属性值直接作为IAM侧的用户组名进行匹配,实际该虚拟用户是否拥有权限取决于IdP侧的用户组是否在IAM中真实存在。

    注意,这个时候如果IdP用户未在IdP侧加入任何用户组,那么其身份准入效果取决于IdP对Groups属性的处理行为:
    表3 IdP用户未加入任何用户组时IdP的处理行为

    IdP用户未加入任何用户组时IdP的处理行为

    IAM识别到的Groups属性状态

    身份准入结果

    IdP发送的断言不存在Groups属性

    属性不存在

    登录失败

    IdP发送的断言存在Groups属性但值为空

    属性存在,值为空字符串

    登录成功

    也就是说,如果IdP在用户未加入任何用户组时仍发送空值的Groups属性,则该规则等价于所有拥有FirstName和LastName的IdP用户均可登录华为云,虽然登录后没有任何用户组权限,但仍然存在身份准入配置过宽的问题。

  • 最佳实践三:将IdP用户组直接映射到IAM用户组时,应配合条件运算符确保身份准入可控
    如果需要将IdP的用户组名直接映射到IAM的同名用户组,正确的做法是使用条件运算符进行身份准入控制:
    [
        {
            "remote": [
                {
                    "type": "FirstName"
                },
                {
                    "type": "LastName"
                },
                {
                    "type": "Groups"
                },
                {
                    "any_one_of": [
                        "idp_ecs_readonly",
                        "idp_ecs_admin"
                    ],
                    "type": "Groups"
                }
            ],
            "local": [
                {
                    "user": {
                        "name": "{0} {1}"
                    }
                },
                {
                    "groups": "{2}"
                }
            ]
        }
    ]

    在上述身份转换规则中,在remote中有两个Groups的type,这是因为拥有any_one_of的Groups的type只用于做身份准入,确保了只有在断言的Groups属性中包含idp_ecs_readonly或idp_ecs_admin的用户才能登录华为云,它不能提取{2}的值。所以,想要在local的groups中仍然使用{2}直接从断言中提取IdP的用户组名,就必须在remote中额外携带一个不带条件运算符的Groups的type。整体表达的含义为:

    在SAML断言验签通过后,当断言的Groups属性中存在idp_ecs_readonly或idp_ecs_admin时,如果断言中的FirstName与LastName属性也存在,那么就可以将IdP用户映射为一个华为云虚拟用户,这个虚拟用户名称为${FirstName} ${LastName},并且将IdP侧的Groups属性值直接作为IAM侧的用户组名进行匹配,实际该虚拟用户是否拥有权限仍然取决于IdP侧的用户组是否在IAM中真实存在;

    这样即使IdP在用户未加入任何用户组时发送了空值的Groups属性,any_one_of条件运算符也会校验失败从而阻止该用户登录华为云。

IAM身份提供商使用的注意事项

  • 注意事项一:针对断言中AttributeStatement元素的同名独立属性,IAM身份转换规则只会取最后一个属性的值
    例如,对于最佳实践三中的身份转换规则,如果IdP断言中的AttributeStatement元素如下:
    <saml:AttributeStatement>
        <saml:Attribute Name="Groups"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">idp_ecs_readonly</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="Groups"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">idp_other</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="First Name"
                        Name="FirstName"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">FirstName</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="Last Name"
                        Name="LastName"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">LastName</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>
    那么,针对上述断言,IAM在local中取到的groups为["idp_other"],因此不满足any_one_of运算符中的值,所以会登录失败。想要传递IdP中多个用户组标准做法是使用多值属性,IdP断言设定Groups为多值属性的示例如下:
    <saml:AttributeStatement>
        <saml:Attribute Name="Groups"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">idp_ecs_readonly</saml:AttributeValue>
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">idp_other</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="First Name"
                        Name="FirstName"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">FirstName</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute FriendlyName="Last Name"
                        Name="LastName"
                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:type="xs:string">LastName</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>

    针对上述断言,IAM在local中取到的groups为["idp_ecs_readonly", "idp_other"],因此满足any_one_of运算符中的值,所以登录成功。

    上述注意事项源于SAML的XML结构允许在AttributeStatement元素中重复出现同名Attribute元素。在基于OIDC协议的虚拟用户SSO中,OIDC令牌为JSON格式,声明的名称唯一,不存在同名独立声明只取最后一个值的问题;如需传递多个用户组,在OIDC协议下直接使用数组形式的多值声明即可,例如"Groups": ["idp_ecs_readonly", "idp_other"]。