如何在移动端开发中正确地使用OAuth协议:常见错误剖析

作者在之前的文章中曾经介绍过 OAuth2.0 协议,并将其与OpenID和SAML性对比。然而,在理论上设计协议是一回事,在工程中实现协议是另一回事,由于很多开发人员没有真正理解OAuth2.0的设计意图和技术细节,导致OAuth2.0在很多项目中的实现是有问题,尤其是在移动端的开发中。本文将专注于OAuth2.0在工程实现上常见的问题,帮助读者理解OAuth2.0的技术细节。

OAuth协议的设计初衷,是用于网站间的授权管理,随着其在业界的流行和应用,OAuth也被广泛应用于用户身份的认证。在移动端的火爆之后,OAuth协议也成为移动端的授权和认证的首选。

但是,由于OAuth本身并非为认证而设计,设计之初更没考虑到移动端的使用情况,再加上大部分开发人员对于OAuth协议的流程理解不到位,这就造成了很多项目在协议的实现上有很多地方错误和漏洞。

本文将帮助大家梳理OAuth协议中需要注意的重点,并指出在授权和认证情况下应该如何使用OAuth,同时也澄清一些在移动端中使用OAuth的错误观点。

本文以OAuth 2.0为准进行说明,如果没有特殊说明下文中的OAuth都特指OAuth 2.0版本。关于OAuth协议本身请见作者前文OAuth2.0 协议入门指南OAuth 2.0的四种工作模式中本文只会涉及到常用的默认模式(implicit type)授权码模式(authorization code type)

1. 使用OAuth可能面临的困难

OAuth协议标准很复杂,很少有开发人员能全部熟悉,因为开发人员理解的不到位和协议本身使用场景的改变,造成很多OAuth使用上的困难,简要概括为以下几点:

  • OAuth协议标准中不涉及认证的内容,所以对于认证的使用场景并更没有明确的说明,这导致许多开发者使用OAuth来认证时,常常依赖于自己的直觉,缺乏标准的指导;
  • OAuth 依赖于浏览器的重定向来实现token的传递,但是对于移动端,暂时还没有好的办法来实现同样的效果;
  • OAuth协议针的隐式模式授权码模式的使用场景不尽相同,用于授权和认证时也存在着差异,但是这种差异往往被忽视。
  • OAuth协议规范扩展性极强,很多身份提供商都会自定义OAuth的扩展,但是这些扩展没有统一的标准来引导,甚至还会有错误。

2. OAuth协议流程的关键点

本节将重点介绍在授权和认证两种不同使用场景下,默认模式授权码模式有什么不同,其工作的关键点哪里。

为了便于分析OAuth协议流程,先给出默认模式和授权码的流程,请见下图。

OAuth2.0 implicit
OAuth2.0 authorization code

2.1 用于授权的情况

所谓授权,指的是用户授权依赖方(Relying Party,RP)获取其在服务提供方(Service Provider, SP)保存的个人资源。

授权的安全焦点在于服务提供方,服务提供方要保证用户的个人资源的确是发送给了用户授权的依赖方。

在协议中,RP获得access token代表用户授权,RP通过access token换取资源,但是access token是一种bearer token,也就是说token和持有者的身份并没有绑定,任何人持有该token都能获取用户的资源。因此, 就需要在协议中保证RP的身份为合法的。

2.1.1 默认模式

默认模式通过RP注册时的返回地址Redirection URI来保证RP的身份,在上图默认模式流程的step2中,SP通过RP提供的App ID将RP发来的Redirection URI和其注册时设置的URI进行比对,如果一致才会按照URI中内容重定向返回token,从而来保证token只发给提前注册过的RP。因此默认模式在授权中安全保证的前提是RP提前注册的Redirection URI

2.1.2 授权码模式

授权码模式在默认模式基础上进一步加强,RP在注册时还会分配一个app secret, 其授权过程的安全性保证来源于
step4、step5用授权码换取token的过程中,使用RP与SP共享的app secretredirection URI对RP的身份进行了认证,因为app secret是不能公开的,只有SP和RP共享的秘密,所以可以确保RP身份合法性。在RP身份被验证后,SP再去检验使用授权码换取token的RP是不是当初申请授权码的RP(授权码会与RP的身份绑定)。

2.2 用于认证的情况

所谓认证,即用户向RP证明他/她的确是SP处某个账户身份的持有者,并以此身份登录RP。

认证的安全重点在于RP,需要协议提供如下安全保证:

  1. 从SP发送到RP的身份认证信息不能被第三方篡改;
  2. RP需要确定其获得的身份认证信息的确响应自己的申请;

如通信都是通过HTTPS,可以保证传递的数据不被篡改,但由于OAuth协议并不是为认证而设计的,所以不是所有的模式都可以用于认证的场景。

2.2.1 默认模式

OAuth2.0的默认模式不能用于认证

由于token是一种bearer token,没有和RP的身份绑定,所以默认模式中RP获得的token不能保证就是自己申请的。因此,默认模式用于认证是不安全的。将RP1申请来的token直接发送给RP2,RP2也可以认可该token为用户身份的凭证。

2.2.2 授权码模式

在授权码模式中,SP并不直接将token发给RP,而是先发送授权码,授权码和RP的身份是绑定的,因此RP可以通过授权码来保证自己获得的token就是自己申请的。也就是流程中的step4、step5的授权码换取token的过程保证了token是RP申请的。

同时授权码换取Token是服务器之间的通信,可以直接使用HTTPS,保证信息不被篡改。

3. Web端与移动端使用OAuth的区别

Web端和移动端的环境大不相同,所以为web环境设计的OAuth协议在移动端中使用会面临很多问题。

3.1 缺少重定向机制:

OAuth使用浏览器的重定向(HTTP status 302)在SP和RP之间跳转和传递数据,但是该功能依赖于浏览器,在移动端的app中并没有这样的机制。而更糟糕的是OAuth的标准中认为重定向是技术细节,并没有给出明确的说明。

移动端环境中和重定向最类似的方式是为应用设置自定义URL scheme的机制了(详见Android 跨应用间调用: URL Scheme),其也能实现App间的跳转和传参,但是无法保证接受者的唯一确定性,可能会出现两个应用定义了相同的scheme。

3.2 对应用的身份缺少认证:

浏览器的重定向机制不光保实现了SP和RP之间的跳转,也可以保证token放回到指定的RP处,因为注册的Redirection URI和RP是一一对应的,这样的URI和网站实体的对应是DNS服务器提供。而移动端也没有这样的机制。

3.3 客户端模式带来的逻辑混乱:

在web的情况下,SP和RP都是web网站,但是在移动端的情况,RP可能是native app和服务器的结合。原来RP中需要完成是认证流程,哪部分应该是手机上的app完成,哪些应该是后台的服务器完成,也是在OAuth移动端应用中模糊的地方。

更主要的是,移动端的环境是不可信任的,用户可能在不知情下安装了恶意应用,这些恶意应用可能会窃取用户在移动端中的数据。因此认证或授权中,和安全性相关的的敏感逻辑和数据储存都不能在移动端完成。

4. 移动端开发和实现中存在的问题

正因为存在上面提到的web端和移动端之间的环境差异,OAuth在移动端开发容易出现以下错误。

4.1 将secret存储在应用中

这是一个最常见的错误,如前文所述,OAuth和安全性相关的的敏感逻辑和数据储存都不能在移动端完成。

因此RP的app secret是不能保存在应用中

该错误可能是由于OAuth将其命名为“app secret”,但实际上这个app和移动端的app没有任何关系,其代指的时RP。

secret应该由RP的服务器端保存并SP通信。

4.2 无法充分理解认证与授权的区别

因为没有理解二者的区别,所以应用了OAuth错误的工作模式,使用默认模式来认证用户身份。

Facebook曾经使用OAuth2.0默认模式用于用户登录认证,后来发现了安全问题,但是要修改成授权码模式又和现有系统不兼容,只好才对默认模式进行了自定义化的安全增强,额外消耗了很多人力物力。如果一开始就能清楚知道认证和授权的不同,采用授权码模式来认证用户,就可以避免后面的麻烦事。

4.3在移动设备中处理重定向:

在开发中如何实现类似浏览器重定向的功能在SP和RP之间跳转并传递数据,是实现OAuth功能的关键点。常见的方法有:

  • 使用URI scheme与Intent在应用间跳转;
  • 模拟web端,使用移动端浏览器与WebView;

无论哪种方法都需要保证

  1. 跳转目标的确定性,即保证跳转到的应用真的是发起者要去访问的应用;
  2. 跳转请求来源的真实性,即跳转请求发起者的身份是真实可信的;

4.3.1 使用URI scheme与Intent:

在Android中,由于应用都要经过开发者的秘钥进行签名,所以SP应可以存储所支持的RP的apk的签名信息(Android提供API通过包名查询),在RP进行请求时比对,来确定请求者的身份。这样就可以保证应用之间跳转和传参的目标正确性。

relying_party = Activity.getCallingPackage()
dev_key_hash = getPackageManager().getPackageInfo(relying_party, PackageManager.GET_SIGNATURES);

反之,RP也可以保存SP的apk的签名信息,验证认证结果是否来自认可的SP。

关于IOS的开发,因笔者知识有限,不能具体说明,但是要指出IOS中存在系统漏洞,允许两个应用同时注册相同的URI的,可能会出现scheme劫持,即所谓的“跨应用资源访问(XARA)”,所以最终无法确定传递token的安全。

4.3.4 移动端浏览器与WebView:

由于OAuth本身就是基于浏览器设计的,所以很自然的就想到在移动端也使用浏览器或者是WebView来展示SP以处理OAuth请求。

但是这样做缺少SP的网页和RP的应用之间安全传递信息的途径。上一节提到验证RP的apk签名信息的方法只有在native APP才能使用,SP中的页面无法验证RP身份。同时,RP无法确定SP的页面在哪个应用的WebView运行中,无法断定是否是恶意应用传来的数据。

4.4 修改的OAuth协议容易出现漏洞:

腾讯曾经修改OAuth2.0的默认模式,来用于认证,使用App ID与User ID的hash值保证token和RP身份绑定关系。但是在用户授权的步骤中,没有出现用户确认框,而是默认同意,直接返回token。

这样做存在被攻击的风险:如果认证过程是在恶意的软件中的WebView中实现的,那么恶意的软件就能够拦截并修改App ID与重定向地址,如果没有给用户提示信息(如什么RP应用要求登录)而默认同意,那么攻击者就能通过这种办法以用户的身份访问任意网站。

腾讯后来及时修改了这个问题。

5. 从教训到总结

  1. 意识到认证和授权的不同,使用授权码模式进行认证,避免使用默认模式;
  2. 不要随意自定义扩展OAuth协议来用于认证,最好使用专门为认证而设计协议,比如OpenID Connect
  3. 在进行授权的时候,要保证给予用户展示请求授权的RP,所以应该在授权前提供确认框,其中要包括RP的身份信息;
  4. 我们需要默认用户的设备是不可信的
    1. 开发者不能将任何秘密的信息(如app secret)存储在客户端;
    2. 必须默认所有从设备发送的消息都是可以被篡改的,和服务器的通信要使用HTTPS,应用之间传递参数,也应该使用预先协商的密钥来加密保护;
    3. 开发者必须保证最后收到User ID的RP是用户想要访问的RP;

扩展阅读:

  1. OAuth2.0 协议入门指南
  2. 认证与授权——单点登录协议盘点:OpenID vs OAuth2 vs SAML
  3. OpenID Connect 协议入门指南
  4. Android 跨应用间调用: URL Scheme
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容