了解单点登录常用协议

最近在改造一个单点登录系统,由于其中涉及到很多不同的协议、不同的授权方式,所以整理了一下相关的知识

名词

什么是单点登录

单点登录(SSO)是一种身份验证机制,它允许用户使用单一的凭据(如用户名和密码)访问多个相互关联的应用程序或系统。在没有单点登录的情况下,用户可能需要为每个应用程序分别记住不同的用户名和密码,这不仅麻烦,而且增加了用户管理凭据的负担和安全风险(如使用简单或重复的密码)。

在企业内部尤为明显,通常企业内部会有许多系统(例如OA系统、库存系统、销售系统、作业系统、审批系统、邮件系统、财务系统等等),如果让员工在每个系统上使用独立的账号密码登录显然不现实。所以就会需要单点登录。用户只要登录一次,就可以在所有系统上工作,无需重复登录。

协议

IdP经常会使用OAuth、OIDC、SAML协议来实现单点登录功能,那这3种协议又有什么区别呢?

OAuth

OAuth(开放授权)是一个开放标准的授权协议,它允许用户在不向第三方应用提供用户名和密码的情况下,授权第三方应用访问其存储在另外一个服务提供商(如社交网络平台、云存储服务等)上的受保护资源。

所以OAuth并不是一种身份认证的协议,而是一个授权协议。

举一个例子: 用户张三访问了一个A网站,A网站希望获取张三在gitee上的代码仓库信息。张三不会把自己的gitee账号密码给A网站,于是A网站就对接gitee,gitee采用OAuth协议授权A网站访问特定资源(这里指的就是张三的代码仓库信息)

核心角色

  • 资源拥有者(Resource Owner): 通常为用户,也可以是应用程序,即该资源的拥有者。
  • 客户端(Client): 本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:Android客户端、Web客户端(浏览器端)、微信客户端等。
  • 授权服务器 (Authorization Server) : 用于服务提供商对资源拥有的身份进行认证、对访问资源进行授权,认证成功后会给客户端发放令牌 (access_token),作为客户端访问资源服务器的凭据。
  • 资源服务器(Resource Server): 存储资源的服务器。

授权模式

授权模式 优点 缺点 适用场景
客户端凭证模式 简单 受使用场景限制 不需要资源拥有者参与的场景,为后台API服务设计,例如:地图信息
密码模式 不需要多次请求转发,额外开销 可以认证身份 需要认证服务器和资源服务器互相信赖,例如:同一公司自研系统
授权码授权模式 安全性高 多次请求 拥有后台管理的应用
隐式授权模式 简单 安全性较差,只有client_id即可获取令牌 应用只有页面没有后台管理,只能使用第三方认证后,直接访问。对安全性不高的场景。例如:浏览器插件
  • 客户端凭证模式

  • 密码模式

  • 授权码授权模式

  • 隐式授权模式

为什么OAuth可以用于单点登录呢?

OAuth并不是一种身份认证的协议,而是一个授权协议,所以只能用来授权,并不能用来身份认证。但事实上用户信息也是一种授保护的资源。只要授权的对象是获取用户信息,那么就可以在通过OAuth认证后,再获取用户信息,从而实现单点登录。

举个例子:gitee提供OAuth授权信息,并且提供授权访问用户信息

OIDC

OIDC是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth2.0。它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议。OIDC是一个协议族,提供很多的标准协议,包括Core核心协议和一些扩展协议。

SAML

SAML(Security Assertion Markup Language)是一种基于XML的安全协议,用于在不同的安全域之间交换认证和授权数据。SAML的主要作用是实现单点登录(SSO),使用户能够在多个应用程序中使用相同的凭证进行登录。

核心角色

  • 主体(principal):通常代表人类用户/浏览器终端
  • 身份提供者(Identity Provider,简称IdP): 主要负责进行身份认证,并将用户的认证信息和授权信息传递给SP
  • 服务提供者(Service Provider,简称SP): 主要职责是验证用户的认证信息,并授权用户访问指定的资源信息

认证过程

题外话

在OIDC中,有一个idToken的东东,它里面包含着用户信息,它的格式是JWT。通常对接单点登录服务器,获取到idToken后需要做一系列的校验。

  1. 验证签名:使用公钥验证ID Token的签名,确保ID Token未被篡改。

  2. 验证发行者:确认ID Token是由信任的授权服务器签发的。

  3. 验证令牌类型:确保令牌类型是id_token。

  4. 验证受众:确认ID Token是发送给正确的客户端。

  5. 验证过期时间:检查令牌是否在有效期内。

  6. 验证发行时间:可选,确保令牌在合理的时间范围内被签发。

  7. 验证nonce:如果使用了nonce参数来防止重放攻击,需要验证nonce值。

突然发现一个很奇怪的要求,原以为验证了签名就可以保证JWT(这里指idToken)未被篡改,为什么还需要验证发行者(iss)呢?进一步提出了以下疑问:

采用了主流的协议就能保证单点登录系统安全可靠吗?

答案显然是否定的,在OIDC文档中(16节)我们看到了一些安全和隐私注意事项,其中包含多种可能的攻击手段(例如:服务器伪装、令牌重用等)和防御措施(TLS要求等),所以为了单点登录服务器完全,需要多方面(技术方式、不同系统)一起保障。