user1094081
user1094081

Reputation:

How to save a string safely in iOS?

I must save a string (a passphrase) in the keychain but the original string:

  1. doesn't come from the server;
  2. is not generated by the user;
  3. it must be compared on a server (I send it over https);

So, the string must live somewhere in the app (hardcoded?).

I'm quite sure you can't prepare data for the keychain to be readily available for the app after it is installed, like you can just add a plist to the app bundle so that it can be loaded right away if the app is running (even if it's the first time it gets launched).

I have read about Data Protection: it allows applications that work with sensitive data to take advantage of encryption available on some devices. Is that the way to go? That is: I store my data into a text file, then I protect the file, then I retrieve my data from the file, then I save it to the keychain?

Any tip is appreciated.

Upvotes: 4

Views: 2629

Answers (6)

harshal jadhav
harshal jadhav

Reputation: 5684

What you can do is have your parameters encrypted using AES 256 encryption while calling an API, then the server decrypts the parameters and again sends encrypted response.You can decrypt and read the response, provided your server and app shares the same key used for encryption and decryption.

I had a similar functionality in my app so my Util class encrypts and decrypts the request and response using the following code,

   class func encrypt (stringToEncrypt: String) -> String {
        let messageData = stringToEncrypt.data(using: .utf8)
        let encryptedBytes = try! AES(key: "abcdefghijklmnopqrstuvwxyz012345", iv: "abcdefghijklmost").encrypt([UInt8](messageData!))
        return encryptedBytes.toBase64()!
    }

   class func decrypt ( message: String) -> String {
        let messageData = Data(base64Encoded: message, options: .ignoreUnknownCharacters)
        let decryptedBytes: [UInt8] = try! AES(key: "abcdefghijklmnopqrstuvwxyz012345", iv: "abcdefghijklmost").decrypt([UInt8](messageData!))
        let unencryptedString = String(bytes: decryptedBytes, encoding: .utf8)
        return unencryptedString!
    } 

Again this is just a suggestion you can do it using other ways also.

Upvotes: 2

Gero
Gero

Reputation: 4434

Variants of this question have been discussed on SO a bunch of times already, and unfortunately you won't find a perfect answer, as there isn't one.

Essentially, you want to ship authentication credentials in your app, i.e. you bundle a secret into it. This means that no matter what you do, there's a possibility that an attacker retrieves it via reverse engineering. Even if you encrypt it with a hard algorithm that is technically secure enough, at some point your app decrypts it and since the app might be in the hands of an attacker, they will be able to sniff the secret.

This means in the end you can only try to make it hard for them by basically obfuscating the way in which you handle the secret ("make sniffing complicated"). Bundling an encrypted file that gets decrypted and then packed into the keychain seems not like a bad idea to me for a start, but keep in mind that for somebody looking at your app, especially on a jailbroken iPhone, this vanishing file can be a good first hint. Also, deleting it doesn't really help, as one re-install easily restores it.

Another idea could be to use On-Demand Resources to distribute your secret, this might also make it easier to replace it with an updated version in case your secret gets compromised. I am not that familiar with on-demand resources myself, though, so I can't tell you how suited they would be for actually revoking things.

This all assumes that you have no way of implementing an authentication mechanism based on user input. That way an attacker could only steal their own password (assuming they don't steal someone else's iPhone...) and not a vital part of your entire app (which could potentially affect all users).


Other SO answers from my bookmarks that might help you are these:


Okay, after re-reading your last comment under the question again, here's a concrete suggestion. I don't know why you need this second password/token at all if you're already having user authentication (you talk about them logging in), but here you go:

  1. You don't bundle a token/pass together with the app at all. When first running your app, user's have to log in anyways (if I got you right), so your server can be involved and this is where you start.
  2. During this login, the server generates a token/password and will send this to the user (depending on your exact implementation/custom protocol this might be done in a second request/response done by the app that uses the same user credentials or you put it into the headers of the response to the very first request sent by the app). Note that you don't need to encrypt this as long as you use https, as this already encrypts this data. So the channel by which your token arrives within your app is secure.
  3. The app then saves this token/pass in the keychain immediately. This is as good encrypted as it gets on a mobile device. Do not save the token in anything else, even temporarily. The pass only ever exists in plain when it's in memory (you can't avoid that).
  4. You use that token for whatever you need it (I am actually wondering why you need it if you're already using https and a user login system at all).

All in all this sounds like a pretty standard pseudo-OAuth-like token approach, if you're just wanting to use the token to avoid always relying on the user credentials (username and password) in each request. Technically you could also just save username and password in the keychain and always get those for each request. Usually these tokens have a limited time-to-live after which they become invalid anyways and the app has to rely on username and password again to get a new one anyways.

The potential downside of a token is if it's static and not user-bound or if it doesn't have this time-to-live limit. You would have to generate this in a clever way on the server and obviously note which user uses which token. That way you can pinpoint security breaches and react accordingly (instead of suddenly shutting down the server for all users by invalidating your one and only token). Again, I don't see why you need this, though.

Regarding a man-in-the-middle attack: A token, be it app-bundled (which is a risk in itself) or server generated, doesn't in itself protect against that. I have no idea what your boss would aim for here, but perhaps I'm missing some information. In general, https already protects you from this (well, usually only the server is authenticated, but if you already have a username and password system, you should be fine for the client side, too). It's actually a big point of it in general. To me this sounds more and more like something in your original problem is simply a misunderstanding of the existing infrastructure and/or a "boss-induced" problem... :)

Upvotes: 2

Ellen
Ellen

Reputation: 5190

As main concern is with paraphrase or access token, it can be saved as encrypted data in keychain but this will be decrypted whenever we will send a request & it can be tracked on jailbroken devices.But using more agnostic algos to encrypt request data will increase API response time.However defining user based permissions at server side is best way as in worst case only 1 user's data can be tracked.

SSL Pinning - using challenge response authentication, we can prevent app from man in the middle attack. Yes, ssl pinning can be bypassed but it's not that easy & only possible on jailbroken devices.

Defining expiry time to few seconds could be another way to prevent user's data.Identifying source of request is also very important before granting permissions.. So by collaborating multiple ways we can try to make better system.

EDIT

Generally we encrypt data using 1 key as token & it can be decrypted once that key is revealed. Asymmetric Encryption adds 1 more layer of encryption using public & private keys. https://developer.apple.com/library/content/documentation/Security/Conceptual/Security_Overview/CryptographicServices/CryptographicServices.html#//apple_ref/doc/uid/TP30000976-CH3-SW12

here is one more example where random salt is used over token to encode data https://en.wikipedia.org/wiki/PBKDF2

Upvotes: 3

Vishnuvardhan
Vishnuvardhan

Reputation: 5107

Encryption techniques may help to get solve the requirement. Here is the simple elaboration how we can maintain a keys on top of the Diffie–Hellman key exchange encryption techniques.

Prerequisites:

  1. Maintain public and private key pairs on both the sides client and server.
  2. Algorithm or logic to generate private keys should use same for both client and server end.
  3. Logic to break the final acquire keys to compare the own string.

Process:

  1. Both app and backend should have a common public key what ever it is in this case just take it as application name like MyApplication.

    public key: MyApplication

  2. Should generate private keys on the both the ends by using some encryption process or logic. For suppose generate random number as private key like below.

    Suppose App private key is: 10 Suppose Backend private key is: 90

  3. Generate exchange keys on both app and backend by following some kind of algorithm. Here just to combine both public and private keys like

    App final key: MyApplication + 10 Backend final key: MyApplication + 90

  4. Exchange the keys in between app and backend.

    App Got key: MyApplication + 90 Backend Got key: MyApplication + 10

  5. Compare the received key with own by using some kind of technique like

    i. Generated new App key by combining app private key and App Got key like: MyApplication + 90 + 10

    ii. Generated new Backend key by combining Backend private key and Backend Got key like: MyApplication + 10 + 90

  6. To Check both the key are same need follow to logic For example here Just add last two number in the keys result will be like

    App owned key: MyApplication + 100

    App acquired key: MyApplication + 100

    BackEnd owned key: MyApplication + 100

    BackEnd acquired key: MyApplication + 100

Hola finally both backend and app has the same key.

Depends on the requirements needs maintain different private key per each session and store it on keychains. or make one private key and store it on app side.

NOTE: This description illustrates just an overview how to maintain the keys, It may involves lot of logics and also has a provision to break up the keys by others that totally depends on encryptions and logics using to generate and break the keys.

References:

  1. https://en.wikipedia.org/wiki/Diffie-Hellman_key_exchange
  2. https://security.stackexchange.com/questions/7390/how-to-properly-encrypt-a-communication-channel-between-a-client-and-a-server-w

Upvotes: 1

Wojciech Nagrodzki
Wojciech Nagrodzki

Reputation: 2868

If it is not possible to provide the string from outside (user input, a server) developer is forced to put it in the application bundle in some form. It can be hardcoded, stored in a file, or generated by a function. It means application has all necessary information to get/produce such a string. Thus it is not able to secure the information as good as encryption would do.

iOS apps are protected by Apple's DRM, so you can feel safe if someone copies your binary to Mac and starts disassembling it. However, if hacker has a jailbroken device there are tools to dump your application binary from memory to disk, unfortunately.

So it boils down to obfuscation. You could write a function that generates the string dynamically (e.g. a series of operations on hardcoded array of bytes and then convert it to the string). This will make your string harder to intercept by the hacker.

Upvotes: 5

Ajith Tom
Ajith Tom

Reputation: 31

For saving some string values in Keychain you can make use of a pod library

pod 'SSKeychain'

You can save a string to keychain as follows

let appName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
SSKeychain.setAccessibilityType(kSecAttrAccessibleAlways)
SSKeychain.setPassword(stringToSave, forService: appName, account: "MyAppName")

Also retrieve the same using

let appName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
let stringRetrieved = SSKeychain.passwordForService(appName, account: "MyAppName")

Import these once you installed the above pod

import SystemConfiguration
import SSKeychain

This will be retained even if the app the deleted and re-installed. If you want additional encryption you can make use of any encryption algorithm this one is useful

AES encryption in swift

Upvotes: 1

Related Questions