起因
之前在测试聚合数据的 iOS SDK Demo 的时候,发现会请求通讯录权限 … 当时刚睡醒没多久,拿了自己在用的手机,还点了 Allow。
然后我就在 Charles 里发现了一个很大的请求,很好奇,便对 SDK 进行了逆向工程,拿到了解密方法,发现我的整个通讯录都被上传了。
于是我在 V2EX 发了一个帖子 请不要使用聚合数据的 SDK,后来被聚合的工作人员看到,他们是这样回复的:
聚合数据一直致力于为开发者提供更为优质的数据服务,并基于此出发点,推出了聚合数据SDK,该SDK整合了目前聚合数据所提供的所有数据接口,此次aveline反馈的聚合数据SDK读取通讯录问题,实质上是聚合数据为了方便用户使用由聚合数据提供的短信API服务。此举对于非短信API服务使用者可能会造成一定不适,考虑到这一点,我们立即对于IOS版与Android版SDK进行了更新,不再要求读取通讯录权限,并对于此前为开发者用户考虑不周的行为表示诚挚歉意,并保证不再进行此类有侵犯用户隐私嫌疑的行为。
聚合数据是一个严格恪守行业底线的平台,聚合数据在进行数据分析的过程中极为注重隐私的保护,数据都进行了严格的加密处理,所搜集的数据仅用于改进网站服务本身,并未用于任何非法用途,对于聚合数据注册账号及密码,也进行了严格的加密措施。
在 CEO 的微博上是这么回复的:
昨天晚上收到这个投诉,问了技术人员说是 主要考量是方便短信api用户 目前sdk 已连夜更新 彻底删除通讯录读取功能
但是实际上并非如此。
来,我们一个一个 SDK 看过去。
分析
因为部分版本已经被更新过,新版的确是 没有 通讯录读取功能,我们这里以老版本来分析。
有些不了解的读者可能会觉得更新了 SDK 就好了呀,但是其实不是这样子的:SDK 是嵌入在 App 里的,要 App 的开发者更新了,提交到 App Store 之后,终端用户更新了才不会继续被窃取通讯录,但是众所周知苹果审核 App 速度并不快,所以还会有很多使用了这个 SDK 并且用户允许访问通讯录的 App 被利用,继续向聚合数据的服务器上传通讯录。
另外,聚合数据服务器上用于接收上传的通讯录的接口并没有被删掉,而是仍然能够正常处理数据的。你就算为了骗我伪造个 404 也好呀~连伪装都不伪装一下,真是不要脸(不过也很有可能是智商低XD
聚合数据 SDK (iOS SDK v1.0.2)
使用 strings 很清楚的可以看到:
1 | # strings JuheApis | grep -b1 "openid=" |
z@J:70=o*O%~!8-i
就是他们加密请求用的密钥啦。
所以,说好的 数据都进行了严格的加密处理
其实就是我们数据加密了但是我们把密钥明文放在里面了哟要看自己拿哟么么哒~
值得一提的是,代码里把用于 AES 的函数命名为了 DESEncrypt
,而早期版本中则是直接有一个 libAes.so
的文件,不得不问一下:是不是为了窃取用户数据然后想改下函数名迷惑下反编译的人呢?可是 … 请问你知道汇编嘛?
因为看汇编代码有点头疼,以下给出的代码均为反编译后生成的 C 代码,并非我拿到了他们的源代码哦。
初始化部分:
1 | // JHHTTPClient + (void)initJHHTTPClient:(int) OpenId:(id) Path:(id) URLRoot:(id) Method:(id) Key:(id) Success:(id) Failure:(id) |
里面的
1 | objc_msgSend(v39, selRef_fetchAddressBookInfo_, v32); |
就是用来获取通讯录的代码。
以下是执行 API 调用的部分:
1 | // JuheAPI - (void)executeWorkWithAPI:(id) parameters:(id) success:(id) failure:(id) |
可以清楚的看到,这里并没有判断是否是调用的短信 API,而判断的是有没有拿到通讯录,有的话,上传。
另外,就算是像他们所说的 方便短信api用户
:嗯我们把他曲解成和开发者共享用户隐私好啦,聚合数据的后台里也没有能看到的地方呀那你到底方便的是谁呐~
呐,拿到加密方式和密钥,解密也就很容易啦:
1 | #!/usr/bin/env php |
现在我们来解密它发送的三个请求:
1 | GET /initapi?0EF3CC39DB8ED6938DC47B2D626F4D1FAEA274211EB1A8FC1490F7FA0BFCD28ACE887174D7C22BAE74DB70A6197552C58E48B39A41B4C798B82E44F34F9EDCE1C3A92E1BC679C91D87CBD598CC36C52AA00294A748DCCE0DF84704E73D00C6A7B8299ED6ED854906B3E72C1028873F7E8FBF1C38A799E059C80AD0A76836C8FB3CA9B410DC41B30394F76CFF4FD321783EDE69D322CA8658FF982E7BD77FAF69C7D798338B01BC62AAD9F20C426642ABE4E6B8DCDDE379ECA2F5188990E2F9FB0E9489AEB47955DC00DE2AD752F06B6BAA18141CBB1C047356D41A8984EE16790C1C18C765A46B8625FCC9E08C3A981226F7BC31422DF0CF446789B808A9003AD73133CCE0A2C79B38EB1A4D92FCF8FBB0C47186418D2EF55E96D2569D544AB84983EB013C2A5FD3B1EBC5EE6234652FE4ABBDE85FE04B5A6372A4004AD74622 HTTP/1.1 |
解密后是一串 querystring,为了看得更清楚一些我这里以 JSON 的格式显示:
1 | { |
发送了我的 IDFA、位置、手机型号、运营商、IMEI、系统版本和网络制式。其中位置信息由于没有请求位置权限,所以显示的是 0.000000。
这里的都还好,下一个请求:
1 | POST /initialize HTTP/1.1 |
解密后是一串格式化过的 JSON,说起来真是浪费空间呐,虽然有 gzip 也不要这样子嘛:
1 | { |
不用我再多说啥了吧?我给份对照表大家慢慢看。
由于里面的英文表达的不准确、大小写位置也有不正确的,可能引起强迫症患者不适,不过原文如此,我也没办法啦~
Parent key | Key | Description |
---|---|---|
- | disPlayName | 显示名称 |
listNoteinfo | noteinfo | 联系人备注 |
listAddress | street | 住址:街道 |
listAddress | city | 住址:城市 |
listAddress | region | 住址:省份 |
listAddress | postCode | 住址:邮编 |
listAddress | formatAddress | 格式化后的住址 |
- | contactId | 联系人 ID |
listEmail | emailType | E-mail 类型 |
listEmail | emailValue | E-mail 地址 |
listPhone | phoneType | 电话号码类型 |
listPhone | phoneNumber | 电话号码 |
listNickname | nickname | 昵称 |
listOrganizations | company | 公司 |
listOrganizations | title | 职位 |
短信验证码 SDK (iOS SDK v1.0.1)
以下的我都偷懒直接 strings 大法好了:
1 | # strings SMSSDKDemo/SMSSDK.framework/SMSSDK | grep -i SMSAddressBook |
这个版本最诚实,uploadSMSAddressBook
就直接当函数名了啊哈哈~
条码数据 SDK (iOS SDK v1.0.2)
1 | # strings Barcode_iOSSDK_v1.2/barcode/demoBarcode/JuheBarcode.framework/Versions/A/JuheBarcode | grep -i SMSAddressBook |
会上传。
比价数据 SDK (iOS SDK v1.0.0)
1 | # strings bijia.sdk.v1.0/JuhePrice.framework/Versions/A/JuhePrice| grep -i SMS |
没有。
电商数据 SDK (iOS SDK v1.0.0)
1 | # strings dianshang.sdk.v1.0/JuheEBusiness.framework/Versions/A/JuheEBusiness | grep -i SMS |
没有。
聚合基站 (2.3.4)
会上传通讯录。
1 | POST /initialize HTTP/1.1 |
解密后结果:
1 | { |
你查查 (1.6.4)
会上传通讯录。
1 | POST /initialize HTTP/1.1 |
解密后结果:
1 | { |
受影响列表
为了方便检索,我将会上传通讯录的 SDK / App 列表整理在这里。
注:
- SDK 的下载地址和更新时间为 iOS 版本里库文件的更新时间;
- App 的更新时间为 App Store 里显示的时间。
名称 | 版本号 | 更新时间 | 是否上传 | 下载地址 |
---|---|---|---|---|
聚合数据 SDK | 1.0.1 | 2014-09-23 | 是 | |
短信验证码 SDK | 1.0.1 | 2014-11-01 | 是 | 官方下载 MEGA |
条码数据 SDK | 1.0.2 | 2014-09-23 | 是 | 官方下载 MEGA |
比价数据 SDK | 1.0.0 | 2014-03-10 | 否 | 官方下载 MEGA |
电商数据 SDK | 1.0.0 | 2014-03-10 | 否 | 官方下载 MEGA |
你查查 | 1.6.4 | 2014-10-09 | 是 | App Store |
聚合基站 | 2.3.4 | 2014-10-17 | 是 | App Store |
另外,聚合数据首页上链接过去的 聚合数据 iOS 项目开发实战:条码查询器 内课程资料里的 Demo 亦包含会上传通讯录的代码。这里限于版权原因就不提供下载了。
结尾
最后,请聚合数据尽快删除存储在服务器上的用户隐私数据并公开承认且道歉。
以及,贵司不如帮员工报个新东方什么的英语培训班?如果新东方救不了的话,不如试试找老罗?代码里各种 typo 看的我都快吐了 :-(