在开发蓝象安全云的过程中,学习了iOS的生物验证登录技术(包括Touch ID 和 Face ID)。然后对比了一些常用的App的指纹登录功能,发现很多并没有充分利用iOS的安全能力。跟大家分享一下相关的知识,也希望我们使用的App也能够越来越安全。
登录验证
为什么要登录验证
早期的互联网服务大多是没有登录的,主要是静态内容,大家浏览。后来有一些简单的论坛,聊天室等动态服务,这个时候就需要密码进行身份认证了。再后来登录作为身份验证的方式就广泛应用到很多很多互联网服务了。
密码登录的安全问题
然而登录要做到安全非常不容易。账号密码一旦落入他人之手,就可能泄露所有信息,甚至被用来诈骗。密码登录的安全问题有哪些呢?
1. 使用HTTP登录验证,而不是HTTPS
很多知名的大公司,包括前些年QQ也采用这种方式登录,自己设计了方法来加密用户输入的密码,通过HTTP方式提交到服务器验证,后来也放弃了。
一定要使用HTTPS验证用户名和密码
一定要使用HTTPS验证用户名和密码
一定要使用HTTPS验证用户名和密码
2. 使用HTTPS登录验证,但是登录页面本身是HTTP
有些技术人员认为只要登录验证使用HTTPS,保证用户名密码不会泄露就安全了,登录框用HTTP也可以,还可以节省服务器资源,毕竟HTTPS握手很耗CPU。
这是一个很常见的错误,因为HTTP不安全,服务器通过HTTP发送给用户的网页,很容易被嵌入JS代码。大家对运营商嵌入网页的广告一点都不陌生吧,如果用户使用了不安全的无线路由器,或者没有密码保护的免费Wi-Fi,试想一下,如果被嵌入恶意JS代码,那输入的用户名密码就暴露了!暴露了!暴露了!
telcom-ads.jpg来源 http://news.ifeng.com/a/20170709/51399381_0.shtml
一定要使用HTTPS展示登录框
一定要使用HTTPS展示登录框
一定要使用HTTPS展示登录框
谁认识YY,搜狐负责登录的技术同学,麻烦转发给他们看看,谢谢!
sohu.png3. 用户名不存在
有的网站登录失败是因为用户名不存在,会返回一个用户名不存在
的提示消息,而不是像上面搜狐的错误那样子。有什么问题吗?
是的,如果一个网站会返回 用户名不存在
的错误消息,而用户名存在,但是密码错误,会提示另外一个消息,那么就给了大坏蛋一个信息,用户名是存在的,接下来就只需要破解密码了。要知道在今天这个信息泄露泛滥的世界里,我们的手机号、email地址基本上没有任何秘密而言了。
4. 明文保存密码
用户注册的密码怎么保存呢?直接放进数据库呗。很多没有经验的程序员开发网站往往会不假思索的把用户信息一起保存到数据库,验证的时候来一句SQL,搞定!
SELECT EXISTS (
SELECT * FROM login_details WHERE username = ? AND password = ?
)
好简单!也好不安全!
明文保存密码意味着开发人员,或者运维人员,甚至是公司其他工作人员可以直接读取用户密码!!!
意味着,一旦发生数据泄漏,大坏蛋就知道了很多人的密码!!!
还意味着,很多重复使用密码的用户,在很多其他平台的账号也暴露了!!!
5. Hash保存密码
既然不能明文保存密码,那就使用Hash函数加密保存密码呗。Hash函数可是单向的,就不信大坏蛋可以把密码还原出来。
这个,还真可以!
Hash密码是可逆的
Hash密码是可逆的
Hash密码是可逆的
是的,你没有看错,Hash密码是可逆的,虽然安全的Hash算法是不可逆的。
这是因为对同一个密码,用同一个Hash算法,比如MD5, SHA1,计算出来的结果总是一样的,你可以看看mysql.users表里面,相同的密码是不是都一样的?:)
这个世界上,就是有一些人吃饱了没事干,就在那里计算所有的密码Hash,而且还公开给人们下载。截至目前,9位以内所有字母数字组合的密码都被计算出来了,总共也就1TB的数据,随便一块硬盘就存下来了。你的密码有几位呢?
这么大的数据量,要通过Hash值反查密码也不容易吧?在彩虹表面前,这都不是事儿。
rainbow.png6. 多重混合Hash密码
一次Hash不行,那就多来几次吧,混起来Hash。是不是想到了很多这样的骚操作?
- md5(sha1(password))
- sha1(sha1(password))
- md5(sha1(md5(md5(password) + sha1(password)) + md5(password)))
这也不安全,事实上多重混合Hash通常会降低安全性。比如MD5已经被证明不安全了,SHA1相对来说要安全一些,组合使用反而降低了SHA1的安全能力。
7. 保存密码的正确姿势
所以呢?保存密码的正确姿势是怎样的?
先把密码腌制起来,然后再用Hash加密,直接上代码,
let salt = someRandomBytes
let saltedPassword = salt + password
let hash = SHA256(saltedPassword)
save(salt, hash)
先生成随机的salt,然后计算salt+password的Hash,最后保存salt和hash值。是的,salt是明文保存的,和hash保存在一起。
验证用户密码的时候,先把salt读出来,同样的方式计算hash,再和数据库中保存的hash对比。
是不是就天下太平了?还是有两个坑,
-
使用固定的salt
在程序里面写死一个随机数当作salt,以后都直接用省去了读数据库的麻烦,好轻松!But, But 这样子就起不到加盐的作用了。
一个用户固定一个salt?安全起见,每次用户重置密码,应该重新生成salt。
-
使用太短的salt
打个比方,如果salt只有1bit,那么计算出来的hash就只有2个可能的值,2bit就有4个可能的值,salt越短,计算出来的hash空间就越小,也就起不到很好的保护作用。
彩虹表都已经计算到10位密码了,salt建议至少128bit,256bit更好。
那是不是1024更好呢?没有必要,因为SHA256的hash值也只有256bit,并不会增加hash值的空间。salt不需要比hash值更长。
8. 再安全一点点
如果不介意验证密码的时候多消耗一些CPU的话,可以把普通的Hash函数换成非常耗时的Hash算法,比如PDKDF2, bcrypt等,这样攻击成本也会变得很高。
9. 撞库怎么办?弱密码怎么办?
worst-passwords.jpg来源 https://www.thesun.co.uk/tech/7978489/worst-passwords-most-common-2018/
应对撞库业界通用的做法是采用双因素验证,例如短信验证码、邮箱验证码,TOTP(基于时间的一次性密码)等。当然针对性的攻击,手机号克隆,破解邮箱等也时有发生,TOTP也无法防止内鬼和黑客拖库。钓鱼攻击被证明能够攻破google的防护。
密码不是唯一的登录验证方式
长期以来,互联网服务基本上都采用密码登录验证方案。众所周知,密码并不是安全的验证方案。然鹅,很多人不知道的是,密码也不是唯一的验证方案。
公钥登录验证
后台开发人员登录服务器通常都使用SSH密钥登录,Github用户也有不少使用SSH密钥更新代码,非常方便安全。
你家Linux服务器还在用密码登录?赶紧换SSH密钥登录。我在之前公司就直接关闭了Linux服务器上的密码验证,只能使用密钥。
ssh-authentication.jpg来源 https://blog.csdn.net/u011118321/article/details/54943855
简单来说,其原理是利用了公钥算法的特点,
- 先把用户的公钥放在服务器上
- 登录时,使用私钥加密(或者签名)一段随机数据
- 服务器使用用户的公钥解密(或者验证签名),对比解密结果是否正确。
公钥验证相比密码验证以及TOTP等有巨大的优势,数据库即使被内鬼或者大坏蛋拖库了,也只能够获得用户的公钥。而使用公钥只能够验证用户,而不能伪装用户登录。更无法通过获取到的公钥去撞库攻击其他平台。
iOS App 登录验证
iPhone最大的优势之一就是安全性,App沙盒机制保证了内部存储的数据也是安全的。
ios-sandbox.png来源 https://www.cse.wustl.edu/~jain/cse571-14/ftp/ios_security/index.html
所以不少App开发者直接使用 UserDefaults
把用户密码明文保存在 Preferences plist中,也有简单写文件保存的。
这里有两个安全问题需要注意,
- iPhone/iOS相对安全,但并不是一定永远安全。不少用户还在使用几年前的系统,这些旧系统可能有不少安全漏洞,可能导致数据泄露。当年苹果不配合FBI,后来FBI破解恐怖分子的iPhone 5C手机,就是利用了iOS9上面的一个漏洞。
- iOS App存储的文件默认会自动通过iCloud备份。iCloud的安全性一直不算很好,当年大表姐就深受其害,今年又出了支付宝盗刷事故。所以敏感文件一定要通过
isExcludedFromBackup
禁止备份。
iOS保存密码的正确姿势
如果一定要保存密码之类的敏感信息,应该使用iOS的 Keychain Services
服务来保存。iOS的 Keychain Services
并不是iCloud keychain,前者是给开发者存储App重要数据的服务,默认并不适用iCloud同步,后者是iCloud给用户提供的一个功能。这两个东西命名很糟糕,很容易混淆。
有点像SQL,需要先创建一个Query,再调用 API 操作,使用起来不算很复杂,Ctrl-C, Ctrl-V
static let server = "www.example.com"
let account = credentials.username
let password = credentials.password.data(using: String.Encoding.utf8)!
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
kSecAttrAccount as String: account,
kSecAttrServer as String: server,
kSecValueData as String: password]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
iOS 生物验证登录
终于到主题了
happy.gif早在2013年推出的iPhone 5S就支持了指纹支付,将生物验证的安全性提升了一大步。
苹果官方提供的iPhone安全架构,
secure-enclave.png当用户通过Touch ID 或者 Face ID 的验证时,Touch ID扫描用户的指纹,提取出指纹特征点的图片数据,发送给直接 Secure Enclave
验证,并不经过iOS操作系统, Secure Enclave
返回验证结果,再通过iOS通知给App。整个过程指纹数据都不会经过iOS操作系统,而是通过硬件线路直接连接到 Secure Enclave
,App就更不可能获取到指纹数据了,非常安全。Face ID也是类似的原理,不同之处是扫描用户头像的3D数据。
另外,iPhone为了提升安全性,在出厂时指纹传感器芯片会和iPhone内部的安全芯片进行密钥配对,保证即使替换了指纹传感器芯片也无法获取到访问 Secure Enclave
中的数据的权限。这就是某些二手iPhone不能够使用指纹识别的原因,因为替换的Home键无法和原装的 Secure Enclave
配对。
生物验证登录原理
iOS 的 Secure Enclave
能够安全的创建密钥对,并使用私钥对数据进行签名。Secure Enclave
创建的私钥只保存在其内部,不允许任何程序读出来。
那么我们要怎样使用iOS的硬件公钥功能实现安全的登录呢?
- 注册流程,
sequenceDiagram
participant User
participant App
participant SecureEnclave
participant Server
App ->> SecureEnclave: SecKeyCreateRandomKey
SecureEnclave ->> User: Request authorize?
User ->> SecureEnclave: Authorized
SecureEnclave ->> App: PrivateKeyRef
App ->> SecureEnclave: SecKeyCopyPublicKey
SecureEnclave ->> App: PublicKey
App ->> Server: Send(PublicKey)
note right of Server: save PublicKey
sign-up.png
App使用 Secure Enclave
创建密钥对时,会提示用户授权,用户只需要通过Touch ID或者Face ID验证就好了。然后再把公钥发送给服务器保存下来。但是私钥是保存在 Secure Enclave
内部的,App无法获取。
- 登录流程
sequenceDiagram
participant User
participant App
participant SecureEnclave
participant Server
User ->> App: Sign in
note over App: Prepare U(user info)
App ->> SecureEnclave: SecKeyCreateSignature
SecureEnclave ->> User: Request authorize?
User ->> SecureEnclave: Authorized
SecureEnclave ->> App: S(signature)
App ->> Server: Send(U, S)
note over Server: verify(PublicKey,U,S)
Server ->> App: Success
authentication.png
登录也很简单,用户点击登录后,App请求 Secure Enclave
签名,iOS会提示用户授权,授权后会返回给App签名,再把签名和登录信息一起发给服务器验证。
生物验证登录更安全
如果App充分利用了iOS生物验证的安全能力,在用户登录时,使用 Secure Enclave
私钥签名,发送给服务器验证,安全性比 Keychain Services
保存密码更高。使用 Secure Enclave
私钥签名技术,私钥只会保存在 Secure Enclave
内部,App,iOS系统都无法读取。也就是说,即使App有漏洞,甚至iOS的沙盒被攻破了,大坏蛋还是无法获取到私钥伪装用户登录。
相对而言,使用 Keychain Services
保存密码,如果App/iOS被攻破了,大坏蛋就可能获取到用户密码,从而伪装用户登录。
看到过一些App有启用指纹登录的功能,但是启用时都没有提示用户进行指纹验证,直接就启用了,显然并没有用上iPhone强大的硬件签名能力。
Secure Enclave 的安全性
Secure Enclave
是非常安全的技术,可能是目前最安全的技术设计了,但并非绝对安全。
事实上,苹果保留了从 Secure Enclave
读取数据的能力,苹果还可以通过系统更新来修改 Secure Enclave
中的系统。
发个硬广
蓝色空间努力把用户的数据控制权还给用户,出品的蓝象安全云无需注册,无需密码,由用户保管加密密钥,密钥与手机设备绑定,安全可靠。
现已在苹果应用商店上架,定价¥25获得300GB/月,目前可以免费试用,欢迎大家下载尝鲜。
cloudeverhttps://itunes.apple.com/app/apple-store/id1445633849?pt=119433202&ct=Tech&mt=8
参考
- https://crackstation.net/hashing-security.htm
- https://en.wikipedia.org/wiki/MD5
- https://en.wikipedia.org/wiki/Cryptographic_hash_function
- https://en.wikipedia.org/wiki/SHA-1
- https://en.wikipedia.org/wiki/Salt_(cryptography)
- https://project-rainbowcrack.com/table.htm
- https://support.apple.com/en-us/HT208108
- https://support.apple.com/en-us/HT204587
- https://developer.apple.com/security/
- https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys
- https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1
- https://developer.apple.com/documentation/security/keychain_services/keychain_items/adding_a_password_to_the_keychain
网友评论