很多应用都需要用户登录或者签名认证,这可能需要在客户端保存登录信息、签名密钥、加密算法等。如何保证这些重要信息不被窃取,算法不被破解,这些成为应用开发中很重要的内容,同样也是最容易忽视的地方。一个小小的细节可能就成为整个系统的突破口,这里从实际工程角度总结了一些容易忽视的细节和常用的方法。
密钥保存在外部
-
Keychain
密钥保存在Keychain并不安全,iOS越狱后可以导出Keychain的内容。应该尽量避免存放重要信息(如:token、用户名、密码等)在Keychain中,即使要存放,也一定要加密后存放。参考http://blog.csdn.net/yiyaaixuexi/article/details/18404343
-
文件
保存在app bundle、plist等配置文件更不安全,但可以使用隐写术等方式迷惑hackers。有请Lena示范:
两张图片看起来是一模一样的,但是右边的图片里却夹带了一些其他内容,这就是潜伏在Lena中的密码,用diff工具比较下这两张图片,你会发现不同的地方是右边的图片最后附加了一串字符:
app secret is "abcdefg123456"
。 这里的隐写方式很简单:cat file >> Lena.jpg
,既不破坏图片原本的信息(或者损失一点点原有信息),又能附加额外的信息,这就是隐写术的原理。这里只是一个简单的例子,没有人真这么使用。有很多更隐蔽的做法,比如把要隐藏的信息分散到图片的每个像素中,例如RGB888的图片,对红蓝分量最后一个bit位进行修改并不会影响图片的质量(因为人眼对对红蓝不敏感),这样一个像素(3byte)就可以存储2bit的信息,4个像素(12byte)就可以夹带1byte的信息了。Xcode打包时会对png图片做特殊处理,如果将密码携带在png中,可能会在使用的时候无法复原。当然现在的隐写术非常多,不只是图片能作为载体,视频、音乐等文件都可以,隐写的方法也多种多样,选择适合自己的就行,据说基地组织就是通过岛国电影传递信息的。
写在代码里安全吗?
下面的代码很常见
1
2 3 4 |
|
这是非常危险的,因为常量会被直接编译到可执行文件的data段,只要对生成的可执行文件使用strings
、otool
等命令就可以dump出原始字符串。
对密码加密
为了使密码不直接出现在可执行文件中,可以对密码加密存储,使用的时候再解密。 例如用AES对密码abcd1234
加密,对称密钥为kCipherKey="abcdefgh12345678"
,加密后的密码用kSecret
表示。使用密码时,再通过kCipherKey
和kSecret
计算出来:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
上面的代码不再明文出现abcd1234
,而是被加密算子kCipherKey
和加密后的密钥kSecret
替代,密码只是在需要的时候临时计算出来。但是这里仍然有缺陷:加密算子kCipherKey
和加密后的密钥kSecret
仍然存储在可执行文件的data段中,留下了蛛丝马迹。我们可以给kCipherKey
取一个有迷惑性的字符串,比如"network error, timeout"
或者使用非字符值,使其不可读。但这都不完美,不在data段中存储这些信息最好。
参数传递的秘密
上面的代码稍做修改
snippet2 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
看似和上面代码没什么区别,除了传入的参数类型变了,其余没什么变化。正是这一点带来了巨大的变化,对比一下调用CCCryptorCreate时的汇编代码:
snippet1-disassemble 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|