Gebruik je Azure Function op een veilige manier

Als ontwikkelaar grijpen we al snel naar een Azure Function om dingen te automatiseren. Azure Functions bieden een krachtig platform waarmee we onze code kunnen schrijven en uitvoeren. Het stelt ons in staat schaalbare en flexibele applicaties te bouwen die kunnen reageren op gebeurtenissen en taken kunnen uitvoeren op basis van triggers. Maar te midden van deze ongekende flexibiliteit en efficiëntie moeten we niet vergeten dat beveiliging een essentiële factor is bij het ontwikkelen van elke applicatie, inclusief Azure Functions. In dit blogbericht bekijken we hoe je Azure Functions veilig kunt gebruiken en de best practices die we moeten volgen om onze applicaties te beschermen tegen mogelijke bedreigingen.

Wanneer we een nieuwe Azure Function van het type HttpTrigger aanmaken, zien we dat de definitie van de Run Task als volgt is opgemaakt:

     public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
        {
           ...

De eerste parameter is het HttpTrigger-attribuut dat bestaat uit de volgende parameters:

  • AuthorizationLevel, we bespreken dit verder in dit blogbericht. Mogelijke waarden zijn:
    • Anonymous
    • User
    • Function
    • System
    • Admin
  • Vervolgens zien we dat de mogelijke methoden zijn gedefinieerd voor onze Azure Function als een string-array, namelijk get & post. Als er geen zijn gedefinieerd, gehoorzaamt de http trigger-functie alle methoden.
  • Gevolgd door de Route-parameter. Deze definieert op welke verzoeken de URL’s reageren. Als er geen zijn gedefinieerd, wordt de naam van de functie gebruikt.

Daarnaast hebben we de HttpRequest-parameter, die wordt gebruikt als invoerObject. Zo wordt de body hieruit opgehaald wanneer je een post-methode gebruikt.

Tot slot hebben we het ILogger-object. Zoals de naam al aangeeft, kan dit worden gebruikt om informatie te loggen, maar ook een fout.

Anonymous

Dit maakt het uiteraard mogelijk dat de Azure Function door anonieme gebruikers en dus door iedereen kan worden gebruikt. Dit is de laagste vorm van beveiliging, om niet te zeggen dat er eigenlijk geen beveiliging plaatsvindt wanneer de Azure Function wordt aangeroepen. Dit formulier wordt niet aanbevolen voor gebruik in productie.

     public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log)
        {
           ...

Function

     public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
        {
           ...

Wanneer je het Function-autorisatieniveau gebruikt, vertel je de Azure Function dat je de host app-sleutels wilt gebruiken die zijn gedefinieerd in de Azure Function. Ga hiervoor naar je Azure Function en klik op App keys. Hier heb je een onderverdeling tussen 2 typen:

  • Host keys
  • System key

Voor dit autorisatieniveau kun je beide waarden gebruiken die zijn gedefinieerd in de onderverdeling van de host keys, namelijk:

  • _master
  • Default

Het wordt echter aanbevolen hier de Default-sleutel te gebruiken, omdat de _master-sleutel eigenlijk bedoeld is voor Azure Functions met het autorisatieniveau Admin.

Je kunt deze sleutel als queryparameter doorgeven op de volgende manier (vervang het vetgedrukte door jouw waarden): https://AzureAppName.azurewebsites.net/api/AzureFunctionName?code=YourDefaultCode

Een andere manier is om je Azure Function-URL aan te roepen zonder queryparameters en de standaardcode door te geven met de headerparameter x-functions-key.

Admin

     public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Admin, "get", "post", Route = null)] HttpRequest req, ILogger log)
        {
           ...

Dit autorisatieniveau is hetzelfde als de Function-sleutel, maar dan bedoeld om bepaalde Azure Functions te beschermen die meer rechten vereisen. Zo is het bijvoorbeeld alleen mogelijk om een Azure Function met dit autorisatieniveau aan te roepen met de host _master-sleutel, door deze door te geven als queryparameter of als headerparameter.

Is het gebruik van het Admin- of Function-autorisatieniveau een veilige manier om je Azure Function te beveiligen? Eigenlijk niet helemaal. Het helpt wel om je Azure Function te beveiligen zodat deze alleen kan worden aangeroepen door mensen die de relevante sleutel hebben. Maar zodra je dit in een client in productie gebruikt, kan je sleutel worden onderschept. Zowel de _master als de Default-sleutel kunnen worden vernieuwd in geval van een beveiligingsprobleem, maar dan moet deze nieuwe sleutel ook worden bijgewerkt in elke client waar de Azure Function wordt gebruikt.

System

Het System-autorisatieniveau wordt gebruikt voor Azure Function Extensions. Je kunt deze sleutel zelf niet aanmaken of vernieuwen; deze kan alleen worden aangemaakt door de betreffende Azure Function-extensie.

User

Voor zover ik begrijp, was het oorspronkelijk bedoeld om EasyAuth te gebruiken, maar deze enum-parameter is op dit moment een verouderde, ongebruikte parameter. De meest recente informatie die ik hierover kan vinden, staat in de reactie op dit issue.

Dit wordt momenteel niet gebruikt, maar er is wel de mogelijkheid om je Azure Function te beveiligen op basis van gebruikersauthenticatie. Meer hierover in de volgende sectie:

Gebruik Azure Function met Entra ID

Je kunt je Azure Function beveiligen op basis van je AAD. Dit kun je doen door een Identity Provider toe te voegen aan je Azure Function. Voor dit voorbeeld configureren we de identity provider met Microsoft Entra ID, maar je kunt ook andere gebruiken zoals Apple, Facebook, Github, Google, Twitter of OpenID Connect.

Klik eerst op de sectie Authentication in je Azure Function.

Klik vervolgens op Add identity provider.

Op het volgende scherm kun je je Identity Provider selecteren. Hier kiezen we voor de Microsoft Entra-identiteiten.

Vervolgens kunnen we de configuratie-instellingen zien voor de geselecteerde identity provider. Merk op dat de identity provider een Entra App Registration gebruikt van waaruit de authenticatie zal plaatsvinden. Hier kiezen we ervoor een nieuwe app-registratie aan te maken.

Klik op Next:Permissions > en zie de benodigde machtigingen voor de AAD App-registratie. Je kunt machtigingen toevoegen indien nodig, maar voor de basis zijn de Graph User.Read-machtigingen voldoende, dus ik laat dit ongewijzigd.

Ten slotte moet je je Azure Function het authenticatieniveau Anonymous geven. De authenticatie wordt gedaan door je identity provider die je zojuist hebt geconfigureerd. Dit werkt boven het authenticatieniveau, dus maak je geen zorgen: je Azure Function is inderdaad beveiligd.

     public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log)
        {
           ...

Maar hoe gebruik je je API nu dat deze is beveiligd met een identity provider? De get-methode is vrij eenvoudig: je kunt gewoon je Azure Function-URL in je browser plakken en je krijgt een prompt om in te loggen. Van daaruit wordt een ID-token gegenereerd en ben je vrij om je Azure Function te gebruiken. De post-methode werkt heel anders, omdat het geen login-redirect ondersteunt. Om je beveiligde Azure Function met de post-methode te gebruiken, moeten we een access token verkrijgen dat je kunt ophalen met het authorize- en token-endpoint.

Om je te laten zien hoe je je beveiligde Azure Function succesvol kunt aanroepen, gebruik ik Postman en meer specifiek het ingebouwde Authorization-tabblad, dat de code van het authorize-endpoint voor mij ophaal en doorstuurt naar het token-endpoint.

Klik op het Authorization-tabblad van je post-verzoek in Postman en kies voor het OAuth 2.0-type en kies ervoor de autorisatiegegevens toe te voegen aan de Request Headers.

Vervolgens hebben we de callback-URL nodig. Deze URL kan worden verkregen uit de Entra App-registratie, in de Redirect URI’s van de sectie Authentication.

Vervolgens heb je het autorisatie- en token-endpoint nodig van je App-registratie. Deze 2 URL’s kunnen worden verkregen uit de sectie Endpoint in het overzichtsscherm van je Azure Function.

Met deze informatie gaan we terug naar het Authentication-tabblad in Postman en vullen we de volgende configuratie in:

  • Grant Type: Authorization Code

  • Callback URL: De URL uit de sectie Redirect URL’s in de Authentication-sectie van onze App-registratie

  • Auth URL: Het Authorize-endpoint

  • Access Token URL: Het Token-endpoint

  • Client ID: De Client ID van je App-registratie

  • Client Secret: Het Client Secret van je App-registratie

  • Scope: Dit is een aaneenschakeling van je Client ID gevolgd door “/.default”. Bijvoorbeeld: 1a66ff2a-8201-4ed6-91d6-3d5039ec4421/.default

Vervolgens scrollen we helemaal naar beneden en klikken op Get New Access Token en daarna op Use Token. Daarna zie je dat een Bearer access token is toegevoegd aan de headers van je verzoek, waarmee je je Azure Function succesvol kunt aanroepen.

Gerelateerde inhoud