Mobile Apps + Azure Keyvault ... Don't Do It!
Hey! Follow along with all the action here! First, if you don't have an Azure subscription, get a free one here! And then next, you can deploy all the resources I'm going to talk about as easily as clicking on this button!
Don't hardcode connection strings - or any secrets - in your apps! And for all that's holy - encrypt those secrets!
Like most other advice that I get (floss... exercise... shower more than once per week...) that seemingly easy to follow best practice of security guidance doesn't get followed very often.
Why? Because it's an extra step in the development cycle. Why go through the hassle of encrypting, storing, having to come up with a means to distribute my app's secrets to other devs on my team... when I could just have them sitting around somewhere in plain text? What are the odds that somebody will decompile my app to get at them anyway?
Just like why would I exercise when I could sit around, eat Pringles, and watch re-runs of SpongeBob SquarePants? What are the odds that I'm going to die of a heart attack today?
Well ... eventually luck runs out. Someday, something bad will happen to my app's secrets even if the app doesn't get cracked (like I commit the secrets file to GitHub). Just like someday I'll regret skipping all of those workouts to watch marathons of the Golden Girls. (Lol ... like that would ever be a regret.)
But there has to be a way ... there has to be a way to keep the app's secrets safe, all the while following best practices, and not making our lives too difficult ... right? Right?
Enter Azure Keyvault
Azure Keyvault provides a centralized place to store not only your application's secrets, but also can be used for key and certificate management too.
Keyvault puts all secrets in one spot ... almost as good as a plain text file, right? 😉
Keyvault's secrets are essentially a key/value store - but with lots of polish on top of them. At a minimum, you create a secret within KeyVault, give it a name and then place the (secret) value in it.
The data/secret is encrypted both at rest and in transmission (and Microsoft never sees the data contained within). You can monitor the access & use of each secret. And access to secrets is controlled by Active Directory.
You can version secrets - so if the connection string changes you can set the existing value as old and create a new value, with the connection string, all within the same key.
You also can disable keys, set activation and deactivation dates.
And each secret in Keyvault gets its own URI which it can be referenced by.
So it's cool, right? Application secrets in a single spot, encryption by default, and there are even Microsoft provided SDKs to get at them.
Done and done.
Let's Use Keyvault!
So I have an app that pulls some data from Azure Table storage.
It's a Xamarin.Forms app and it displays the data it finds in the Table in a ListView
- it looks like this:
The code which grabs the data from Table storage looks like this:
There's a couple things wrong here (or as I like to say, it's demo code!) - but the big one, and the one that we're going to fix, is that the Azure Storage account's API key is hardcoded.
The fix should be fairly easy ... add the API key for Azure Storage to Keyvault, use the .NET Keyvault SDK to grab it, then continue on with the code - pretty much as is.
So Let's Add The Secret!
Adding the secret in Keyvault is easy, easy, easy! (As an aside, don't you hate when people say something is easy? Everything's easy when you already know how to do it! So let's try it again....)
Adding the secret in Keyvault isn't too bad!
Open up your Keyvault in the portal, go to the Secrets
option then the Generate/Import
button.
Then you'll see this page - on there give the secret a name - and pop in the value. In this case it'll be the storage connection key.
Now on to using Keyvault's .NET SDK to access it from our mobile app and we'll be done!
But... Don't Use Keyvault - In a Mobile App
Ugh... but I have some bad news ... don't use Azure Keyvault when you're building a mobile app.
At least not directly.
You see, we want to keep all app secrets off the device if possible. Even if they're never being stored and will only be in memory transiently.
To solve this particular conumdrum... we're still going to take advantage of Keyvault and all it has to offer and put the Azure Storage API key in there.
But instead of having the mobile app directly access the Table storage, we're going to have an Azure Function do that.
This then gives us several benefits.
- We get to use Keyvault! 🎉
- Keyvault is accessed completely over the Azure backbone - no public internet - and same for Azure storage. Fast!
- We'll end up with an API that can be used in more places than the mobile app.
OK good ... we're going to use Function-Table storage bindings to get at the data. And the mobile app will call that Function.
But how does the Function access Keyvault to retrieve the Azure Storage connection string?
Hardcoding Keyvault's credentials into the Function's app settings would kind of defeat the purpose of not having the Storage's connection string in the app settings.
So how does Azure Functions communicate to Azure Keyvault ... without the hardcoded credentials?
Through the wonders of the Managed Identity.
Managed Identity
So remember when I said that access to the Keyvault is controlled through Active Directory?
It turns out that it's pretty straightforward to make your Function App a member of the Active Directory. And once it's Active Directory-izied you can grant the Function App access to Key Vault as easily as you can grant access to a person that's in Active Directory.
And this is what's known as Managed Identity.
Active Directory-izing Your Function App
This same process goes for any App service.
It's really kinda anti-climatic.
Pop over to the Azure portal for your Function. Select the Platform Features
tab, then the Identity
option.
You have Active Directory-izied your Functions app.
Now the Functions App (which is really an App Service) will now appear in the Active Directory that your Azure subscription is a part of.
Done (with this part).
Granting the Keyvault Permissions
The next step is to grant the new Active Directory (AD) principal - aka your Function App permission to the secrets contained within Keyvault.
Back over to Keyvault in the Azure portal we go!
You grant new permissions to AD principals through the Access policies
option and the Add new
button.
The next page then take a bit of explanation.
The first thing you can do is Configure from template
. This is a shortcut to select various permissions for you. Which can be seen in the various permissions dropdowns.
I picked Secret Management
which selected all the secret functions for me.
The next thing that needs to be done is select the principal to apply these roles to.
And to select your function app - search for its name. Eventually AD will find it. Select the name and hit Select
and OK
a bunch of times.
And then you're set.
Getting the Function to Talk to Keyvault
Now to get the Function to use Keyvault. The code looks like this:
If you've ever created a Function that accessed Azure Table storage before, you're probably to yourself ... "the Function's code when accessing Keyvault is exactly the same as when it doesn't!"
The Function's code when accessing Keyvault is exactly the same as when it doesn't!
But how does the Function use Keyvault? The magic is in the App Settings. In particular the KeyvaultedStorageConnection
string.
To make that particular value access Keyvault - you use this syntax:
So it goes into the Function's app settings. It's the value portion of the KeyvaultedStorageConnection
key.
And as you can tell, it uses the syntax: @Microsoft.Keyvault(SecretUri=<A SECRET URI>)
.
Where does that <A SECRET URI>
come from?
From the secret's panel in Keyvault, of course! Back into Keyvault into the portal you go!
Click on Secrets
on the left-hand navigation. Then click on the secret you just created. Then by clicking on the current version
in the resulting window, you'll see the property's of the secret.
In that window - there's a Secret Identifier
box. That's the URI which you'll use for the <A SECRET URI>
above.
The App's Code
At this point you're done. Well, except for getting the mobile app to call the Function of course.
The Function in this case is HTTP triggered. So it's a web call from our Xamarin app - looks like this:
That's All Folks
And finally, we've reached the end of our journey!
The mobile app no longer has secrets hardcoded in it, but yet it's not using Keyvault either! Because we don't want any secrets to land on that mobile app if we can at all avoid it.
We're still using Keyvault though, and all of the goodness it provides, and created a Function to access it - through Managed Identity. This way the Function doesn't have to have any secrets stored in it either!
Cool, cool, cool! Not too much extra work - and a ton of extra security!
Now if you'll excuse me - I think there's another rerun of SpongeBob about to air.
A Quick Postscript - Or Using a SAS On Device
Why not use a Storage Access Signature (SAS) to access the Azure Storage Account? Well, from the mobile device that could totally have been done. And with that I could have still used the .NET Storage SDK.
However, to generate that SAS, I would still have needed to get direct access to the Storage account through the full connection string somehow. And that would be done in the same manner as above. Except instead of returning data, the Function would have returned an SAS token.