Where can I store an api key for a webservice securely?

I'm developing an android app within Xamarin.Forms. From there I call a web api which needs an api key within the headers for authentication, which is always the same. At the moment, the API key is hardcoded within my app, but this is not really secure..

I already thought about saving the api key via SettingsPlugin, but the problem is that I have to save the api key the first time there and this would be also hard coded..

I don't want that someone else can retrieve my api key by getting the source code out of my apk-file. What can I do? Thanks for your help!

Best Answer

Answers

  • seanydaseanyda GBMember ✭✭✭✭✭

    Never really thought about this before. Does your app require a login too? I just hardcode the API Key into the app but I also have basic authentication headers, so I pass the Username and Password of the user within the headers (included in each call).

    Something like the below:
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", authUser.Username, authUser.Password))));

    But what are the chances of someone leaking your APK, decoding it and firing connections to your server?

  • tschennietschennie USMember ✭✭

    No the app doesn't require an user login.. They just can use it. If he want some special features, he can pay once for getting it. So therefore I don't want that the user can decode my APK and seeing the api key for the web service..

  • NMackayNMackay GBInsider, University mod
    edited November 2017

    @tschennie

    I hold all end points (data, sts etc) as encrypted strings in the app. The oAuth token is persisted in a SQLite DB and encrypted before storing and retrieving, it is persisted unencrypted only in memory. I have a platform specific service that does the decryption by just injecting the service that's registered in the IoC into the viewmodel.

     public CustomerService(ICryptoService crypto)
            {
                _endpoint = crypto.Decrypt(crypto.GetEncryptedString(EncryptedString.DsEp));
                _header = crypto.Decrypt(crypto.GetEncryptedString(EncryptedString.DsAh));
            }
    

    I can't provide the implementations I use obviously but there's samples out there, the upshot is that someone disassembling the app hopefully won't be able top easily spot endpoints , client secret keys etc. It certainly makes it more difficult.

    Other than that there are commercial products to help obfuscate code.

  • Amar_BaitAmar_Bait DZMember ✭✭✭✭✭
    Never ever put anything sensible in your app. By never I mean Really NEVER. The solution is to create a web server that serves as a proxy. You call your web server, which calls the API you need then returns the response. Like that there will be no keys stored in your app. You should always consider that anything you put in your app is public and very easily accessible even by a novice "hacker" (aka script kiddie)
  • Amar_BaitAmar_Bait DZMember ✭✭✭✭✭
    edited November 2017
    Clarification: if the api key in question your own key to differentiate users (for eg. Paying vs free)?
  • tschennietschennie USMember ✭✭

    @nadjib said:
    Never ever put anything sensible in your app. By never I mean Really NEVER. The solution is to create a web server that serves as a proxy. You call your web server, which calls the API you need then returns the response. Like that there will be no keys stored in your app. You should always consider that anything you put in your app is public and very easily accessible even by a novice "hacker" (aka script kiddie)

    But how can I secure that no ones call my web server? The advantage is only that the "hacker" cannot see my api key, but he still can access my API via the web server..

    @NMackay said:

    I hold all end points (data, sts etc) as encrypted strings in the app. The oAuth token is persisted in a SQLite DB and encrypted before storing and retrieving, it is persisted unencrypted only in memory. I have a platform specific service that does the decryption by just injecting the service that's registered in the IoC into the viewmodel.

     public CustomerService(ICryptoService crypto)
            {
                _endpoint = crypto.Decrypt(crypto.GetEncryptedString(EncryptedString.DsEp));
                _header = crypto.Decrypt(crypto.GetEncryptedString(EncryptedString.DsAh));
            }
    

    I can't provide the implementations I use obviously but there's samples out there, the upshot is that someone disassembling the app hopefully won't be able top easily spot endpoints , client secret keys etc. It certainly makes it more difficult.

    Other than that there are commercial products to help obfuscate code.

    Ok yes, I thought that this could be the only solution :/

  • tschennietschennie USMember ✭✭

    @nadjib said:
    Clarification: if the api key in question your own key to differentiate users (for eg. Paying vs free)?

    No I just have one api key, but if the user has the key, he can see things, which he has to pay for..

  • NMackayNMackay GBInsider, University mod

    @tschennie said:

    @nadjib said:
    Clarification: if the api key in question your own key to differentiate users (for eg. Paying vs free)?

    No I just have one api key, but if the user has the key, he can see things, which he has to pay for..

    I'd maybe rethink your security model, if that key is compromised your entire pay model for the app is blown. Can you not validate your key via a secure HTTPS call using oAuth at app startup or something, that way at least if the key is compromised you can push out a new version of the app. At the very least you need to encrypt and obfuscate that key as best you can.

  • NMackayNMackay GBInsider, University mod
    edited November 2017

    @nadjib said:
    Never ever put anything sensible in your app. By never I mean Really NEVER. The solution is to create a web server that serves as a proxy. You call your web server, which calls the API you need then returns the response. Like that there will be no keys stored in your app. You should always consider that anything you put in your app is public and very easily accessible even by a novice "hacker" (aka script kiddie)

    Very true , the 1st lob app we put in the store was a hybrid app, the Android version was disassembled and the endpoints attacked, there was nothing not mostly publicly available behind but it was a lesson learned. We don't put lob apps in the store anymore.

  • NMackayNMackay GBInsider, University mod

    @nadjib said:
    In that case you should implement a way to identifying users (users roles specifically ie. paying vs free), and not using an API key at all, since an api key is a generic authentication. You should generate and send a unique identifier for each user with your api call, then compare it with a database server side to check if the user is registered as a paying one or free.

    A quick method would be to generate a unique user Id (a Guid.NewGuid().ToString() is enough) at the 1st launch of the app, store this unique Id in the phone (via Settings plugin for eg) then when the user unlocks the paying feature by a purchase, you send that Id (after purchase receipt validation of course) to store it server side in the database. Then it'll be easy to detect if the user is a paying one or not. The downside of that method is that the stored id might be deleted from the user phone when he uninstalls the app.

    A fix for that problem would be instead of generating a random Id, you retrieve a constant, but unique Id per device. Methods exist in each platform to get a unique Id per device. This solution is better for not making paying users angry since they can always access the paid feature even if they uninstall and reinstall the app. The downside is that it's not as secure as the 1st method, since a user can spoof the id if the device is rooted (?).

    The best method is to rely on each platform In App Billing system + you can combine it with the 1st one (random Id) and you have your uninstall proof, secure solution:

    When the user starts the app for the 1st time, you check via In App Billing API to see if the user has purchased or not your feature. If yes, verify this claim server side (never trust client side In App Billing receipt verification), generate and store into db a unique Id (server side too) and return the result to the client for eg:

    { UniqueId: "2cf478c8-0f19-4170-9b41-c6f963fee8d2", ReceiptVerified: true }

    In your app, store that UniqueId using Settings plugin for eg, then pass it in your request to check against the Ids database.

    +1 Some very good suggestions here.

  • tschennietschennie USMember ✭✭

    @nadjib said:
    In that case you should implement a way to identify users (users roles specifically ie. paying vs free), and not using an API key at all, since an api key is a generic authentication. You should generate and send a unique identifier for each user with your api call, then compare it with a database server side to check if the user is registered as a paying one or free.

    A quick method would be to generate a unique user Id (a Guid.NewGuid().ToString() is enough) at the 1st launch of the app, store this unique Id in the phone (via Settings plugin for eg) then when the user unlocks the paying feature by a purchase, you send that Id (after purchase receipt validation of course) to store it server side in the database. Then it'll be easy to detect if the user is a paying one or not. The downside of that method is that the stored id might be deleted from the user phone when he uninstalls the app.

    A fix for that problem would be instead of generating a random Id, you retrieve a constant, but unique Id per device. Methods exist in each platform to get a unique Id per device. This solution is better for not making paying users angry since they can always access the paid feature even if they uninstall and reinstall the app. The downside is that it's device dependant (so a user will not be able to use the app if he changes device).

    The best method is to rely on each platform In App Billing system + you can combine it with the 1st one (random Id) and you have your uninstall proof, secure solution:

    When the user starts the app for the 1st time, you check via In App Billing API to see if the user has purchased or not your feature. If yes, verify this claim server side (never trust client side In App Billing receipt verification), generate and store into db a unique Id (server side too) and return the result to the client for eg:

    { UniqueId: "2cf478c8-0f19-4170-9b41-c6f963fee8d2", ReceiptVerified: true }

    In your app, store that UniqueId using Settings plugin for eg, then pass it in your request to check against the Ids database.

    Or you can just have standard user accounts with email and password and you're done :)

    It's my first app, so I'm really new to this topic. Therefore thank you very much for your suggestions!
    At the moment I haven't thought about how to implement the paying process, but now I know how I can secure it.

    But back to the free version of the app. I don't want to blow it up for the user so that he has to register for, because in normal case he just use it once or twice. So therefore it should be as easy as possible.
    So because one feature is free, I don't need also an api key for it, am I right? I can let the api function open for everyone?

Sign In or Register to comment.