版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.06.07 星期五 |
前言
在这个信息爆炸的年代,特别是一些敏感的行业,比如金融业和银行卡相关等等,这都对
app
的安全机制有更高的需求,很多大公司都有安全 部门,用于检测自己产品的安全性,但是及时是这样,安全问题仍然被不断曝出,接下来几篇我们主要说一下app
的安全机制。感兴趣的看我上面几篇。
1. APP安全机制(一)—— 几种和安全性有关的情况
2. APP安全机制(二)—— 使用Reveal查看任意APP的UI
3. APP安全机制(三)—— Base64加密
4. APP安全机制(四)—— MD5加密
5. APP安全机制(五)—— 对称加密
6. APP安全机制(六)—— 非对称加密
7. APP安全机制(七)—— SHA加密
8. APP安全机制(八)—— 偏好设置的加密存储
9. APP安全机制(九)—— 基本iOS安全之钥匙链和哈希(一)
10. APP安全机制(十)—— 基本iOS安全之钥匙链和哈希(二)
11. APP安全机制(十一)—— 密码工具:提高用户安全性和体验(一)
12. APP安全机制(十二)—— 密码工具:提高用户安全性和体验(二)
13. APP安全机制(十三)—— 密码工具:提高用户安全性和体验(三)
14. APP安全机制(十四) —— Keychain Services API使用简单示例(一)
15. APP安全机制(十五) —— Keychain Services API使用简单示例(二)
16. APP安全机制(十六) —— Keychain Services API使用简单示例(三)
开始
首先看下写作环境
Swift 5, iOS 12, Xcode 10
在本教程中,您将学习如何使用SSL Pinning
和Alamofire
防止中间人攻击。 您将使用Charles Proxy
工具模拟中间人攻击。
如今,大多数iOS应用程序与服务器通信以检索要使用的信息。当应用交换信息时,它们通常使用传输层安全性(TLS)
协议来提供安全通信。
应用程序通常不会确定在尝试与服务器建立连接时要信任哪些证书以及哪些证书不信任。相反,它们完全依赖于iOS包含的证书。
即使TLS保护传输的数据免遭窃听和篡改,攻击者也可以使用黑客或自签名证书设置中间人攻击。通过这些证书,他们可以捕获移入和移出应用程序的数据。
在本教程中,您将学习如何使用SSL Certificate Pinning
和Alamofire 5防止中间人攻击。要验证您的实现是否按预期起作用,您将使用Charles Proxy
的man-in-the-middle
策略。
注意:安全套接字层
(Secure Sockets Layer - SSL)
是TLS
的始祖。TLS
解决了 Internet Engineering Task Force (IETF)发现的各种安全漏洞,这些漏洞影响了SSL 3.0
版。在本教程中,您应该将SSL
和TLS
作为同义词阅读,但代码实现应始终使用TLS
。
在本教程中,您将使用PinMyCert
,这是一个使用Stack Exchange REST API
检索Stack Overflow
用户的iOS应用程序。
首先使用下载好的起始程序,在Xcode中打开PinMyCert.xcodeproj
。
为了让您专注,初学者项目已经为您设置了与SSL Certificate Pinning
无关的所有内容。
打开Main.storyboard
并查看其中包含的视图控制器。左侧的视图控制器是应用程序的根导航控制器。接下来,您将拥有ViewController
,其中包含一个列出从Stack Overflow
检索到的用户的表。最后,您有DetailViewController
,它显示所选用户的详细信息。
ViewController
使用NetworkClient
。这是Alamofire
的一个包装器,它公开了一个执行网络请求的API。在NetworkClient
中,您将实现处理SSL Certificate Pinning
的逻辑。稍后会详细介绍。
构建并运行应用程序,您将看到此初始屏幕:
在深入研究代码之前,让我们来谈谈TLS
吧!
了解TLS
要了解SSL Certificate Pinning
,您应该首先掌握TLS的本质及其加密基础。
TLS的主要目标是为双方之间交换的消息增加隐私和完整性。 换句话说,TLS允许您通过网络传输数据,而不会将数据暴露给不受信任的第三方。
当客户端和服务器需要TLS连接时,构建该连接遵循三个阶段,按特定顺序执行。
1. The Three Phases of TLS Connections
在第一阶段,客户端启动与服务器的连接。
然后,客户端向服务器发送一条消息,该消息列出了它可以支持的TLS
版本以及它可用于加密的密码套件。
注意:密码套件是一组通过TLS保护网络连接所需的算法。有关详细信息,请参阅cipher suite。
服务器使用选定的密码套件进行响应,并将一个或多个数字证书(digital certificates)
发送回客户端。
客户端验证这些数字证书(简称证书)是否有效。它还验证服务器是真实的,而不是想要窥探敏感信息的人。
如果验证成功,则验证的第二阶段开始。客户端生成预主密钥(pre-master secret key)
并用服务器的公钥(即证书中包含的公钥)对其进行加密。
服务器接收加密的预主密钥并用其私钥解密。服务器和客户端各自基于预主密钥生成主密钥(master secret key)
和会话密钥。
注意:第二阶段使用公钥加密public-key cryptography或非对称加密
(asymmetric cryptography)
。这是一个使用密钥对的加密系统:广泛传播的公钥和只有所有者知道的私钥。
然后在最后阶段使用该主密钥来解密和加密两个参与者交换的信息。
注意:第三阶段使用对称密钥加密(symmetric-key cryptography),其中您使用相同的密钥来加密明文和解密密文。
2. About Digital Certificates
正如您在上一节中所了解到的,服务器将一个或多个证书发送回客户端。
那么,什么是证书? 证书是封装有关拥有证书的服务器的信息的文件。 它类似于身份证,例如护照或驾驶执照。
证书颁发机构(Certificate Authority (CA))可以颁发证书,也可以自签名。 在第一种情况下,CA
必须在颁发证书之前以及应用程序使用证书时验证证书持有者的身份。 在第二种情况下,其证明其身份的同一实体签署证书。
3. The Structure of a Digital Certificates
证书的结构使用X.509标准X.509 standard。 以下是主要字段:
- Subject:提供CA颁发证书的实体(计算机,用户,网络设备等)的名称。
- Serial Number:为CA颁发的每个证书提供唯一标识符。
- Issuer:为颁发证书的CA提供唯一的名称。
- Valid From:提供证书生效的日期和时间。
- Valid To:提供证书不再有效的日期和时间。
- *Public Key:包含与证书一起使用的密钥对的公钥。
- Algorithm Identifier:表示用于签署证书的算法。
- Digital Signature:用于验证证书真实性的位字符串。
由公钥和算法标识符组成的对象表示主题公钥信息(subject public key info)
。
X.509
证书的编码方式可能不同,这会影响它们的外观。 最常见的是:
-
Privacy Enhanced Mail (PEM):
Base-64
编码,其文件扩展名为.pem
。 -
Distinguished Encoding Rules (DER):二进制编码,其文件扩展名为
.cer,.der
和.crt
。 -
Public Key Cryptography Standards (PKCS)公钥加密标准(PKCS):用于在单个文件中交换公共和私有对象。 它的扩展名是
.p7b,.p7c,.p12,.pfx
等。
4. 验证数字证书
从CA
获得证书时,该证书是信任链(chain of trust)或证书链的一部分。
链中的证书数量取决于CA的层次结构。 双层层次结构是最常见的。 颁发CA签署用户证书,根CA签署颁发CA证书。 根CA是自签名的,应用程序必须在最后信任它。
在证书验证期间,应用验证:
- 评估日期必须介于证书的
Valid From
和Valid To
字段之间,以使证书有效。 - 数字签名,通过查找下一个颁发CA或中间CA的公钥。 该过程一直持续到它到达根证书。
注意:iOS会将所有知名的根CA证书保留在其
Trust Store
中。 如果您想了解预装iOS的受信任根证书,请参阅Apple在iOS中可用的受信任根证书列表(Apple’s lists of available trusted root certificates in iOS)。
SSL Certificate Pinning Under the Hood
SSL Certificate Pinning
或简称为pinning
,是将主机与其证书或公钥相关联的过程。一旦知道了主机的证书或公钥,就可以将其固定到该主机上。
换句话说,您将应用程序配置为拒绝除一个或几个预定义证书或公钥之外的所有证书。每当应用程序连接到服务器时,它都会将服务器证书与固定证书或公钥进行比较。当且仅当它们匹配时,应用程序信任服务器并建立连接。
您通常在开发时添加服务的证书或公钥。换句话说,您的移动应用应该在应用包中包含数字证书或公钥。这是首选方法,因为攻击者无法污染pin
。
1. Why Do You Need SSL Certificate Pinning?
通常,您将设置和维护TLS
会话委托给iOS。这意味着当应用程序尝试建立连接时,它不会确定要信任哪些证书以及哪些证书不信任。该应用程序完全依赖于iOS Trust Store
提供的证书。
但是,此方法存在缺陷:攻击者可以生成自签名证书并将其包含在iOS Trust Store
中或破解根CA证书(hack a root CA certificate)。这允许这样的攻击者设置一个中间人攻击并捕获移入和移出应用程序的传输数据。
通过固定限制受信任证书集可防止攻击者分析应用程序的功能及其与服务器通信的方式。
2. Types of SSL Certificate Pinning
如果你想实现固定(pinning)
- 你似乎在做,因为你正在阅读本教程 - 你可以决定两个选项:
- Pin the certificate - 固定证书:您可以下载服务器的证书并将其捆绑到您的应用程序中。在运行时,应用程序会将服务器的证书与您嵌入的证书进行比较。
- Pin the public key - 固定公钥:您可以检索证书的公钥,并将其作为字符串包含在您的代码中。在运行时,应用程序会将证书的公钥与代码中的硬编码进行比较。
在这两个选项之间进行选择取决于您的需求和服务器配置。如果您选择第一个选项,则需要在服务器旋转(更改)其证书时上传您的应用程序,否则它将停止工作。如果选择第二个选项,则可能违反密钥轮换策略,因为公钥不会更改。
注意:除了固定证书或公钥之外,还可以固定
subject
公钥信息。在撰写本文时,Alamofire
无法执行此类pinning
。如果您正在寻找这样的解决方案,请参阅TrustKit。
现在你已经掌握了pinning
的工作原理,现在是时候看看Alamofire 5
能为你做些什么了!
Pinning in Alamofire 5
Alamofire 5
支持证书和公钥的固定。 特别是,它提供了两个不同的类,分别称为PinnedCertificatesTrustEvaluator
和PublicKeysTrustEvaluator
,它们允许您处理这些情况。
注意:此后,本教程仅涵盖证书固定
(certificate pinning)
。 如果您愿意,可以在完成教程后使用公钥固定(public key pinning)
的实现。
1. Storing The Certificate
要查看Alamofire 5
的运行情况,首先需要从StackExchange.com
下载证书。
使用OpenSSL
从Stack Overflow
服务器检索证书。 更具体地说,您将使用s_client
命令,该命令可以通过指定服务器地址和端口443
连接到任何服务器上的SSL
。
打开一个新终端并键入cd
后跟一个空格。 然后,拖放您的入门项目的目录,然后按键盘上的Enter
键。
仍然在终端窗口中,键入cd PinMyCert
以移动到项目的根文件夹中。
接下来,复制并粘贴以下代码段:
openssl s_client -connect api.stackexchange.com:443 </dev/null
完成后,您将收到大量数据,包括证书列表。 链中的每个证书都有一个公用名Common Name (CN)
。
Certificate chain
0 s:/C=US/ST=NY/L=New York/O=Stack Exchange, Inc./CN=*.stackexchange.com
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA
1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
在此之下,您可以看到您感兴趣的实际证书,其中CN
是* .stackexchange.com
。
Server certificate
-----BEGIN CERTIFICATE-----
MIIHMjCCBhqgAwIBAgIQBmgM1QeOzDnM9C33n9PrfTANBgkqhkiG9w0BAQsFADBw
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
dXJhbmNlIFNlcnZlciBDQTAeFw0xNjA1MjEwMDAwMDBaFw0xOTA4MTQxMjAwMDBa
MGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJOWTERMA8GA1UEBxMITmV3IFlvcmsx
HTAbBgNVBAoTFFN0YWNrIEV4Y2hhbmdlLCBJbmMuMRwwGgYDVQQDDBMqLnN0YWNr
ZXhjaGFuZ2UuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0YD
zscT5i6T2FaRsTGNCiLB8OtPXu8N9iAyuaROh/nS0kRRsN8wUMk1TmgZhPuYM6oF
S377V8W2LqhLBMrPXi7lnhvKt2DFWCyw38RrDbEsM5dzVGErmhux3F0QqcTI92zj
VW61DmE7NSQLiR4yonVpTpdAaO4jSPJxn8d+4p1sIlU2JGSk8LZSWFqaROc7KtXt
lWP4HahNRZtdwvL5dIEGGNWx+7B+XVAfY1ygc/UisldkA+a3D2+3WAtXgFZRZZ/1
CWFjKWJNMAI6ZBAtlbgSNgRYxdcdleIhPLCzkzWysfltfiBmsmgz6VCoFR4KgJo8
Gd3MeTWojBthM10SLwIDAQABo4IDzDCCA8gwHwYDVR0jBBgwFoAUUWj/kK8CB3U8
zNllZGKiErhZcjswHQYDVR0OBBYEFFrBQmPCYhOznZSEqjIeF8tto4Z7MIIB/AYD
VR0RBIIB8zCCAe+CEyouc3RhY2tleGNoYW5nZS5jb22CEXN0YWNrb3ZlcmZsb3cu
Y29tghMqLnN0YWNrb3ZlcmZsb3cuY29tgg1zdGFja2F1dGguY29tggtzc3RhdGlj
Lm5ldIINKi5zc3RhdGljLm5ldIIPc2VydmVyZmF1bHQuY29tghEqLnNlcnZlcmZh
dWx0LmNvbYINc3VwZXJ1c2VyLmNvbYIPKi5zdXBlcnVzZXIuY29tgg1zdGFja2Fw
cHMuY29tghRvcGVuaWQuc3RhY2thdXRoLmNvbYIRc3RhY2tleGNoYW5nZS5jb22C
GCoubWV0YS5zdGFja2V4Y2hhbmdlLmNvbYIWbWV0YS5zdGFja2V4Y2hhbmdlLmNv
bYIQbWF0aG92ZXJmbG93Lm5ldIISKi5tYXRob3ZlcmZsb3cubmV0gg1hc2t1YnVu
dHUuY29tgg8qLmFza3VidW50dS5jb22CEXN0YWNrc25pcHBldHMubmV0ghIqLmJs
b2dvdmVyZmxvdy5jb22CEGJsb2dvdmVyZmxvdy5jb22CGCoubWV0YS5zdGFja292
ZXJmbG93LmNvbYIVKi5zdGFja292ZXJmbG93LmVtYWlsghNzdGFja292ZXJmbG93
LmVtYWlsghJzdGFja292ZXJmbG93LmJsb2cwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRw
Oi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzUuY3JsMDSgMqAw
hi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzUuY3Js
MEwGA1UdIARFMEMwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMIGDBggrBgEFBQcBAQR3MHUw
JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBNBggrBgEFBQcw
AoZBaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkhpZ2hB
c3N1cmFuY2VTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsF
AAOCAQEARIdUz7n08ZtqWscAmTXegtB6yPrU0l5IQCXQRqnEVXPKyS+w8IVOcblT
T/W2Qlp5we2BTDbRDfVokXIOSxOTAT0XN3f3c+nbvKJ3XMBH236846AY6bpfqL0/
05gcdt39d2iXTL+qnJW9P0yFKpkfGXBBTYQl4ACSeThSuSBXIVJ0v/TfR9+ggXuP
pmXiIKkPOReKu2Tu8SO7+5KRqRJvYhP9mhL4Bl+YLrTQXzM1NwVAahRT1QJJNemy
yEY1kkZOCKt0xRu4CVWhJlpNdoRZenT9BrD8Fo22kt5MxAvCVrjT/g1BHDQd4S8p
PKC8kRwmMA8mdo8TiHJQMy0DBCDCDg==
-----END CERTIFICATE-----
subject=/C=US/ST=NY/L=New York/O=Stack Exchange, Inc./CN=*.stackexchange.com
issuer=/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA
要将证书复制到文件中,请再次使用openssl
。 重复上一个命令并将其输出传递给openssl x509
,指定DER
编码并将其输出到名为stackexchange.com.der
的新文件:
openssl s_client -connect api.stackexchange.com:443 </dev/null \
| openssl x509 -outform DER -out stackexchange.com.der
如果您已正确执行这些步骤,则应该能够在项目的同一文件夹中看到该证书。
2. Implementing Certificate Pinning
在编写代码之前,您需要导入先前下载的证书。 如果您还没有打开它,请在Xcode中打开PinMyCert.xcodeproj
。
右键单击Project导航器中的根PinMyCert
文件夹。 单击Add Files to “PinMyCert”...
,然后在文件选择器中,找到并选择stackexchange.com.der
并单击Add
。
打开NetworkClient.swift
并将以下代码粘贴到文件末尾:
struct Certificates {
static let stackExchange =
Certificates.certificate(filename: "stackexchange.com")
private static func certificate(filename: String) -> SecCertificate {
let filePath = Bundle.main.path(forResource: filename, ofType: "der")!
let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
let certificate = SecCertificateCreateWithData(nil, data as CFData)!
return certificate
}
}
上面的结构提供了一种用户友好的方法来从主包中检索证书。
SecCertificateCreateWithData
负责从DER
编码的文件创建证书对象。
仍在NetworkClient.swift
中,找到NetworkClient
并用以下代码替换整行let session = Session.default
:
// 1
let evaluators = [
"api.stackexchange.com":
PinnedCertificatesTrustEvaluator(certificates: [
Certificates.stackExchange
])
]
let session: Session
// 2
private init() {
session = Session(
serverTrustManager: ServerTrustManager(evaluators: evaluators)
)
}
以下是上述代码的细分:
- 1) 您创建一个名为
evaluateators
的字典,其中包含一个键值对。 键的类型为String
,它表示要检查的主机。 该值是ServerTrustEvaluating
的子类型,名为PinnedCertificatesTrustEvaluator
。 它描述了您要为该特定主机应用的评估策略。 您将使用PinnedCertificatesTrustEvaluator
来验证服务器信任。 如果固定证书与服务器证书完全匹配,则服务器信任有效。 - 2) 您声明了一个使用
ServerTrustManager
实例化Session
的私有初始化程序。 后者负责管理evaluators
字典中声明的映射。
现在,打开ViewController.swift
并找到负责网络请求的代码:
NetworkClient.request(Router.users)
.responseDecodable { (response: DataResponse<UserList>) in
switch response.result {
case .success(let value):
self.users = value.users
case .failure(let error):
self.presentError(withTitle: "Oops!", message: error.localizedDescription)
}
}
用这个新实现替换它:
NetworkClient.request(Router.users)
.responseDecodable { (response: DataResponse<UserList>) in
switch response.result {
case .success(let value):
self.users = value.users
case .failure(let error):
let isServerTrustEvaluationError =
error.asAFError?.isServerTrustEvaluationError ?? false
let message: String
if isServerTrustEvaluationError {
message = "Certificate Pinning Error"
} else {
message = error.localizedDescription
}
self.presentError(withTitle: "Oops!", message: message)
}
}
虽然成功案例仍然相同,但您已经通过附加条件丰富了失败案例。 首先,您尝试将error
转换为AFError
。 如果转换成功,您将评估isServerTrustEvaluationError
。 如果其值为true
,则表示证书锁定失败。
构建并运行应用程序。 什么都不应该在视觉上改变。
可是等等! 如果这是一个教你如何防止中间人攻击的教程,那么如果没有发生攻击,你怎么能确定你已经完成了所有事情?
要回答这个问题,请直接跳到下一部分。
Testing Certificate Pinning With Charles
为了验证一切都按预期运行,您首先需要下载最新版本的Charles Proxy the latest version of Charles Proxy,在撰写本文时版本为4.2.8
。双击DMG
文件并将Charles
图标拖到Applications
文件夹进行安装。
Charles
是一个代理服务器,一个中间件,位于您的应用程序和计算机的网络连接之间。您可以使用Charles配置网络设置以通过它路由所有流量。这使Charles
可以检查进出计算机的所有网络事件。
代理服务器处于强大的地位,这也意味着它们有可能被滥用。这就是TLS
如此重要的原因:数据加密可以防止代理服务器和其他中间件窃听敏感信息。
Charles生成自己的自签名证书,您可以在Mac和iOS设备上安装这些证书以进行TLS
加密。由于此证书不是由受信任的证书颁发者颁发的,因此您需要告知您的设备明确信任它。安装并信任后,Charles
将能够解密SSL
事件。
但是,在您的情况下,Charles不会窥探您的SSL
消息。查尔斯偷偷摸摸的中间人策略是行不通的,因为你的pinning
策略会阻止它。
1. Certificate Pinning in Action
要查看增强的安全性,请启动Charles
。 Charles一启动就开始记录网络事件。
在Charles
中,首先切换到Sequence
选项卡。 然后在过滤器框中输入api.stackexchange.com
以便更容易找到所需的请求,并单击扫帚符号以清除当前会话。
现在单击菜单栏上的Proxy
并选择macOS Proxy
将其重新打开(如果它尚未显示复选标记)。
然后,单击Proxy ▸ SSL Proxying Settings
并将api.stackexchange.com
添加到列表中。 您可以将Port
字段留空。 选择Enable SSL Proxying
,然后单击OK
进行确认。
注意:完成测试后,请记住取消选择
Enable SSL Proxying
。 否则,您将无法正常运行该应用程序。
接下来,您需要安装Charles Proxy SSL
证书以允许在Simulator
中代理SSL请求。
在Charles
中,单击Help ▸ SSL Proxying ▸ Install Charles Root Certificate in iOS Simulators
。 然后,在模拟器上,打开Settings
应用。 点按 General ▸ About ▸ Certificate Trust Settings
(它位于底部,因此您可能需要滚动)。 点击开关以打开Charles Proxy CA
,然后在出现的警告对话框中点击Continue
。
回到Xcode,构建并运行项目。 您应该看到如下警告:
在Charles
一侧,您应该看到如下所示的失败:
恭喜! 你现在有一个能够防止中间人攻击的应用程序!
当您完成对应用程序和Charles的试验后,从模拟器中删除Charles CA
证书非常重要。 启用模拟器后,从菜单中选择Hardware ▸ Erase All Content and Settings...
,然后单击Erase
。
如果您想了解有关SSL证书固定和安全性的更多信息,请查看OWASP Mobile Security Testing Guide。 它是一份全面的测试指南,涵盖了移动应用安全测试期间使用的流程,技术和工具。
后记
本篇主要讲述了阻止使用SSL Pinning 和 Alamofire的中间人攻击,感兴趣的给个赞或者关注~~~