装箱百万奖金,第六届全国工业互联网数据创新应用大赛火热报名中! 了解详情
写点什么

为什么说在移动 App 中使用 OAuth API 密钥是不安全的?

  • 2019-01-30
  • 本文字数:2970 字

    阅读完需:约 10 分钟

为什么说在移动App中使用OAuth API密钥是不安全的?

对于移动应用程序来说,使用密钥来访问后端 API 服务来获取数据是非常常见的。那么,如何将密钥安全地包含在移动应用程序中呢?往短了说,你做不到。往长了说,请看这篇文章的剩余部分。



保存在本地和移动应用程序中的密钥被提取

从 JavaScript 应用程序中提取 API 密钥非常简单。只要你加载包含 JavaScript 应用程序的网页,浏览器就会下载整个源代码。你所要做的就是单击“查看源代码”,就可以看到整个源代码(包括 API 密钥)。JavaScript 代码通常会被压缩或缩小,看懂源代码可能并没有那么容易,但应用程序中定义的所有字符串都是可见的。


对于原生移动应用程序来说也是一样。这些应用程序在运行之前也会被下载到设备上,只是你下载的是二进制文件,而不是未编译的源代码。


让我们来做一个有趣的快速演示。如果你使用的是 Mac,并且安装了 1Password,请运行下面这个命令。你可以针对 Mac 上的任意一个二进制文件运行这个命令,但 1Password 应用程序中恰好有一些非常容易读懂的数据。


strings /Applications/1Password\ 7.app/Contents/MacOS/1Password\ 7 | grep 1Password
复制代码


strings 命令将会显示二进制文件的所有嵌入字符串,然后我们使用 grep 查找与 1Password 匹配的字符串,结果得到了在应用程序中使用的一堆文本!


...Restarting 1Password...1Password failed to connect to 1PasswordPlease quit 1Password and start it again.Add Account to 1PasswordAn update to Safari is required in order to use the 1Password extension.Welcome to 1Password!...
复制代码


如果应用程序中嵌入了 API 密钥,strings 命令也会将它们显示出来。


让我们从头开始写一个简单的程序来证明这一点。我们将编写一个输出“hello world”的 C 语言程序。首先,创建一个叫作 hello.c 的文件,并写入以下内容:


#include <stdio.h>
char hello[] = "hello";char world[] = "world";
int main(){ printf("%s %s", hello, world); return 0;}
复制代码


在这段代码中,我们声明一个叫作 example 的字符串,值为“hello world”,然后在程序运行时打印它。你可以使用任何一个 C 语言编译器(如 gcc)编译它:


$ gcc hello.c
复制代码


结果会得到一个叫作 a.out 的二进制文件,然后你可以运行它:


$ ./a.outhello world
复制代码


让我们针对这个二进制文件运行 strings 命令:


$ strings a.out%s %shelloworld
复制代码


它很容易就能发现二进制文件中的文本。


现在你可能会想:“如果我将 API 密钥分成几个部分并将它们分散在代码中会怎样?”这可能会为你赢得一些时间,但仍然会被一个真正有决心的人找出来。


即使是 Twitter 也无法阻止这种情况发生!2013 年,Twitter 官方应用程序的 API 密钥就是通过这种方式泄露的,让攻击者可以冒充合法的 Twitter 应用程序。


关键问题在于,一旦你将包含应用程序需要使用的字符串的内容发送给用户,总有人会提取它们。解决这个问题唯一的方法是使用“硬件安全模块”,秘钥被存储在微处理器中,微处理器无法通过编程的方式提取任密钥,它可以对数据进行加密签名,而不是发送密钥本身。


总的来说,如果你以未编译或二进制形式向用户发送代码,他们就有可能看到其中的内容。


HTTPS 请求在移动应用程序端被拦截

即使你认为你使用了最厉害的混淆技术,并且确信没有人能够从应用程序二进制文件中提取密钥,但总有人可以通过另一种方式找到密钥。


与运行在数据中心服务器上的应用程序不同,移动应用程序运行在用户手中的设备上,经过各种网络。用户可能是通过自家的网络安装应用程序,然后连接到咖啡店的网络并打开它,又或者在通过身份认证之前连接到酒店的网络。这些网络都是不可信任的,并且很有可能会出问题,或者攻击者会试图拦截数据!


你可能会想:“HTTPS 会保护传输中的数据!”在正常情况下确实如此。只要应用程序在发出请求时正确验证 HTTPS 证书,处于手机和服务器之间的攻击者几乎不可能看到流量。


但是,我们担心的不是这个问题。如果有人愿意,他们可以为你的 API URL 提供自己的 HTTPS 证书,在请求离开手机之前拦截自己的 HTTPS 连接。网上有很多教程教你如何做到这一点!实际上,这一项很好的技术,在开发应用程序时,可以用它来测试自己的应用程序,也是人们对 Instagram 等私有 API 进行反向工程的常见方式。


如果你有兴趣尝试一下,可以看看 Charles Proxy(https://www.charlesproxy.com/)或者免费的 mitmproxy(https://mitmproxy.org/)。


在手机上安装了自己的证书授权程序后,它就可以为任何一个域颁发 HTTPS 证书,对于你的手机来说,一切看起来都很正常。只是你的手机实际上是在向运行在笔记本电脑上的软件发出 HTTPS 请求,然后你的笔记本电脑再向真实的 API 发起新的 HTTPS 请求。这样你的笔记本电脑就可以看到手机发送给 API 的所有内容。


当然,攻击者不会通过这种方式随机拦截用户的传输数据,但如果有人想要知道应用程序使用的密钥,他们就可以通过这种方式轻松查看应用程序通过网络发送的所有数据。这意味着尽管你尽最大努力在源代码中隐藏应用程序密钥,仍然会在通过网络传输时被拦截。


这与 OAuth 有什么关系?

我们已经看到了从移动应用中提取 API 密钥的两种方法,但这与 OAuth 有什么关系呢?


传统上,OAuth 2.0 应用程序在开发人员注册应用程序时会发出 client_id 和 client_secret。当应用程序在 Web 服务器上运行时,这没问题,因为应用程序用户永远无法访问源代码,因此无法查看密钥。但是,当我们在 JavaScript 或原生应用程序中使用 OAuth 2.0 时,显然会有问题,因为正如我们所看到的那样,它们没有保密的能力。


在 OAuth 1 中,每个 API 请求都需要使用一个密钥,这是它的主要缺点之一,也是它被 OAuth 2.0 取代的主要原因。OAuth 1 是在移动应用程序开始流行之前出现的,所以它并没有考虑到这些限制。


随着 OAuth 2.0 的出现,这种情况发生了变化,特别是在引入 PKCE(证明密钥交换)扩展之后。我喜欢把 PKCE 看作是一个“动态”的客户端密钥。PKCE 没有采用向移动应用程序传递 client_secret 的方式,而是在每次启动 OAuth 请求流时,应用程序都会创建一个新的随机密钥。这样就不存在需要提前传递的密钥,攻击者也没有什么东西可窃取。


OAuth 仍然会通过网络发送访问令牌,如果使用了 mitmproxy 之类的东西,那么它们对你仍然是可见的,但不同的是,这个令牌是动态发出的,并且特定于使用它的用户!这样一来,源代码中就没有密钥了,如果有人想从他们自己的设备上拦截流量,他们看到的只是一个访问令牌!他们无法访问应用程序已经无法访问的东西。


如何保护移动应用中的密钥

希望你现在已经了解为什么在移动应用程序中发布 API 密钥或其他密钥是不安全的。那么你会怎么做呢?


OAuth 解决了这个问题,它没有向移动应用程序传递任何密钥,而是让用户参与获取应用程序访问令牌的过程。这些访问令牌在用户每次登录时都是唯一的。PKCE 扩展提供了在移动应用程序上安全执行 OAuth 请求流的解决方案,即使没有使用预先准备的密钥。


如果你需要从移动应用程序中访问 API,但愿可以支持 OAuth 和 PKCE!幸运的是,有关 PKCE 的大部分繁琐的任务都是由像 AppAuth 这样的 SDK 来处理的,所以你不需要自己编写所有的代码。如果你使用的是像 Okta 这样的 API,那么 Okta 的 SDK 会自动执行 PKCE,你完全不需要操心。


英文原文:https://developer.okta.com/blog/2019/01/22/oauth-api-keys-arent-safe-in-mobile-apps


2019-01-30 10:066866
用户头像

发布了 731 篇内容, 共 405.6 次阅读, 收获喜欢 1966 次。

关注

评论

发布
暂无评论
发现更多内容

容器最佳实践系列-查看docker的文件系统

混沌畅想

浅谈BU安全建设

I

项目管理 企业安全 BU安全 安全建设

Apache之道在腾讯的探索与实践

腾源会

Apache 开源 腾源会 腾讯开源

vivo 全球商城:优惠券系统架构设计与实践

vivo互联网技术

服务器 架构设计

万字长文讲透低代码

百度开发者中心

最佳实践 开发者 方法论 低代码 语言 & 开发

C++ Vector

若尘

c++ vector 8月日更

TCP如何进行拥塞控制

W🌥

计算机网络 TCP/IP 8月日更

我看 JAVA 之 并发编程【三】java.util.concurrent.atomic

awen

Java 并发编程 Atomic 原子操作

听说你对explain 很懂?

艾小仙

sql 大前端

十万高层齐卸甲,竟无一人有慈心。前阿里员工看阿里高管不法侵害女员工事件

刘悦的技术博客

阿里巴巴 面试 职场 职场 PUA

AI + K8S 驱动存储技术变革

焱融科技

人工智能 Kubernetes 云原生 高性能 存储

Compose 编程思想

Changing Lin

8月日更

接口返回值一定不允许使用枚举类型吗?

skow

Java 面试 后端 开发规范

从0开始的TypeScriptの二:类型系统

空城机

JavaScript typescript 8月日更

使用Grafana显示Prometheu监控

Rubble

Grafana Prometheus 8月日更

从技术到文案,还回技术么?

escray

学习 极客时间 朱赟的技术管理课 8月日更

十大排序算法--桶排序

Ayue、

排序算法 8月日更

【“互联网+”大赛华为云赛道】EI命题攻略:华为云EI的能力超丰富,助你实现AI梦想

华为云开发者联盟

大数据 modelarts 大赛 互联网+ 华为云EI

iPhone Shortcuts 使用与场景

TroyLiu

iphone 效率工具 快捷指令 shortcuts nfc

【“互联网+”大赛华为云赛道】IoT命题攻略:仅需四步,轻松实现场景智能化设计

华为云开发者联盟

IoT 华为云 LiteOS 互联网+ IoT边缘

The Data Way Vol.1|风口下的开源市场:如何看待开源与商业的关系?

SphereEx

数据库 开源

带你看论文丨全局信息对于图网络文档解析的影响

华为云开发者联盟

文档 CNN网络 图网络 非结构化文档 全局信息

你真的懂语音特征吗?

华为云开发者联盟

语音 音频 声学 时域图 时域

孩子排斥写作业 VS 员工不接活儿——项目管理来帮忙

Ian哥

手撸二叉树之二叉搜索树中的搜索

HelloWorld杰少

数据结构与算法 8月日更

oeasy教您玩转vim - 11 - # 向前向后

o

工作多年,分享16条职场经验给新人朋友

架构精进之路

职场 成长 经验分享 8月日更

这波性能优化,太炸裂了!

why技术

Java 性能优化 JVM

Docker新手FAQ系列 1

混沌畅想

简单的Postman,还能玩出花?

码农参上

8月日更

Python代码阅读(第7篇):列表分组计数

Felix

Python 编程 Code Programing 阅读代码

为什么说在移动App中使用OAuth API密钥是不安全的?_安全_Aaron Parecki_InfoQ精选文章