Security Issues
2.1.1 (User refresh token located in manifest.json)
All the sensitive data have to be stored in secure manner, for example using encrypted SQL storages, keychain or Secure Enclave.
done in React Native.
2.1.2 (Authentication response cached in database in plain text)
All the sensitive data have to be stored in secure manner, for example using encrypted SQL storages, keychain or Secure Enclave.
not done
2.1.3 (SSL pinning is not implemented)
done
2.1.4 (App screenshot information disclosure)
To protect sensitive data, block caching of application snapshots using API configuration or code.
done
2.1.5 (App Transport Security (ATS) is disabled)
2.1.6 Application does not clear cached data after logout
Caching functionality helps to operate application with data more reliable in terms of performance, but have to clear it in certain scenarios, one of them is logout.
done
2.1.7 (Application permissions have to be reviewed)
2.1.8 (Application contains DEV setting plist)
should be done when EXPO is removed
2.1.9 (No source code obfuscation)
2.1.10 (can run on jailbroken phones)
not sure
Remove data in your Cache folder
First let’s look at the contents of your cache folder. Notice the use of NSCachesDirectory macro.
1 2 3 |
NSFileManager * fileManager = [NSFileManager defaultManager]; NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString * filePath = [documentsPath stringByAppendingPathComponent:@"com.yourproject.app"]; |
[0] [15:12:53] fsCachedData
[0] [15:12:53] Users
[0] [15:12:53] Cache.db-shm
[0] [15:12:53] Cache.db-wal
[0] [15:12:53] Cache.db
and they are located in:
/Users/rickytsao/Library/Developer/CoreSimulator/Devices/F19D977F-B37C-4DA5-98DA-6C1B2ADCD500/data/Containers/Data/Application/44CF2644-6491-4A4A-9A5C-9DA0F405A45E/Library/Caches/com.rocktree.app/
source code to do this:
Device.h
1 2 3 4 |
#import "React/RCTBridgeModule.h" @interface Device : NSObject <RCTBridgeModule> @end |
Device.m
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 43 44 |
#import "Device.h" #import <UIKit/UIKit.h> @implementation Device //export the name of the native module as 'Device' since no explicit name is mentioned RCT_EXPORT_MODULE(); -(void) removeFilesFromCaches: (NSString*)appID usingCallbackBlock:(RCTResponseSenderBlock)callback { NSFileManager * fileManager = [NSFileManager defaultManager]; NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString * filePath = [documentsPath stringByAppendingPathComponent:@"com.yourproject.app"]; NSArray * fileArray = [fileManager contentsOfDirectoryAtPath:filePath error:nil]; NSMutableArray * allLocations = [NSMutableArray array]; for (NSString * fileName in fileArray) { NSString * fileURL = [filePath stringByAppendingPathComponent:fileName]; [allLocations addObject:fileURL]; NSError *error; BOOL success = [fileManager removeItemAtPath:fileURL error:&error]; if (success) { } else { callback(@[[NSNull null], @"Failed to remove file(s)"]); NSLog(@"Could not delete file -:%@ ",[error localizedDescription]); } } callback(@[[NSNull null], @"Successfully removed these files:", allLocations]); } RCT_EXPORT_METHOD(getDeviceName:(RCTResponseSenderBlock)callback){ // emptying NSURL Cache [[NSURLCache sharedURLCache] removeAllCachedResponses]; [self removeFilesFromCaches:@"com.rocktree.app" usingCallbackBlock:callback]; } @end |
Call the code in React Native
ProfilesDetailsScreen/index.js
Import NativeModules from react native
1 2 3 4 5 |
import { ... NativeModules, Platform, } from 'react-native'; |
In the log out handler, call the bridge function.
1 2 3 4 5 6 7 8 9 10 11 |
onLogout = async () => { const { logoutUserAction, navigation } = this.props; logoutUserAction(); if (Platform.OS === 'ios') { NativeModules.Cleaner.removeAllCachedData((err, msg, result) => {}); } await AsyncStorage.removeItem('userRefreshToken'); navigation.navigate('Auth'); }; |
Thus, when you log out, it bridges to the iOS module. the iOS module manages to access its cached data and remove whatever was saved there.
Snapshots
snapshots:
https://stackoverflow.com/questions/7520076/prevent-ios-from-taking-screen-capture-of-app-before-going-into-background
place overlay image when the app is about to resign. and remove this overlay when its about to become active.
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 |
@interface AppDelegate () @property (nonatomic, strong) EXViewController *rootViewController; @property (nonatomic, strong) UIImageView * imageView; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; _window.backgroundColor = [UIColor whiteColor]; [[ExpoKit sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions]; _rootViewController = [ExpoKit sharedInstance].rootViewController; _window.rootViewController = _rootViewController; [_window makeKeyAndVisible]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { self.imageView = [[UIImageView alloc]initWithFrame:[self.window frame]]; [self.imageView setImage:[UIImage imageNamed:@"launch_background_image.png"]]; [self.window addSubview:self.imageView]; } - (void)applicationDidBecomeActive:(UIApplication *)application { if(self.imageView != nil) { [self.imageView removeFromSuperview]; self.imageView = nil; } } |