【问题标题】:How to secure ASP.NET Core Web API endpoints to only allow requests from React application?如何保护 ASP.NET Core Web API 端点只允许来自 React 应用程序的请求?
【发布时间】:2021-01-03 19:04:15
【问题描述】:

我在一个 Web 应用程序中创建了一个 ASP.NET Core Web API 和 React 并部署到生产环境中。

终点是:

www.myserver.com/obs 是前端应用。

www.myserver.com/obs/api/GetValue 是网络 API。

您如何保护 Web API 端点,以便只有来自 React 应用程序的请求才能调用 API?

例如,如果我要在远程机器上对 www.myserver.com/obs/api/GetValue 进行 Postman 调用,它不应该返回资源。

一种方法是使用 API 密钥,但是您会将 API 密钥放在反应端的什么位置?我读到您可以将它放在 .env 文件中,但是在生产中您仍然可以使用 dev-tools 找到该文件。

我读到的另一个选项是创建一个代理 API,react 应用程序调用并且代理具有 API 密钥,但这似乎有点矫枉过正,有没有我错过的更简单的方法?

【问题讨论】:

    标签: reactjs asp.net-core asp.net-core-webapi


    【解决方案1】:

    你不能。你的 react 应用程序可以被浏览器读取,因此任何知道如何使用浏览器开发者工具或在他们的计算机上拦截 HTTP(s) 请求的人都可以读取。 如果您的 React 应用程序可以与您的 API 通信,那么其他任何人都可以。代理也是如此。你可以找到更详细的答案here

    如果您想控制访问权限,您可以引入身份验证,并且只向受信任的用户授予访问权限,但如果他们真的想要,您仍然无法阻止他们在您的 react 应用程序之外访问您的 API。

    您可以采取一些步骤来增加难度。我建议您阅读有关创建安全 API 的内容。一些帮助您入门的链接:

    【讨论】:

      【解决方案2】:

      一种方法是使用 API 密钥,但是您会将 API 密钥放在哪里 在反应方面?

      是的,您可以创建 API 密钥中间件并使用它来验证请求。如果请求来自 react 应用程序,您可以在请求标头中添加 API 密钥。像这样的代码:

      使用 fetch 方法:

              fetch('/api/MoviesAPI', {
                  method: 'Get', // or 'Post'
                  headers: {
                      'Content-Type': 'application/json',
                      'ApiKey':'Test-value',
                  }, 
                  })
                  .then(response => response.json())
                  .then(data => {
                      console.log('Success:', data);
                  })
                  .catch((error) => {
                      console.log('Error:', error);
                  });
      

      使用Ajax方法:

              $.ajax({
                  type: "Get",
                  url: "/api/MoviesAPI",  //remember change the controller to your owns. 
                  contentType: 'application/json',
                  beforeSend: function (xhr) { xhr.setRequestHeader('ApiKey', 'test-value'); },
                  success: function (data) {
                      console.log(data)
                  },
                  failure: function (response) {
                      console.log(response.responseText);
                  },
                  error: function (response) {
                      console.log(response.responseText);
                  }
              });
      

      更多关于reactjs中自定义header发送请求的详细信息,你可以用谷歌或者Bing搜索“reactjs call api with custom headers”,相关文章很多。

      另外,关于创建 API 密钥中间件,您可以参考以下步骤:

      1. 在API应用中创建一个ApiKeyMiddleware.cs类,添加如下代码:

         public class ApiKeyMiddleware
         {
             public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
             {
                 _next = next;
                 _configuration = configuration;
             }
             private readonly RequestDelegate _next;
             private readonly IConfiguration _configuration;
             public async Task Invoke(HttpContext context)
             {
                 if (context.Request.Path.StartsWithSegments(new PathString("/api")))
                 {
                     //Let's check if this is an API Call
                     if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
                     {
                         // validate the supplied API key
                         // Validate it
                         var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
                         await ValidateApiKey(context, _next, headerKey);
                     }
                     else
                     {
                         await _next.Invoke(context);
                     }
                 }
                 else
                 {
                     await _next.Invoke(context);
                 }
             }
             private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
             {
                 // validate it here
                 var valid = false;
        
                 var Apikey = _configuration["ApiKey"];
                 if (key != null && key==Apikey)
                 {
                     valid = true;
                 }
        
                 if (!valid)
                 {
                     context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                     await context.Response.WriteAsync("Invalid API Key");
                 }
                 else
                 {
                     var identity = new GenericIdentity("API");
                     var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
                     context.User = principal;
                     await next.Invoke(context);
                 }
             }
         }
        
      2. 在 Startup.cs 文件的 Configure 方法中注册此中间件。

             app.UseMiddleware<ApiKeyMiddleware>(); //add APIkeyMiddleware
             app.UseHttpsRedirection();
             app.UseStaticFiles();
        
             app.UseRouting();
        
             app.UseAuthentication(); //Call the UseAuthentication
             app.UseAuthorization();
        
      3. 在API控制器或动作方法中,添加Authorize属性。

         [HttpGet]
         [Authorize]
         public async Task<ActionResult> GetMovie()
         {
             return Ok(await _context.Movie.ToListAsync());
         }
        

      那么,如果请求头中不包含ApiKey或者key值无效,则不会返回资源。

      编辑:

      关于 API 密钥值,您可以将它们存储在 appsettings.json 文件或内存中的 .NET 对象中。使用时可以从配置中获取。

      例如:存放在appsettings.json文件中:

      {
         ...  
         "Apikey": "my Test API key"  
      }
      

      然后,使用以下代码获取键值

        public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
          {
              _next = next;
              _configuration = configuration;
          }
          private readonly RequestDelegate _next;
          private readonly IConfiguration _configuration;
      
          private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
          {
              // validate it here
              var valid = false;
              //get the key value from configuration.
              var Apikey = _configuration["ApiKey"]; 
              ...
      

      在反应方面,您可以创建一个服务来获取此键值,然后使用 api 键发送请求。

      【讨论】:

      • 感谢您的回复,我了解如何使用 API 密钥发送请求,但是您实际将实际 API 密钥“测试值”存储在哪里?我们不想在代码中硬编码 ApiKey,那么你将如何传递它?
      • 您可以将 API 密钥值存储在 appsettings.json 文件或内存 .NET 对象中。然后在反应方面,您可以创建一个服务并从配置中获取键值。参考:Configuration in ASP.NET Core.
      • @ZhiLv 如果反应应用程序可以从服务器端配置请求键值,是什么阻止其他人做同样的事情,或者观察这个请求,即。通过 Fiddler,学习密钥?
      • 嗯,我认为有一些方法可以存储和检索秘密。否则,将 apikey 加密/编码存储在您的反应中,因此它不是浏览器/开发工具上可能可见的实际值。然后您可以从请求标头参数中解密您的 api 并确保这是预期值。对于它的价值,对于我的客户端中的字符,我将 base64 编码值反转为 Upper as Lower 和 Lower as Upper。然后将它们还原并使用我的基本身份验证的秘密值。
      • @Ak777 - 你可以使用 jibberish 作为你的“秘密”;或来自客户端的任何其他类型的加密值。任何人仍然可以看到正在提交给服务器的请求标头/正文,然后将相同的乱码/加密值与 Postman、curl 等一起使用......
      猜你喜欢
      • 2021-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-13
      • 1970-01-01
      • 2021-04-14
      • 2021-05-24
      • 1970-01-01
      相关资源
      最近更新 更多