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 easy as clicking on the button below 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?
Key Vault puts all secrets in one spot ... almost as good as a plain text file, but with encryption, authorization, and lots of other perks. 😉
Key Vault secrets are essentially a key/value store - but with lots of polish on top of them. At a minimum, you create a secret within Key Vault, give it a name and then place the (secret) value in it.
As the documentation states, 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 Key Vault 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.
We’re going to start out with a Xamarin mobile app. That app is going to access Azure Table storage. The API Key to get at Table storage will be hardcoded into the app.
We want to move the API key into Key Vault. But we don’t want to have any credentials or secrets at all in the mobile app.
So instead we’re going to create a Functions app to use Key Vault. (And you’ll see how we get around the credentials problem below.)
The mobile app will then invoke the Function to get the data.
In fact, anybody will be able to issue a GET request against the Function and receive back the contents of the table.
The API key to Azure Storage is protected within Key Vault and only accessible from the Azure Function. And since the Function only returns data, we’re not concerned with people inserting data into the Table.
If you wanted to lock down the Function so only certain people can access it or limit the data the Function returns based on who’s logged in. Check out my series of articles on authentication and authorization with Azure AD B2C.
Away we go!
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 Key Vault, use the .NET Key Vault SDK to grab it, then continue on with the code - pretty much as is.
Adding the secret in Key Vault 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 Key Vault isn't too bad!
Open up your Key Vault in the portal, go to the
Secrets option then the
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 Key Vault's .NET SDK to access it from our mobile app and we'll be done!
Ugh... but I have some bad news ... don't use Azure Key Vault 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 Key Vault 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 Key Vault! 🎉
- Key Vault 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 Key Vault to retrieve the Azure Storage connection string?
Hardcoding Key Vault'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 Key Vault ... without the hardcoded credentials?
Through the wonders of the Managed Identity.
So remember when I said that access to the Key Vault 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.
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
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).
The next step is to grant the new Active Directory (AD) principal - aka your Function App permission to the secrets contained within Key Vault.
Back over to Key Vault 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.
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
OK a bunch of times.
And then you're set.
Now to get the Function to use Key Vault. The code looks like this:
If you've ever created a Function that accessed Azure Table storage before, you're probably thinking to yourself ... "the Function's code when accessing Key Vault is exactly the same as when it doesn't!"
The Function's code when accessing Key Vault is exactly the same as when it doesn't!
But how does the Function use Key Vault? The magic is in the App Settings. In particular the
To make that particular value access Key Vault - you use this syntax:
So it goes into the Function's app settings. It's the value portion of the
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 Key Vault, of course! Back into Key Vault into the portal you go!
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.
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:
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 Key Vault 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 Key Vault 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.
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.