Securing Azure Functions
In many ways, planning for secure development, deployment, and operation of serverless functions is much the same as for any web-based or cloud hosted application. Azure App Service provides the hosting infrastructure for your function apps. This article provides security strategies for running your function code, and how App Service can help you secure your functions.
The platform components of App Service, including Azure VMs, storage, network connections, web frameworks, management and integration features, are actively secured and hardened. App Service goes through vigorous compliance checks on a continuous basis to make sure that:
- Your app resources are secured from the other customers' Azure resources.
- VM instances and runtime software are regularly updated to address newly discovered vulnerabilities.
- Communication of secrets (such as connection strings) between your app and other Azure resources (such as SQL Database) stays within Azure and doesn't cross any network boundaries. Secrets are always encrypted when stored.
- All communication over the App Service connectivity features, such as hybrid connection, is encrypted.
- Connections with remote management tools like Azure PowerShell, Azure CLI, Azure SDKs, REST APIs, are all encrypted.
- 24-hour threat management protects the infrastructure and platform against malware, distributed denial-of-service (DDoS), man-in-the-middle (MITM), and other threats.
For more information on infrastructure and platform security in Azure, see Azure Trust Center.
This section guides you on configuring and running your function app as securely as possible.
Defender for Cloud
Defender for Cloud integrates with your function app in the portal. It provides, for free, a quick assessment of potential configuration-related security vulnerabilities. Function apps running in a dedicated plan can also use Defender for Cloud's enhanced security features for an additional cost. To learn more, see Protect your Azure App Service web apps and APIs.
Log and monitor
One way to detect attacks is through activity monitoring and logging analytics. Functions integrates with Application Insights to collects log, performance, and error data for your function app. Application Insights automatically detects performance anomalies and includes powerful analytics tools to help you diagnose issues and to understand how your functions are used. To learn more, see Monitor Azure Functions.
Functions also integrates with Azure Monitor Logs to enable you to consolidate function app logs with system events for easier analysis. You can use diagnostic settings to configure streaming export of platform logs and metrics for your functions to the destination of your choice, such as a Logs Analytics workspace. To learn more, see Monitoring Azure Functions with Azure Monitor Logs.
For enterprise-level threat detection and response automation, stream your logs and events to a Logs Analytics workspace. You can then connect Microsoft Sentinel to this workspace. To learn more, see What is Microsoft Sentinel.
For more security recommendations for observability, see the Azure security baseline for Azure Functions.
By default, clients can connect to function endpoints by using both HTTP or HTTPS. You should redirect HTTP to HTTPs because HTTPS uses the SSL/TLS protocol to provide a secure connection, which is both encrypted and authenticated. To learn how, see Enforce HTTPS.
When you require HTTPS, you should also Require the latest TLS version. To learn how, see Enforce TLS versions.
For more information, see Secure connections (TLS).
Function access keys
Functions lets you use keys to make it harder to access your HTTP function endpoints during development. Unless the HTTP access level on an HTTP triggered function is set to
anonymous, requests must include an API access key in the request.
While keys provide a default security mechanism, you may want to consider other options to secure an HTTP endpoint in production. For example, it's not a good practice to distribute shared secret in public apps. If your function is being called from a public client, you may want to consider implementing another security mechanism. To learn more, see Secure an HTTP endpoint in production.
When you renew your function key values, you must manually redistribute the updated key values to all clients that call your function.
Authorization scopes (function-level)
There are two access scopes for function-level keys:
Function: These keys apply only to the specific functions under which they're defined. When used as an API key, these only allow access to that function.
Host: Keys with a host scope can be used to access all functions within the function app. When used as an API key, these allow access to any function within the function app.
Each key is named for reference, and there's a default key (named "default") at the function and host level. Function keys take precedence over host keys. When two keys are defined with the same name, the function key is always used.
Master key (admin-level)
Each function app also has an admin-level host key named
_master. In addition to providing host-level access to all functions in the app, the master key also provides administrative access to the runtime REST APIs. This key can't be revoked. When you set an access level of
admin, requests must use the master key; any other key results in access failure.
Due to the elevated permissions in your function app granted by the master key, you shouldn't share this key with third parties or distribute it in native client applications. Use caution when choosing the admin access level.
Specific extensions may require a system-managed key to access webhook endpoints. System keys are designed for extension-specific function endpoints that called by internal components. For example, the Event Grid trigger requires that the subscription use a system key when calling the trigger endpoint. Durable Functions also uses system keys to call Durable Task extension APIs.
The scope of system keys is determined by the extension, but it generally applies to the entire function app. System keys can only be created by specific extensions, and you can't explicitly set their values. Like other keys, you can generate a new value for the key from the portal or by using the key APIs.
The following table compares the uses for various kinds of access keys:
|Execute a function||Specific function||Function|
|Execute a function||Any function||Function or host|
|Call an admin endpoint||Function app||Host (master only)|
|Call Durable Task extension APIs||Function app1||System2|
|Call an extension-specific Webhook (internal)||Function app1||system2|
1Scope determined by the extension.
2Specific names set by extension.
To learn more about access keys, see the HTTP trigger binding article.
By default, keys are stored in a Blob storage container in the account provided by the
AzureWebJobsStorage setting. You can use the AzureWebJobsSecretStorageType setting to override this behavior and store keys in a different location.
|Second storage account||
||Stores keys in Blob storage of a different storage account, based on the SAS URL in AzureWebJobsSecretStorageSas.|
||Keys are persisted on the file system, which is the default in Functions v1.x.|
|Azure Key Vault||
||The key vault set in AzureWebJobsSecretStorageKeyVaultUri is used to store keys. To learn more, see Use Key Vault references for Azure Functions.|
||The resource set in AzureWebJobsKubernetesSecretName is used to store keys. Supported only when running the Functions runtime in Kubernetes. The Azure Functions Core Tools generates the values automatically when deploying to Kubernetes.|
When using Key Vault for key storage, the app settings you need depend on the managed identity type. Functions runtime version 3.x only supports system-assigned managed identities.
|Setting name||System-assigned||User-assigned||App registration|
While function keys can provide some mitigation for unwanted access, the only way to truly secure your function endpoints is by implementing positive authentication of clients accessing your functions. You can then make authorization decisions based on identity.
Enable App Service Authentication/Authorization
The App Service platform lets you use Azure Active Directory (AAD) and several third-party identity providers to authenticate clients. You can use this strategy to implement custom authorization rules for your functions, and you can work with user information from your function code. To learn more, see Authentication and authorization in Azure App Service and Working with client identities.
Use Azure API Management (APIM) to authenticate requests
APIM provides a variety of API security options for incoming requests. To learn more, see API Management authentication policies. With APIM in place, you can configure your function app to accept requests only from the IP address of your APIM instance. To learn more, see IP address restrictions.
As with any application or service, the goal is run your function app with the lowest possible permissions.
User management permissions
Permissions are effective at the function app level. The Contributor role is required to perform most function app-level tasks. You also need the Contributor role along with the Monitoring Reader permission to be able to view log data in Application Insights. Only the Owner role can delete a function app.
Organize functions by privilege
Connection strings and other credentials stored in application settings gives all of the functions in the function app the same set of permissions in the associated resource. Consider minimizing the number of functions with access to specific credentials by moving functions that don't use those credentials to a separate function app. You can always use techniques such as function chaining to pass data between functions in different function apps.
A managed identity from Azure Active Directory (Azure AD) allows your app to easily access other Azure AD-protected resources such as Azure Key Vault. The identity is managed by the Azure platform and does not require you to provision or rotate any secrets. For more about managed identities in Azure AD, see Managed identities for Azure resources.
Your application can be granted two types of identities:
- A system-assigned identity is tied to your application and is deleted if your app is deleted. An app can only have one system-assigned identity.
- A user-assigned identity is a standalone Azure resource that can be assigned to your app. An app can have multiple user-assigned identities.
Managed identities can be used in place of secrets for connections from some triggers and bindings. See Identity-based connections.
For more information, see How to use managed identities for App Service and Azure Functions.
Restrict CORS access
Cross-origin resource sharing (CORS) is a way to allow web apps running in another domain to make requests to your HTTP trigger endpoints. App Service provides built-in support for handing the required CORS headers in HTTP requests. CORS rules are defined on a function app level.
While it's tempting to use a wildcard that allows all sites to access your endpoint, this defeats the purpose of CORS, which is to help prevent cross-site scripting attacks. Instead, add a separate CORS entry for the domain of each web app that must access your endpoint.
To be able to connect to the various services and resources need to run your code, function apps need to be able to access secrets, such as connection strings and service keys. This section describes how to store secrets required by your functions.
Never store secrets in your function code.
By default, you store connection strings and secrets used by your function app and bindings as application settings. This makes these credentials available to both your function code and the various bindings used by the function. The application setting (key) name is used to retrieve the actual value, which is the secret.
For example, every function app requires an associated storage account, which is used by the runtime. By default, the connection to this storage account is stored in an application setting named
App settings and connection strings are stored encrypted in Azure. They're decrypted only before being injected into your app's process memory when the app starts. The encryption keys are rotated regularly. If you prefer to instead manage the secure storage of your secrets, the app setting should instead be references to Azure Key Vault.
You can also encrypt settings by default in the local.settings.json file when developing functions on your local computer. To learn more, see the
IsEncrypted property in the local settings file.
Key Vault references
While application settings are sufficient for most many functions, you may want to share the same secrets across multiple services. In this case, redundant storage of secrets results in more potential vulnerabilities. A more secure approach is to a central secret storage service and use references to this service instead of the secrets themselves.
Azure Key Vault is a service that provides centralized secrets management, with full control over access policies and audit history. You can use a Key Vault reference in the place of a connection string or key in your application settings. To learn more, see Use Key Vault references for App Service and Azure Functions.
Identities may be used in place of secrets for connecting to some resources. This has the advantage of not requiring the management of a secret, and it provides more fine-grained access control and auditing.
When you are writing code that creates the connection to Azure services that support Azure AD authentication, you can choose to use an identity instead of a secret or connection string. Details for both connection methods are covered in the documentation for each service.
Some Azure Functions trigger and binding extensions may be configured using an identity-based connection. Today, this includes the Azure Blob and Azure Queue extensions. For information about how to configure these extensions to use an identity, see How to use identity-based connections in Azure Functions.
Set usage quotas
Consider setting a usage quota on functions running in a Consumption plan. When you set a daily GB-sec limit on the sum total execution of functions in your function app, execution is stopped when the limit is reached. This could potentially help mitigate against malicious code executing your functions. To learn how to estimate consumption for your functions, see Estimating Consumption plan costs.
The triggers and bindings used by your functions don't provide any additional data validation. Your code must validate any data received from a trigger or input binding. If an upstream service is compromised, you don't want unvalidated inputs flowing through your functions. For example, if your function stores data from an Azure Storage queue in a relational database, you must validate the data and parameterize your commands to avoid SQL injection attacks.
Don't assume that the data coming into your function has already been validated or sanitized. It's also a good idea to verify that the data being written to output bindings is valid.
While it seems basic, it's important to write good error handling in your functions. Unhandled errors bubble-up to the host and are handled by the runtime. Different bindings handle processing of errors differently. To learn more, see Azure Functions error handling.
Disable remote debugging
Make sure that remote debugging is disabled, except when you are actively debugging your functions. You can disable remote debugging in the General Settings tab of your function app Configuration in the portal.
Restrict CORS access
Azure Functions supports cross-origin resource sharing (CORS). CORS is configured in the portal and through the Azure CLI. The CORS allowed origins list applies at the function app level. With CORS enabled, responses include the
Access-Control-Allow-Origin header. For more information, see Cross-origin resource sharing.
Don't use wildcards in your allowed origins list. Instead, list the specific domains from which you expect to get requests.
Store data encrypted
Azure Storage encrypts all data in a storage account at rest. For more information, see Azure Storage encryption for data at rest.
By default, data is encrypted with Microsoft-managed keys. For additional control over encryption keys, you can supply customer-managed keys to use for encryption of blob and file data. These keys must be present in Azure Key Vault for Functions to be able to access the storage account. To learn more, see Encryption at rest using customer-managed keys.
Azure Functions tooling an integration make it easy to publish local function project code to Azure. It's important to understand how deployment works when considering security for an Azure Functions topology.
App Service deployments require a set of deployment credentials. These deployment credentials are used to secure your function app deployments. Deployment credentials are managed by the App Service platform and are encrypted at rest.
There are two kinds of deployment credentials:
User-level credentials: one set of credentials for the entire Azure account. It can be used to deploy to App Service for any app, in any subscription, that the Azure account has permission to access. It's the default set that's surfaced in the portal GUI (such as the Overview and Properties of the app's resource page). When a user is granted app access via Role-Based Access Control (RBAC) or coadmin permissions, that user can use their own user-level credentials until the access is revoked. Do not share these credentials with other Azure users.
App-level credentials: one set of credentials for each app. It can be used to deploy to that app only. The credentials for each app are generated automatically at app creation. They can't be configured manually, but can be reset anytime. For a user to be granted access to app-level credentials via (RBAC), that user must be contributor or higher on the app (including Website Contributor built-in role). Readers are not allowed to publish, and can't access those credentials.
At this time, Key Vault isn't supported for deployment credentials. To learn more about managing deployment credentials, see Configure deployment credentials for Azure App Service.
By default, each function app has an FTP endpoint enabled. The FTP endpoint is accessed using deployment credentials.
FTP isn't recommended for deploying your function code. FTP deployments are manual, and they require you to synchronize triggers. To learn more, see FTP deployment.
When you're not planning on using FTP, you should disable it in the portal. If you do choose to use FTP, you should enforce FTPS.
Secure the scm endpoint
Every function app has a corresponding
scm service endpoint that used by the Advanced Tools (Kudu) service for deployments and other App Service site extensions. The scm endpoint for a function app is always a URL in the form
https://<FUNCTION_APP_NAME.scm.azurewebsites.net>. When you use network isolation to secure your functions, you must also account for this endpoint.
By having a separate scm endpoint, you can control deployments and other advanced tools functionalities for function app that are isolated or running in a virtual network. The scm endpoint supports both basic authentication (using deployment credentials) and single sign-on with your Azure portal credentials. To learn more, see Accessing the Kudu service.
Continuous security validation
Since security needs to be considered a every step in the development process, it make sense to also implement security validations in a continuous deployment environment. This is sometimes called DevSecOps. Using Azure DevOps for your deployment pipeline let's you integrate validation into the deployment process. For more information, see Learn how to add continuous security validation to your CI/CD pipeline.
Restricting network access to your function app lets you control who can access your functions endpoints. Functions leverages App Service infrastructure to enable your functions to access resources without using internet-routable addresses or to restrict internet access to a function endpoint. To learn more about these networking options, see Azure Functions networking options.
Set access restrictions
Access restrictions allow you to define lists of allow/deny rules to control traffic to your app. Rules are evaluated in priority order. If there are no rules defined, then your app will accept traffic from any address. To learn more, see Azure App Service Access Restrictions.
Secure the storage account
When you create a function app, you must create or link to a general-purpose Azure Storage account that supports Blob, Queue, and Table storage. You can replace this storage account with one that is secured with service endpoints or private endpoints. For more information, see Restrict your storage account to a virtual network.
Private site access
Azure Private Endpoint is a network interface that connects you privately and securely to a service powered by Azure Private Link. Private Endpoint uses a private IP address from your virtual network, effectively bringing the service into your virtual network.
If you want to make calls to Private Endpoints, then you must make sure that your DNS lookups resolve to the private endpoint. You can enforce this behavior in one of the following ways:
- Integrate with Azure DNS private zones. When your virtual network doesn't have a custom DNS server, this is done automatically.
- Manage the private endpoint in the DNS server used by your app. To do this you must know the private endpoint address and then point the endpoint you are trying to reach to that address using an A record.
- Configure your own DNS server to forward to Azure DNS private zones.
To learn more, see using Private Endpoints for Web Apps.
Deploy your function app in isolation
Azure App Service Environment (ASE) provides a dedicated hosting environment in which to run your functions. ASE lets you configure a single front-end gateway that you can use to authenticate all incoming requests. For more information, see Configuring a Web Application Firewall (WAF) for App Service Environment.
Use a gateway service
Gateway services, such as Azure Application Gateway and Azure Front Door let you set up a Web Application Firewall (WAF). WAF rules are used to monitor or block detected attacks, which provide an extra layer of protection for your functions. To set up a WAF, your function app needs to be running in an ASE or using Private Endpoints (preview). To learn more, see Using Private Endpoints.