前言
在做GCM推送的时候,遇到GCM Server返回“Invalid Package Name”的错误信息。一开始以为是服务端的Server API Key与APP没有匹配上,后来查明是后台人员在发往GCM Server的消息中加了restricted_package_name这个字段,并且传了错误的值。于是引发了以下对restricted_package_name字段的探究。
restricted_package_name的含义
官网是这样解释的:
This parameter specifies the package name of the application where the registration tokens must match in order to receive the message.
即是说,只有restricted_package_name字段中指定包名的应用才能接收到推往设备的消息。
restricted_package_name的验证
于是,我们尝试在推送消息中加入restricted_package_name字段,并赋以不同的value进行测试,得到如下结论:
* 正确的包名,推送成功。
* 空字符串,推送成功。
* 错误的包名,推送失败。返回“Invalid Package Name”错误信息。
由以上的测试,可以看出:只要restricted_package_name予以有效赋值,GCM Server就会发往指定包名的应用。但是,如果推送的registration ids并不是为该应用分配的,则会报“Invalid Package Name”,信息不会被发送。
restricted_package_name是否必要?
首先我们先来理一下推送流程:
1.在GCM官网创建应用Create or choose an app。我们填写APP packagename,由此生成Server API Key以及APP SenderId。
2.APP通过SenderId,获取registration id(类似iOS的deviceToken),并发给Server。
3.Server通过API Key发送消息到registration id标识的设备上的APP。
从流程中,可以看到API Key、SenderId、registration id是相互关联的。并且,我们似乎可以猜测,GCM Server收到推送请求时能够判断API Key与registration id是否匹配,并且是发往哪一个应用的。
为什么说能识别出发往哪一个应用?这主要是出于两个方面:一是创建应用时需要填写packagename,通过API Key可以找出对应的packagename;二是registration id,官网写着“a token that can identify and authorize the instance of the application on the device.This is similar to an OAuth2 token except, it applies to the application instance instead of a user.”即它至少包含device和application两个信息,同样可以拿到packagename;而且从上文“Invalid Package Name”的错误信息也能说明registration id可以获取packagename。
写到这里,估计大家都困惑了。既然GCM Server能通过API Key和registration id识别packagename,那restricted_package_name是不是显得有点多余?答案并非如此,我们只是说GCM Server可以通过API Key和registration id分别获取对应的packagename并校验其一致性,但不代表GCM Server有这样做!一切靠实际结果来验证。
接下来,我们就来做一下测试:
前期准备:
* 一台安卓设备
* APP 1,对应Server 1、SenderId 1、API Key 1
* APP 2,对应Server 2、SenderId 2、API Key 2
* 不加restricted_package_name字段
测试案例 1:验证“GCM Server是否会校验API Key与registration id的一致性”
为了好理解一点,我们可以假设这样一个场景:4月1日想恶作剧一下,打算用我们的服务器往别人家APP推送一条搞怪的消息。
注意:在案例中registration id简称为token
测试过程:
1.APP 2通过SenderId 2生成一个token 22(22表示由APP 2利用SenderId 2生成的)。
2.Server 1拿到这个token 22,通过API Key 1推送消息到token 22。
测试结果:
GCM Server返回“MismatchSenderId”的错误信息。
测试案例 2:验证“GCM Server是否会校验API Key与registration id对应packagename的一致性”
同样,我们可以假设这样一个场景:一个黑客破解了我们APP的源码,拿到我们的SenderId,然后自己写一个APP利用这个SenderId生成对应token,并发给我们服务器注册,这样我们服务器推送消息时也会推给黑客的APP。
测试过程:
1.APP 2通过SenderId 1生成一个token 21(21表示由APP 2利用SenderId 1生成的);
2.Server 1拿到这个token 21,通过API Key 1推送消息到token 21。
测试结果:
Server 1推送成功,APP 2能够接收到推送消息。
测试案例 3:为了进一步验证,我们让Server 1同时往token 11、token21推送同一条消息,结果APP1、APP2都能接收到推送消息。
结论:
1.GCM Server会校验API Key与registration id的一致性。
2.GCM Server不会校验API Key与registration id对应APP packagename的一致性。
以上,我们就可以明白了,只要“API Key”与“registration id对应的SenderId”能够匹配上,就可以推送成功。GCM Server并不管是哪一个应用!restricted_package_name的作用不言而喻。
>> 同样,我们对此进行了验证。
测试案例 4:在基于案例3的基础上,加入restricted_package_name。首先赋以APP 1的packagename,结果是:token 11(APP 1)推送成功,token 21(APP 2)报“Invalid Package Name”错误信息;其次改为APP 2的packagename,结果是:token 11(APP 1)报“Invalid Package Name”错误信息,token 21(APP 2)推送成功。
为什么GCM Server不校验?
这也不能说是Bug,可能是Google工程师觉得没有必要,也可能是GCM允许同个开发者的多个应用可以共享同一套API Key和SenderId吧。
在questions about security nature of GCM这个帖子中,一位开发者提出了类似案例2中的场景,对此,Google工程师在答复中发布了“加入restricted_package_name字段”的消息,并且指出真正的问题--“为什么APP 2可以在Server 1上注册token?”他建议:向Server注册token时,应进行加密处理或者一定的校验,以保证token的合法性。
总结
GCM 同一套API Key和SenderId允许发给多个应用,可以通过restricted_package_name来指定某个应用。
参考文章
groups.google.com/forum/#!topic/android-gcm/M-EevBitbhQ
android - Why is restricted_package_name used? - Stack Overflow
developers.google.com/cloud-messaging/http-server-ref#error-codes