4

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.

Gero
  • 4,394
  • 20
  • 36
  • @nathan: thanks for the edit! –  Jul 24 '17 at 08:20
  • You can save a string in the Keychain and the Keychain will encrypt it. Why isn't this a solution? – zaph Jul 27 '17 at 12:57
  • 1 & 2 in the question state where it can't come from but not state where it does come from. – zaph Jul 27 '17 at 13:03
  • @zaph: the point of the question is exactly that: where can I safely store (locally) a string? :-) –  Jul 27 '17 at 14:38
  • Store the key in the Keychain. You are not helping be being vague. – zaph Jul 27 '17 at 16:42
  • @zaph: to store it in the keychain they must be somewhere else when the app starts, keychain is not a plist –  Jul 27 '17 at 18:38
  • You have yet to say where the strings come from. It is possible to create a string in the app at run time and store it in the Keychain. – zaph Jul 27 '17 at 20:41
  • @zaph: the question is exactly about that! At the moment there are two solutions, the one you suggest is one of the two. Another is logging a user (over https), store his credentials before logging, if login was succesful, use his credentials, encode/encrypt them some way and store the result in the keychain –  Jul 28 '17 at 05:24
  • To sum up, you want hardcoded string, then you want to encrypt this string(somehow) and save it safely on the device. And app will send this encrpyted string to the server, then server will make sure that is correct string and send the response back to the app, yes? – kamwysoc Jul 28 '17 at 07:34
  • @k8mil: not necessarily, say there are two ways: generate the string on the fly and store it in the keychain (and the server should generate it the same way or have it generated before) OR hardcode and encrypt it some way. When I posted, this second one was my choice but I'm going to prefer the first one, maybe this way: the user logs in over https, I store his credentials before he send them, if login was successful, I take his credentials, I generate on the fly my passphrase hashing/salting it one way and I save it to the keychain –  Jul 28 '17 at 07:45
  • I don't get the question. The assumption that you can't store a string in the keychain is incorrect. You totally can, that's the whole purpose of the keychain. So I am not sure what this question is actually about. – Michael Ochs Jul 31 '17 at 08:13
  • @MichaelOchs: I actually can store a string in the keychain, of course, but the question was: if the string is not coming from the server and it is not typed by the user, how do I safely save a string? Initially, I thought to put it in Data Protection and then copy it to the keychain: this is quite ok if you delete the data protected file on first load. Then, a user suggested a more interesting thing (generate the string, and then save it in the keychain, eventually). That's it –  Jul 31 '17 at 08:17
  • 3
    @MichaelOchs The wording is a bit imprecise (I'll suggest an edit to the question soon), but I finally got that it's about how to *bundle* the string into the app. I.e. how to "bring it into" the application. What 3000 means with "manually save anything in the Keychain as you would do with a plist" seems to be "how to have something added into the keychain on install", like a plist is installed with the app (as it comes bundled). – Gero Jul 31 '17 at 10:24
  • @Gero: sorry for having written the post that way! :-( –  Jul 31 '17 at 11:50
  • 1
    @3000 Hey, no problem, happens to all of us. :) I'm just glad I could figure it out and help make it more readable (I could have been wrong, after all) – Gero Jul 31 '17 at 11:53

6 Answers6

5

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.

Wojciech Nagrodzki
  • 2,788
  • 15
  • 13
  • Ok, but what if I store the password in a txt file, I protect it with Data Protection, on first load I read the password, I store it in the keychain and I delete the file? –  Jul 27 '17 at 08:21
  • Apple's Data Protection keeps your application's files safe when device is locked. Once user unlocks the device files are accessible. There will be no protection if device is not using pin/touchID. It is a useful feature if your device gets stolen. - It does not fit in scenario you would like to use here. – Wojciech Nagrodzki Jul 27 '17 at 09:54
  • Problem is server and client must share the same string: so, if the string must be generated the way you suggest client side, I should at least take the resulted string and record it on a server or have the server using the same algorithm to generate the same result –  Jul 27 '17 at 10:02
  • 1
    You are correct, you can take the resulted string and place it on the server side. That would be the way to go. – Wojciech Nagrodzki Jul 27 '17 at 10:04
3

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

Ellen
  • 5,180
  • 1
  • 12
  • 16
  • Hi @Ellen, I don't want to send that passphrase: I want to use is to encrypt data I send/receive over https –  Jul 31 '17 at 06:50
  • 1
    Hope above links help you to identify better approach. – Ellen Jul 31 '17 at 07:30
  • @3000 Wait, now you say you want to use this string to manually encrypt something that is then sent using https? That's... unnecessary. https already uses encryption, *asymmetric* encryption, actually (to be precise asymmetric encryption to exchange a safe key to symmetrically encrypt the traffic). This is superior to a symmetric protocol that relies on storing the key permanently in the app. Of course if an attacker gets your app (on a jailbroken device), *nothing* protects you from them finding out your secret. User-based passes are the best option, as Ellen said, since they limit impact. – Gero Jul 31 '17 at 07:35
  • @Gero: it's an idea from my boss: he says this can reduce the risk coming from man-in-the-middle attacks –  Jul 31 '17 at 07:47
  • 1
    See my updated answer below. I have no idea how MITM could be a problem in the first place, you seem to have user authentication via name and password and can use HTTPS, which authenticates the server for the client. Also note that Ellen's hint towards SSL Pinning addresses MITM as well, so you might wanna check that out, too. However, I believe your boss might misunderstand something and that you're, in fact, good to go already with onboard means. – Gero Jul 31 '17 at 08:03
2

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... :)

Gero
  • 4,394
  • 20
  • 36
2

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.

harshal jadhav
  • 5,456
  • 2
  • 18
  • 26
1

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

Ajith Tom
  • 31
  • 6
  • This is ok, but it's not my issue: problem is I don't want to write it in plain text and then save it programmatically the way you do. So, I was asking if the way to go is saving it using Data Protection and THEN add it to the keychain. –  Jul 24 '17 at 09:51
  • The AES encryption in swift link shared above helps to encrypt any text content which can be saved to a text file or by any other means which can be decrypted only by using the key. More complex encryption algorithms are also available. Is that the kind of protection you need? – Ajith Tom Jul 24 '17 at 12:22
  • No, AES is just a part of my design: in particular, I could store my key directly in the app but I was looking for the safest way to do it –  Jul 24 '17 at 13:48
1

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
Vishnuvardhan
  • 5,081
  • 1
  • 17
  • 33
  • Since the OP states that https is being used a shared key can simnple be sent over https. – zaph Jul 27 '17 at 13:05
  • @zaph: I said I don't want the shared key sent over the network, it should be somewhere on the client (or generated on the client) –  Jul 27 '17 at 14:43
  • Yes and It seems that this answer is about shareing a key over the network. – zaph Jul 27 '17 at 16:45
  • Yes, It is totally about sharing the key. but in secured way. With out sharing keys we can't compare the dynamically generated keys, Unless to maintain a static key need to share in offline. – Vishnuvardhan Jul 28 '17 at 02:10
  • If HTTPS is available that is all that is needed to share a key. – zaph Jul 29 '17 at 15:47
  • ["Schneier's Law"](https://www.schneier.com/blog/archives/2011/04/schneiers_law.html): "*Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can't break.*" – zaph Jul 29 '17 at 15:56