从 Azure 函数安全地调用 Web Api

Calling WebApi from Azure function securely

我有一个场景,其中我有 asp.net 核心网络 api 和一个 azure 函数。我每 1 小时从 azure time 触发函数调用 web api。我没有在 Web api 上启用身份验证,我不想 public 访问它。只有我的 azure 函数应该能够访问网络 api。我如何限制 web api 从 public 访问,但只能从 azure 函数访问而不实施身份验证。

我尝试了以下, 在 webapi appsettings 文件中,我更新了“AllowedHosts”:“https://testfuntionapp.azurewebsites.net”。通过此更改,我的 testfuntionapp 无法访问网络 api。

我正在尝试一种具有成本效益的解决方案。

请检查我的发现是否有助于:

How can I restrict web api to access from public but only from azure function without implementing authentication.

这就是 Azure Virtual Networks 的来源。

创建虚拟网络,将 Azure 函数配置为只能在此 VNet 上调用,还可以配置核心应用程序对 VNet 的访问。

  • 通过使用专用端点,只能通过您的虚拟网络访问资源。
  • 如果启用虚拟网络集成,则 Azure Functions 可以通过配置的专用端点访问指定资源,这是更高级别的安全性。

参考文献:

我还建议按照 HariKrishnaRajoli 的建议使用专用端点。

理论上,仅配置 http header filtering for access restriction rules 并且只允许包含只有您的 azure 函数代码知道的秘密 header 的请求也足够了。这可能与“基本”HTTP 身份验证安全性相当,并且比其他选项弱。

作为替代方案,您可以使用 AAD 身份验证而无需真正“实施”很多身份验证代码。

  1. 在WebApi
  2. 上配置Easy Auth
  3. 为您的 Azure 函数配置 ManagedIdentity
  4. 使用“App Role Assignments”授予您的托管身份访问权限以调用 WebApi“
  5. 将您的 Azure 函数扩展到 acquire 并在调用 WebApi 时传递访问令牌

如果您只希望 Azure 函数能够访问您的网站 API,您可以通过 IP 地址限制对您网站的访问 API。

在您的网站 API 的 appsettings.json 文件中,添加以下内容:

"AllowedHosts": "https://testfuntionapp.azurewebsites.net"

这将限制对您的网站的访问 API 只有来自指定 Azure 功能的请求。

执行此操作的最快和“最肮脏”的方法是将 API 键作为 header 从函数传递给您的 REST API。

您可以将 API 键硬编码到您的配置文件或从您的数据库加载它。

我的实现:

namespace CoreApi.Middleware
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class RequirePartnerApiKeyAttribute : Attribute, IAsyncActionFilter
    {
        private const string ApiKeyHeaderName = "x-partner-apikey";

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //----------------------------------------------------
            // Validate Api Key
            //----------------------------------------------------
            if (!context.HttpContext.Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKey) ||
                string.IsNullOrEmpty(apiKey))
            {
                context.Result = new UnauthorizedResult();
                return;
            }

            var partnerService = context.HttpContext.RequestServices.GetRequiredService<IPartnerService>();
            var apiRequest = new PartnerAuthenticateRequest { PartnerApiKey = apiKey };
            var partner = partnerService.Authenticate(apiRequest, new CancellationToken()).Result;
            if (partner == null)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
            else
            {
                if (Enum.IsDefined(typeof(ApiKeyTypes), partner.PartnerName))
                    context.HttpContext.Items["APIKeyName"] = Enum.Parse(typeof(ApiKeyTypes), partner.PartnerName);
            }
            //----------------------------------------------------

            await next();
        }
    }
}

您可以通过调用以下任何中间件访问 API 密钥:context.HttpContext.Items["APIKeyName"]