Friday, 30 October 2026

How do you secure an Azure Function?

Securing an Azure Function

Securing a function is layered: control who can call it, what it can reach, and where its secrets live. Start with the first three layers, then add the rest as your requirements grow.

1. Authenticate and authorize callers (inbound)

Function keys (the default for HTTP triggers). AuthorizationLevel.Function requires a key in the x-functions-key header (or ?code=). Keys are simple but they're shared secrets with no identity attached, so treat them as a minimum, not a full solution. Avoid AuthorizationLevel.Anonymous unless something else (such as API Management or Easy Auth) is enforcing access in front of it. Note that Admin level uses the master key, which grants access to everything, so never share it.

App Service Authentication ("Easy Auth") with Microsoft Entra ID. This is the recommended approach for user- or service-facing APIs. The platform validates tokens before your code runs. Enable it under Settings → Authentication and set unauthenticated requests to return 401:

az webapp auth update --name my-func-app --resource-group my-rg \
  --enabled true --action Return401

You then add an Entra ID identity provider (via the portal or az webapp auth microsoft update) with your app registration. For finer control, check roles or scopes in code from the validated claims.

API Management in front. Put APIM in front of the function to get JWT validation, rate limiting, IP filtering, and subscription keys, and then restrict the function so it only accepts traffic from APIM.

Webhooks from third parties (Stripe, GitHub): verify the provider's signature header inside your function rather than relying only on a key.

2. Restrict the network

  • Access restrictions: allow only specific IP ranges, service tags, or VNets (e.g., only your APIM or Front Door).
  • Private endpoints: make the function reachable only from inside your VNet (Premium, Flex Consumption, or Dedicated plans).
  • Front Door or Application Gateway with WAF: adds DDoS and OWASP protection, and you can lock the function to accept traffic only from them.
  • VNet integration (outbound): lets the function reach private resources like databases and Key Vault over the VNet, so those can have their public access turned off.

3. Use managed identity instead of connection strings (outbound)

Give the function a managed identity and grant it RBAC roles on the resources it needs. This removes stored credentials entirely.

az functionapp identity assign --name my-func-app --resource-group my-rg
// In the isolated worker, no keys required
var client = new BlobServiceClient(
    new Uri("https://mystorage.blob.core.windows.net"),
    new DefaultAzureCredential());

Triggers and bindings can use identity-based connections too. For example, with Service Bus you set ServiceBusConnection__fullyQualifiedNamespace instead of a connection string, and grant the identity the Azure Service Bus Data Receiver role. The same pattern works for the function's own host storage account (AzureWebJobsStorage__accountName), which is worth doing so you can disable shared key access on that account.

4. Protect secrets

  • Store remaining secrets in Key Vault and use Key Vault references in app settings: @Microsoft.KeyVault(VaultName=my-vault;SecretName=MySecret).
  • Never commit secrets to source control or put them in local.settings.json files that get checked in (that file is for local development only).
  • Rotate function keys periodically, and use per-client keys or host keys rather than distributing the default key broadly.

5. Harden the transport and the platform

  • HTTPS only: az functionapp update --set httpsOnly=true
  • Minimum TLS version 1.2, and disable FTP/FTPS deployment where you can (--ftps-state Disabled).
  • Disable SCM basic auth and deploy using Entra identities or federated credentials from CI/CD.
  • CORS: list only the exact origins that need browser access, and never use * for authenticated APIs.
  • Keep the runtime current: use a supported .NET and Functions runtime version, and update dependencies.

6. Code-level hygiene

  • Validate and sanitize all inputs, including queue and blob payloads, not just HTTP.
  • Don't return stack traces or internal details in error responses.
  • Apply least privilege. A function that only reads a queue shouldn't have write access to the storage account.
  • Avoid logging sensitive data (tokens, PII, secrets).

7. Monitor and detect

  • Enable Application Insights and diagnostic logs to Log Analytics.
  • Turn on Microsoft Defender for Cloud (Defender for App Service covers Function Apps) for threat detection and recommendations.
  • Alert on spikes in 401/403 responses, unusual invocation counts, and failed Key Vault access.

A sensible baseline

For a typical HTTP-triggered API:

  1. Enable Easy Auth with Entra ID (return 401 for unauthenticated calls).
  2. Use a managed identity with least-privilege RBAC for everything the function touches.
  3. Put APIM or Front Door in front and restrict inbound access to it.
  4. Store leftover secrets in Key Vault, enforce HTTPS and TLS 1.2+, and disable FTP and basic auth.
  5. Send logs to Application Insights and enable Defender for Cloud.


No comments:

Post a Comment

Note: only a member of this blog may post a comment.