OpenID Connect、 OAuth和JWT三者关系与区别

OpenID Connect:OAuth 2.0协议之上的简单身份层

 

OpenID Connect是什么?OpenID Connect(目前版本是1.0)是OAuth 2.0协议(可参考本人此篇:OAuth 2.0 / RCF6749 协议解读)之上的简单身份层,用 API 进行身份交互的框架,允许客户端根据授权服务器的认证结果最终确认用户的身份,以及获取基本的用户信息;它支持包括Web、移动、JavaScript在内的所有客户端类型;它是可扩展的协议,允许你使用某些可选功能,如身份数据加密、OpenID提供商发现、会话管理
OpenID Connect vs OpenID 2.0:OpenID Connect完成很多与OpenID 2.0相同的任务,是API-friendly,定义了可选的签名和加密的机制;OAuth 1.0a和OpenID 2.0的集成需要扩展,而OpenID Connect协议本身就建立在OAuth 2.0之上
部分名词解释:

1. Relying Party(RP):依赖方,通常是第三方应用程序(客户端)
2. OpenID Provider(OP):OpenID 提供方,通常是一个 OpenID 认证服务器,它能为依赖方提供断言以证实用户拥有某个标识
3. End-User(EU):终端用户,指持有账号的人

OpenID Connect协议构成:

    Core – 定义 OpenID Connect 核心功能: 认证建立在OAuth 2.0之上,使用声明与终端用户进行信息交互
    Discovery – (Optional) Defines how Clients dynamically discover information about OpenID Providers
    Dynamic Registration – (Optional) Defines how clients dynamically register with OpenID Providers
    OAuth 2.0 Multiple Response Types – 定义了几种新的OAuth 2.0响应类型
    OAuth 2.0 Form Post Response Mode – (Optional) Defines how to return OAuth 2.0 Authorization Response parameters (including OpenID Connect Authentication Response parameters) using HTML form values that are auto-submitted by the User Agent using HTTP POST
    Session Management – (Optional) Defines how to manage OpenID Connect sessions, including postMessage-based logout functionality
    Front-Channel Logout – (Optional) Defines a front-channel logout mechanism that does not use an OP iframe on RP pages
    Back-Channel Logout – (Optional) Defines a logout mechanism that uses direct back-channel communication between the OP and RPs being logged out

下边两条是 Web RPs 实现者的独立参考指南:

    Basic Client Implementer’s Guide – Simple subset of the Core functionality for a web-based Relying Party using the OAuth code flow
    Implicit Client Implementer’s Guide – Simple subset of the Core functionality for a web-based Relying Party using the OAuth implicit flow

协议迁移规范:

    OpenID 2.0 to OpenID Connect Migration 1.0 – Defines how to migrate from OpenID 2.0 to OpenID Connect

OpenID Connect 工作组已启动新的工作计划:

    OpenID Connect Profile for SCIM Services – (Optional) Defines how to use SCIM with OpenID Connect
    OpenID Connect Federation – (Optional) Defines how sets of OPs and RPs can establish trust by utilizing a Federation Operator

OpenID Connect的工作流程:下以EU获取UserInfo为例来说明,

1. RP(客户端)发送一个认证请求给OP;
2. OP对EU进行身份认证并获得授权;
3. OP发送ID Token给RP,通常也同时发送Access Token(为兼容OAuth 2.0。ID Token其实可以取代Access Token用来完成授权);
4. RP使用Access Token发送一个请求UserInfo EndPoint;
5. UserInfo EndPoint返回EU的Claims。

下边是关于“ID Token 与 Access Token”的描述来自 User Authentication with OAuth 2.0 [UserInfo Endpoint]:

It should be noted that clients are not required to use the access token, since the ID Token contains all the necessary information for processing the authentication event. However, in order to provide compatibility with OAuth and match the general tendency for authorizing identity and other API access in parallel, OpenID Connect always issues the ID token along side an OAuth access token.

ID Token:它是一种JWT(JSON Web Token)格式数据,主要由以下几部分构成:

1. iss:必须。Issuer Identifier,OP的唯一标识,一个区分大小写的https URL,不包含query和fragment组件
2. sub:必须。Subject Identifier,iss提供的EU的标识,在iss范围内唯一,最长为255个ASCII个字符,区分大小写
3. aud:必须。Audience(s),标识ID Token的受众,必须包含OAuth2的client_id,分大小写的字符串数组
4. exp:必须。Expiration time,超过此时间的ID Token会作废
5. iat:必须。Issued At Time,JWT的构建的时间
6. auth_time:AuthenticationTime,EU完成认证的时间。如果RP发送AuthN请求的时候携带max_age的参数,则此Claim是必须的
7. nonce:RP发送认证请求的时候提供的随机字符串,用来减缓重放攻击,也可以用来关联客户端Session。如果nonce存在,客户端必须验证nonce
8. acr:可选。Authentication Context Class Reference,表示一个认证上下文引用值,可以用来标识认证上下文类
9. amr:可选。Authentication Methods References,表示一组认证方法
10. azp:可选。Authorized party,结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用

形如:

{
   "iss": "https://server.example.com",
   "sub": "24400320",
   "aud": "s6BhdRkqt3",
   "nonce": "n-0S6_WzA2Mj",
   "exp": 1311281970,
   "iat": 1311280970,
   "auth_time": 1311280969,
   "acr": "urn:mace:incommon:iap:silver"
}

 关于协议定义Claims的更多信息参考:http://openid.net/specs/openid-connect-core-1_0.html#Claims。ID Token必须使用JWS进行签名,如果要用JWE加密也必须先进行JWS签名
JSON Web Signature (JWS):JSON数据进行数字签名或MACed后再经base64url编码。

JWS Compact Serialization如下连接序列:Header.Payload.Signature

BASE64URL(UTF8(JWS Protected Header)) || '.' ||
BASE64URL(JWS Payload) || '.' ||
BASE64URL(JWS Signature)

1. JWS Protected Header
    {"typ":"JWT",
      "alg":"HS256"}
    编码后:
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
2. JWS Payload
    {"iss":"joe",
      "exp":1300819380,
      "http://example.com/is_root":true}
    编码后:
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
3. JWS Signature
    Signing Input value:
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
     .
     eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
    HMAC SHA-256 key:
    {"kty":"oct",
      "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
     }
    JWS Signature:
    dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
4. JWS Compact Serialization
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
     .  
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
     cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
     .
     dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

JWS JSON Serialization包含如下几个部分:

1. "protected", with the value BASE64URL(UTF8(JWS Protected Header))
2. "header", with the value JWS Unprotected Header
3. "payload", with the value BASE64URL(JWS Payload)
4. "signature", with the value BASE64URL(JWS Signature)

General JWS JSON Serialization:

{
      "payload":"<payload contents>",
      "signatures":[
       {"protected":"<integrity-protected header 1 contents>",
        "header":<non-integrity-protected header 1 contents>,
        "signature":"<signature 1 contents>"},
       ...
       {"protected":"<integrity-protected header N contents>",
        "header":<non-integrity-protected header N contents>,
        "signature":"<signature N contents>"}]
}

Flattened JWS JSON Serialization:

{
      "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
      "protected":"eyJhbGciOiJFUzI1NiJ9",
      "header": {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},
      "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"
}

JSON Web Encryption (JWE):

JWE Compact Serialization如下连接序列:

      BASE64URL(UTF8(JWE Protected Header)) || '.' ||
      BASE64URL(JWE Encrypted Key) || '.' ||
      BASE64URL(JWE Initialization Vector) || '.' ||
      BASE64URL(JWE Ciphertext) || '.' ||
      BASE64URL(JWE Authentication Tag)

JWE JSON Serialization包含如下几个部分:

      1. "protected", with the value BASE64URL(UTF8(JWE Protected Header))
      2. "unprotected", with the value JWE Shared Unprotected Header
      3. "header", with the value JWE Per-Recipient Unprotected Header
      4. "encrypted_key", with the value BASE64URL(JWE Encrypted Key)
      5. "iv", with the value BASE64URL(JWE Initialization Vector)
      6. "ciphertext", with the value BASE64URL(JWE Ciphertext)
      7. "tag", with the value BASE64URL(JWE Authentication Tag)
      8. "aad", with the value BASE64URL(JWE AAD)

General JWE JSON Serialization:

{
      "protected":"<integrity-protected shared header contents>",
      "unprotected":<non-integrity-protected shared header contents>,
      "recipients":[
       {"header":<per-recipient unprotected header 1 contents>,
        "encrypted_key":"<encrypted key 1 contents>"},
       ...
       {"header":<per-recipient unprotected header N contents>,
        "encrypted_key":"<encrypted key N contents>"}],
      "aad":"<additional authenticated data contents>",
      "iv":"<initialization vector contents>",
      "ciphertext":"<ciphertext contents>",
      "tag":"<authentication tag contents>"
}

Flattened JWE JSON Serialization:

{
      "protected":"<integrity-protected header contents>",
      "unprotected":<non-integrity-protected header contents>,
      "header":<more non-integrity-protected header contents>,
      "encrypted_key":"<encrypted key contents>",
      "aad":"<additional authenticated data contents>",
      "iv":"<initialization vector contents>",
      "ciphertext":"<ciphertext contents>",
      "tag":"<authentication tag contents>"
}

 

JSON Web Key (JWK):JWK用于JWS和JWE中,也是一种JSON数据结构,其包含了如下几项:

1. "kty" (Key Type) Parameter
2. "use" (Public Key Use) Parameter
3. "key_ops" (Key Operations) Parameter
4. "alg" (Algorithm) Parameter
5. "kid" (Key ID) Parameter
6. "x5u" (X.509 URL) Parameter
7. "x5c" (X.509 Certificate Chain) Parameter
8. "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter
9. "x5t#S256" (X.509 Certificate SHA-256 Thumbprint)

形如:

     {"kty":"EC",
      "crv":"P-256",
      "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
      "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
      "kid":"Public key used in JWS spec Appendix A.3 example"
     }

 
Base64编码:Base64是基于64个可打印字母表(Table 1: The Base64 Alphabet)来表示数据的方法,可用于二进制数据如图片、视频、音频数据的转换表示,可查看RFC2045~RFC2049,上面有MIME(Multipurpose Internet Mail Extensions)的详细规范:

1. 每3个字节一组,再按6bits(2 ^ 6 = 64)为一组分成4组,即3 * 8bits = 4 * 6bits;
2. 以6bits的值为索引查找字母表中对应的字符。最后3字节变成4字节,长度增加33%;
3. 对于不足3字节的情况用\x00字节补足,最后用尾部加“=”的个数来表示缺少的字节数,即Base64编码的尾部最多有1~2个“=”。

在RFC 2045中还规定,输出每行不超过76字符(CRLF结尾):

The encoded output stream must be represented in lines of no more than 76 characters each. All line breaks or other characters not found in Table 1 must be ignored by decoding software.

 由于+和/字符在URL中传递是不安全的,因此又出现了以-和_来替换+和/的Base64URL编码:

由于“=”字符也可能出现在Base64编码中,但“=”用在URL、Cookie里面会造成歧义,所以很多Base64编码把“=”去掉。至于去掉“=”后如何解码呢?正常Base64编码后的序列字符数一定是4的倍数,如果结果不是,则可反推去掉了多少个“=”

 

相关参考:

1. OIDC(OpenId Connect)身份认证授权(核心部分)

2. RFC 7515 - JSON Web Signature (JWS)
3. RFC 7516 - JSON Web Encryption (JWE)

4. RFC 7517 - JSON Web Key (JWK)
 

 

OAuth 2和JWT - 如何设计安全的API?

Moakap译,原文 OAuth 2 VS JSON Web Tokens: How to secure an API

本文会详细描述两种通用的保证API安全性的方法:OAuth2和JSON Web Token (JWT)

假设:

  • 你已经或者正在实现API;
  • 你正在考虑选择一个合适的方法保证API的安全性;

JWT和OAuth2比较?

要比较JWT和OAuth2?首先要明白一点就是,这两个根本没有可比性,是两个完全不同的东西。

  • JWT是一种认证协议
    JWT提供了一种用于发布接入令牌(Access Token),并对发布的签名接入令牌进行验证的方法。 令牌(Token)本身包含了一系列声明,应用程序可以根据这些声明限制用户对资源的访问。

  • OAuth2是一种授权框架
    另一方面,OAuth2是一种授权框架,提供了一套详细的授权机制(指导)。用户或应用可以通过公开的或私有的设置,授权第三方应用访问特定资源。

既然JWT和OAuth2没有可比性,为什么还要把这两个放在一起说呢?实际中确实会有很多人拿JWT和OAuth2作比较。标题里把这两个放在一起,确实有误导的意思。很多情况下,在讨论OAuth2的实现时,会把JSON Web Token作为一种认证机制使用。这也是为什么他们会经常一起出现。

先来搞清楚JWT和OAuth2究竟是干什么的~

JSON Web Token (JWT)

JWT在标准中是这么定义的:

JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).
- RFC7519 https://tools.ietf.org/html/rfc7519

JWT是一种安全标准。基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交信息信息的合法性;如果验证成功,会产生并返回一个Token(令牌),用户可以使用这个token访问服务器上受保护的资源。

一个toke的例子:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
  • 1

一个token包含三部分:

header.claims.signature

为了安全的在url中使用,所有部分都base64 URL-safe进行编码处理。

Header头部分

头部分简单声明了类型(JWT)以及产生签名所使用的算法。

{
  "alg" : "AES256",
  "typ" : "JWT"
}
  • 1
  • 2
  • 3
  • 4

Claims声明

声明部分是整个token的核心,表示要发送的用户详细信息。有些情况下,我们很可能要在一个服务器上实现认证,然后访问另一台服务器上的资源;或者,通过单独的接口来生成token,token被保存在应用程序客户端(比如浏览器)使用。
一个简单的声明(claim)的例子:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
  • 1
  • 2
  • 3
  • 4
  • 5

Signature签名

签名的目的是为了保证上边两部分信息不被篡改。如果尝试使用Bas64对解码后的token进行修改,签名信息就会失效。一般使用一个私钥(private key)通过特定算法对Header和Claims进行混淆产生签名信息,所以只有原始的token才能于签名信息匹配。

这里有一个重要的实现细节。只有获取了私钥的应用程序(比如服务器端应用)才能完全认证token包含声明信息的合法性。所以,永远不要把私钥信息放在客户端(比如浏览器)。

OAuth2是什么?

相反,OAuth2不是一个标准协议,而是一个安全的授权框架。它详细描述了系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)之间怎么实现相互认证。

The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf.
- RFC6749 https://tools.ietf.org/html/rfc6749

这里简单说一下涉及到的基本概念。

Roles角色

应用程序或者用户都可以是下边的任何一种角色:

  • 资源拥有者
  • 资源服务器
  • 客户端应用
  • 认证服务器

Client Types客户端类型

这里的客户端主要指API的使用者。它可以是的类型:

  • 私有的
  • 公开的

Client Profile客户端描述

OAuth2框架也指定了集中客户端描述,用来表示应用程序的类型:

  • Web应用
  • 用户代理
  • 原声应用

Authorization Grants认证授权

认证授权代表资源拥有者授权给客户端应用程序的一组权限,可以是下边几种形式:

  • 授权码
  • 隐式授权
  • 资源拥有者密码证书
  • 客户端证书

Endpoints终端

OAuth2框架需要下边几种终端:

  • 认证终端
  • Token终端
  • 重定向终端

从上边这些应该可以看出,OAuth2定义了一组相当复杂的规范。

使用HTTPS保护用户密码

在进一步讨论OAuth2和JWT的实现之前,有必要说一下,两种方案都需要SSL安全保护,也就是对要传输的数据进行加密编码。

安全地传输用户提供的私密信息,在任何一个安全的系统里都是必要的。否则任何人都可以通过侵入私人wifi,在用户登录的时候窃取用户的用户名和密码等信息。

一些重要的实施考虑

在做选择之前,参考一下下边提到的几点。

时间投入

OAuth2是一个安全框架,描述了在各种不同场景下,多个应用之间的授权问题。有海量的资料需要学习,要完全理解需要花费大量时间。甚至对于一些有经验的开发工程师来说,也会需要大概一个月的时间来深入理解OAuth2。 这是个很大的时间投入。

相反,JWT是一个相对轻量级的概念。可能花一天时间深入学习一下标准规范,就可以很容易地开始具体实施。

出现错误的风险

OAuth2不像JWT一样是一个严格的标准协议,因此在实施过程中更容易出错。尽管有很多现有的库,但是每个库的成熟度也不尽相同,同样很容易引入各种错误。在常用的库中也很容易发现一些安全漏洞。

当然,如果有相当成熟、强大的开发团队来持续OAuth2实施和维护,可以一定成都上避免这些风险。

社交登录的好处

在很多情况下,使用用户在大型社交网站的已有账户来认证会方便。

如果期望你的用户可以直接使用Facebook或者Gmail之类的账户,使用现有的库会方便得多。

结论

做结论前,我们先来列举一下JWT和OAuth2的主要使用场景。

JWT使用场景

无状态的分布式API

JWT的主要优势在于使用无状态、可扩展的方式处理应用中的用户会话。服务端可以通过内嵌的声明信息,很容易地获取用户的会话信息,而不需要去访问用户或会话的数据库。在一个分布式的面向服务的框架中,这一点非常有用。

但是,如果系统中需要使用黑名单实现长期有效的token刷新机制,这种无状态的优势就不明显了。

优势

  • 快速开发
  • 不需要cookie
  • JSON在移动端的广泛应用
  • 不依赖于社交登录
  • 相对简单的概念理解

限制

  • Token有长度限制
  • Token不能撤销
  • 需要token有失效时间限制(exp)

OAuth2使用场景

在作者看来两种比较有必要使用OAuth2的场景:

外包认证服务器

上边已经讨论过,如果不介意API的使用依赖于外部的第三方认证提供者,你可以简单地把认证工作留给认证服务商去做。
也就是常见的,去认证服务商(比如facebook)那里注册你的应用,然后设置需要访问的用户信息,比如电子邮箱、姓名等。当用户访问站点的注册页面时,会看到连接到第三方提供商的入口。用户点击以后被重定向到对应的认证服务商网站,获得用户的授权后就可以访问到需要的信息,然后重定向回来。

优势

  • 快速开发
  • 实施代码量小
  • 维护工作减少

大型企业解决方案

如果设计的API要被不同的App使用,并且每个App使用的方式也不一样,使用OAuth2是个不错的选择。

考虑到工作量,可能需要单独的团队,针对各种应用开发完善、灵活的安全策略。当然需要的工作量也比较大!这一点,OAuth2的作者也指出过:

To be clear, OAuth 2.0 at the hand of a developer with deep understanding of web security will likely result is a secure implementation. However, at the hands of most developers – as has been the experience from the past two years – 2.0 is likely to produce insecure implementations.


hueniverse - OAuth 2.0 and the Road to Hell

优势

  • 灵活的实现方式
  • 可以和JWT同时使用
  • 可针对不同应用扩展

进一步

  • http://jwt.io - JWT官方网站,也可以查看到使用不同语言实现的库的状态。
  • http://oauth.net/2/ OAuth2官方网站, 也也可以查看到使用不同语言实现的库的状态。
  • OAuth 2 tutorials - Useful overview of how OAuth 2 works
  • Oauth2 Spec issues Eran Hammer’s (推进OAuth标准的作者) views on what went wrong with the OAuth 2 spec process. Whatever your own opinion, good to get some framing by someone who understand’s key aspects of what make a security standard successful.
  • Thoery and implemnetation: with Laravel and Angular Really informative guide to JWT in theory and in practice for Laravel and Angular.
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值