简体   繁体   English

ASP.NET Core Web API身份验证允许未经授权的访问

[英]ASP.NET Core Web API Authentication allowing unauthorized access

I'm creating an authentication scheme for my ASP.NET Core API. 我正在为ASP.NET Core API创建身份验证方案。

It calls my handler and hits the breakpoint just fine, but the API calls still return results even when the authorization fails. 它会调用我的处理程序,并且可以很好地达到断点,但是即使授权失败,API调用仍会返回结果。

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
    if (!Request.Headers.ContainsKey(AuthorizationHeaderName))
    {
        //Authorization header not in request
        return AuthenticateResult.Fail("Missing Authorization header");
    }

In my naive understanding, it shouldn't return data if it fails authentication. 以我的幼稚理解,如果身份验证失败,它不应返回数据。

What am I missing? 我想念什么?

DETAILS 细节

I register the scheme like this in Startup.ConfigureServices 我在Startup.ConfigureServices中注册了这样的方案

services.AddAuthentication(options => {
    // This (options.Default..Scheme) causes the default authentication scheme to be set.
    // Without this, the Authorization header is not checked and
    // you'll get no results. 
   options.DefaultAuthenticateScheme = BasicAuthenticationDefaults.AuthenticationScheme;
}).AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("Basic", null);

Startup.Config calls Startup.Config调用

 app.UseAuthentication();
 app.UseHttpsRedirection();
 app.UseMvc();

The rest of the code looks like this: 其余代码如下所示:

using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace WebAPI.Authentication
{
    public interface IBasicAuthenticationService
    {
        Task<AuthenticateResult> HandleAuthenticateAsync();
    }

    public static class BasicAuthenticationDefaults
    {
        public const string AuthenticationScheme = "Basic";
    }

    public class BasicAuthenticationOptions : AuthenticationSchemeOptions
    { }

    public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
    {
        private const string AuthorizationHeaderName = "Authorization";
        private const string BasicSchemeName = "Basic";

        public BasicAuthenticationHandler(
            IOptionsMonitor<BasicAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey(AuthorizationHeaderName))
            {  // Rejected here. Should fail.
                //Authorization header not in request
                return AuthenticateResult.Fail("Missing Authorization header");
            }

            if ....  // never gets this far
            }

            return AuthenticateResult.Success(ticket);
        }
    }
}

And here is the controller that is improperly returning results. 这是不正确返回结果的控制器。

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace TMAWebAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }
    } 
}

All these lines of code get hit in the debugger, so that part seems to be working correctly. 所有这些代码行都在调试器中被击中,因此该部分似乎正常工作。

But the API call is still returning results even though it fails authentication. 但是,即使验证失败,API调用仍会返回结果。

Update: 更新:
Adding an AuthenticationScheme attribute to the Controller makes it fail. 向Controller添加AuthenticationScheme属性会使它失败。
Like this: 像这样:

[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = "Basic")]
public class ValuesController : ControllerBase

This is no good. 不好 It should fail by default instead of having to add it to every controller. 默认情况下它将失败,而不必将其添加到每个控制器。

Update 2: 更新2:

Adding a filter to services.AddMvc looks promising, but that doesn't work either. 向services.AddMvc添加一个过滤器看起来很有希望,但这也不起作用。 Documentation claims that you don't have to implement an Authorization filter since they are included. 文档声称由于包含了它们,因此您不必实施授权过滤器。 Not that I can find. 不是我能找到。

I inherited from AuthorizeAttribute, using the idea from Matti Price, and IFilterMetadata, required by AddMvc. 我继承了AuthorizeAttribute,并使用了Matti Price和AddMvc所需的IFilterMetadata的思想。 That compiles but still allows unauthorized access. 可以编译,但仍允许未经授权的访问。

public class BasicAuthorizeAttribute : AuthorizeAttribute, IFilterMetadata { }       

services.AddMvc(options => {
    options.Filters.Add(typeof(BasicAuthorizeAttribute));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Update 3: 更新3:
Tried 试着

policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser()

as suggested by Matti, but that returned 按照Matti的建议,但是又回来了

InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

I don't have any interest in redirecting to a non-existent login page for an API, so I tried 我对重定向到API的不存在的登录页面没有任何兴趣,因此我尝试了

policy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(new[] {BasicAuthenticationDefaults.AuthenticationScheme })

That compiles but throws the exception 编译但引发异常

InvalidOperationException Message=AuthorizationPolicy must have at least one requirement.

You'll need to add the [Authorize] attribute to your controllers to cause the authorization to actually do anything with it's result. 您需要将[Authorize]属性添加到控制器,以使授权实际上对其结果执行任何操作。 You can add it globally like this : 您可以像这样在全局添加它:

services.AddMvc(config =>
{
    var policy = new AuthorizationPolicyBuilder()
                 .RequireAuthenticatedUser()
                 .Build();
    config.Filters.Add(new AuthorizeFilter(policy));
});

The final resolution uses AddMVC. 最终解决方案使用AddMVC。 The solution was that in addition to adding a scheme, the scheme needed a requirement. 解决方案是,除了添加方案之外,该方案还需要一个要求。

This works-ish. 这行得通。 If I don't send any auth header, it returns an empty page. 如果我不发送任何身份验证标头,它将返回一个空白页。 If I send an expired header, then it sends 500. Should be 401. But it doesn't return any values and that's really all I care about. 如果我发送了一个过期的标头,则它发送500。应该是401。但是它不返回任何值,而这正是我所关心的。

public class TokenAuthorizationRequirement: IAuthorizationRequirement {}

services.AddMvc(config => {
   var policy = new AuthorizationPolicyBuilder()
      .AddAuthenticationSchemes(new[] {BasicAuthenticationDefaults.AuthenticationScheme })
      .AddRequirements(new BasicAuthorizationRequirement())
      .Build();
   config.Filters.Add(new AuthorizeFilter(policy));
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM