漫谈iOS安全

讲一讲iOS的安全机制,如何保证APP安全

Posted by Ted on October 11, 2016

一、前言

经常都能听到大家说,iOS很安全,iPhone比安卓手机安全。那么为什么iOS会安全,难道仅仅是因为iOS是闭源然后安卓是开源的,开源的就一定比闭源安全?前段时间著名的Xcode Ghost 事件也让人觉得iOS好像并不是那么地安全,今天就随便说说iOS的安全。

二、沙盒机制

沙盒简介

Complex systems will always have vulnerabilities, and software complexity only increases over time. No matter how carefully you adopt secure coding practices and guard against bugs, attackers only need to get through your defenses once to succeed. While App Sandbox doesn’t prevent attacks against your app, it does minimize the harm a successful one can cause.

A non-sandboxed app has the full rights of the user who is running that app, and can access any resources that the user can access. If that app or any framework it is linked against contain security holes, an attacker can potentially exploit those holes to take control of that app, and in doing so, the attacker gains the ability to do anything that the user can do.

复杂系统始终都会存在漏洞,软件复杂性只会随着时间的推移而增加。 无论你采取安全的编码方式多么谨慎来避免错误,攻击者只需要击穿一次防御就能成功。 虽然应用程序沙盒不能防止对您的应用程序的攻击,但它确实最大限度地减少了可能导致的危害。

非沙盒应用程序具有正在运行该应用程序的用户的完全权限,并且可以访问用户可以访问的任何资源。 如果该应用程序或任何框架被链接到一些安全漏洞,攻击者可能潜在地利用这些漏洞来控制该应用程序,并且在这样做时,攻击者可以执行任何用户可以执行的操作。

Designed to mitigate this problem, the App Sandbox strategy is twofold:

  1. App Sandbox enables you to describe how your app interacts with the system. The system then grants your app the access it needs to get its job done, and no more.
  2. App Sandbox allows the user to transparently grant your app additional access by way of Open and Save dialogs, drag and drop, and other familiar user interactions.

为了解决上面的问题,沙盒策略有两个方面:

应用程序沙盒允许您描述应用程序如何与系统进行交互。 系统然后授予您的应用程序所需的访问权限,以完成其工作,不会赋予更多的权限。 应用程序沙盒允许用户通过打开和保存对话框,拖放和其他熟悉的用户交互方式透明地授予您的应用程序附加访问权限。

img

iOS的沙盒

来看看iOS的沙盒结构图

img

img

iOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等。

  1. 每个应用程序都有自己的存储空间
  2. 应用程序不能翻过自己的围墙去访问别的存储空间的内容
  3. 应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放行。

1、AppName.app 目录:这是应用程序的程序包目录,这里面存放的是应用程序的源文件,包括资源文件和可执行文件。包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。

NSLog(@"%@",[[NSBundle mainBundle] bundlePath]);
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"png"];
UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];

2、Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes共享文件。可被iTunes备份。

NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

3、Library 目录:这个目录下有两个子目录:

NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];

Preferences 目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好. Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。 可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。

NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];

4、tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。

NSString *tmpDir =  NSTemporaryDirectory();

综上,itunes会备份的目录为Documents以及Library目录里除Caches目录的文件。

三、HTTPS

这是对网络数据传输过程中的加密。

其实HTTPS从最终的数据解析的角度,与HTTP没有任何的区别,HTTPS就是将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;而对于接收端,在SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层,就是普通的HTTP数据。HTTP和SSL/TSL都处于OSI模型的应用层。从HTTP切换到HTTPS是一个非常简单的过程。

可参考这篇博文Http与Https

四、APP加固

1、字符串混淆

我们在代码中有时候会用到一些静态字符串,比如像一些SDK的key、网络加密使用的“盐”等字符串,不能使用明文保存,需要对这些静态字符串进行加密,以防止在APP被反编译后泄露。然后在需要使用字符串的地方进行解密。通常我们使用异或加密来加密字符串.

异或的运算方法是一个二进制运算: 1^1=0 0^0=0 1^0=1 0^1=1

两者相等为0,不等为1.

对于一个字符来说,都可以用二进制码来表示.如A:01000001,字符的异或就是对每一位进行二进制运算. 用于加密算法时,假设你要加密的内容为A,密钥为B,则可以用

异或加密:C=A^B 在数据中保存加密后的C就行了. 用的时候:A=B^C 即可取得原加密A的内容,所以只要知道密钥,就可以完成加密和解密.

异或加密和解密的方法一致,运算一次就是加密,再运算一次就是解密。

- (NSString *)xorStr:(NSString *)xorStr withKey:(NSString *)key{
    NSData  *strData = [xorStr dataUsingEncoding:NSUTF8StringEncoding];
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    Byte *keyBytes = (Byte *)[keyData bytes];   //取关键字的Byte数组, keyBytes一直指向头部
    Byte *sourceDataPoint = (Byte *)[strData bytes];  //取需要异或的数据的Byte数组
    for (long i = 0; i < [strData length]; i++) {
        sourceDataPoint[i] = sourceDataPoint[i] ^ keyBytes[(i % [keyData length])]; //然后按位进行异或运算
    }
    NSString *outStr = [[NSString alloc]initWithData:strData encoding:NSUTF8StringEncoding];
    return outStr;
}
  1. 类名、方法名混淆

    对应用程序的方法名和方法体进行混淆,保证源码被逆向后很难明白它的真正功能。

  2. 程序结构混淆加密

    对应用程序逻辑结构进行打乱混排,保证源码可读性降到最低。

  3. 反调试、反注入等一些主动保护策略

    这是一些主动保护策略,增大破解者调试、分析APP的门槛。

五、数据加密

以下代码可以在Github下载

Github-iOS加密

哈希(Hash)是将目标文本转换成具有相同长度的、不可逆的杂凑字符串(或叫做消息摘要),而加密(Encrypt)是将目标文本转换成具有不同长度的、可逆的密文。

img

1、哈希Hash

基本原则是:如果被保护数据仅仅用作比较验证,在以后不需要还原成明文形式,则使用哈希;如果被保护数据在以后需要被还原成明文,则需要使用加密。

1.1、Md5
- (NSString *)md5:(NSString *)src
{
    if (src && src.length > 0) {
        const char *cStr = [src UTF8String];
        unsigned char result[16];
        CC_MD5( cStr, (CC_LONG)strlen(cStr), result ); // This is the md5 call
        return [NSString stringWithFormat:
                @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                result[0], result[1], result[2], result[3],
                result[4], result[5], result[6], result[7],
                result[8], result[9], result[10], result[11],
                result[12], result[13], result[14], result[15]
                ];
    }
    return nil;
}
1.2、SHA
- (NSString*)sha1:(NSString *)src
{
    const char *cstr = [src cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *data = [NSData dataWithBytes:cstr length:src.length];
    //使用对应的CC_SHA1,CC_SHA256,CC_SHA384,CC_SHA512的长度分别是20,32,48,64
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    //使用对应的CC_SHA256,CC_SHA384,CC_SHA512
    CC_SHA1(data.bytes, data.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

2、Base64编码

Base-64编码可以将任意一组字节转换为较长的常见文本字符序列

3、对称加密

3.1、DES

DES是一种分组数据加密技术(先将数据分成固定长度的小数据块,之后进行加密),速度较快,适用于大量数据加密,而3DES是一种基于DES的加密算法,使用3个不同密匙对同一个分组数据块进行3次加密,如此以使得密文强度更高。

3.2 、AES

AES(Advanced Encryption Standard),高级加密标准,是下一代的加密算法标准,速度快,安全级别高。AES算法基于排列和置换运算。排列是对数据重新进行安排,置换是将一个数据单元替换为另一个。

@interface NSString (AES)
//加密
- (NSString *) AES256_Encrypt:(NSString *)key;
//解密
- (NSString *) AES256_Decrypt:(NSString *)key;
@end

.m

//加密
- (NSString *) AES256_Encrypt:(NSString *)key{
    const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *data = [NSData dataWithBytes:cstr length:self.length];
    //对数据进行加密
    NSData *result = [data AES256_Encrypt:key];

    //转换为2进制字符串
    if (result && result.length > 0) {

    Byte *datas = (Byte*)[result bytes];
    NSMutableString *output = [NSMutableString stringWithCapacity:result.length * 2];
    for(int i = 0; i < result.length; i++){
        [output appendFormat:@"%02x", datas[i]];
    }
    return output;
    }
    return nil;
}

//解密
- (NSString *) AES256_Decrypt:(NSString *)key{
    //转换为2进制Data
    NSMutableData *data = [NSMutableData dataWithCapacity:self.length / 2];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [self length] / 2; i++) {
    byte_chars[0] = [self characterAtIndex:i*2];
    byte_chars[1] = [self characterAtIndex:i*2+1];
    whole_byte = strtol(byte_chars, NULL, 16);
    [data appendBytes:&whole_byte length:1];
    }

    //对数据进行解密
    NSData* result = [data AES256_Decrypt:key];
    if (result && result.length > 0) {
        return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
    }
    return nil;
}

4、非对称加密

几种非对称性加密算法:RSA,DSA,ECC

RSA和DSA的安全性及其它各方面性能都差不多,而ECC较之则有着很多的性能优越,包括处理速度,带宽要求,存储空间等等