Enable cross domain request (CORS) in ASP.NET Core

Posted by jmgrhm on Sun, 03 Nov 2019 18:48:21 +0100

This article describes how to enable CORS in ASP.NET Core applications.

Browser security prevents web pages from sending requests to other domains rather than serving them. This restriction is known as the same origin policy. The same source policy prevents malicious sites from reading sensitive data from another site. Sometimes, you may want to allow other sites to make cross domain requests for your app. For more information, see MOZILLA CORS.

Cross source resource sharing(CORS):

  • Is a W3C standard that allows servers to relax the same source policy.
  • It is not a security function. CORS relaxes security. The API cannot be more secure by allowing CORS. For more information, see CORS work Principle.
  • Allow the server to explicitly allow some cross source requests while rejecting others.
  • Than earlier technologies (e.g. JSONP )Safer and more flexible.

View or download sample code(How to download)

Same origin

If two URLs have the same scheme, host and port( RFC 6454 ), they have the same source.

The two URLs have the same source:

  • https://example.com/foo.html
  • https://example.com/bar.html

The origins of these URLs are different from the first two:

  • https://example.net – different domains
  • https://www.example.com/foo.html – different subdomains
  • http://example.com/foo.html – different scenarios
  • https://example.com:9000/foo.html – different ports

Internet Explorer does not consider this port when comparing sources.

CORS with naming strategy and Middleware

CORS middleware handles cross domain requests. The following code enables CORS for the entire application by specifying the source:

 
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(MyAllowSpecificOrigins,
            builder =>
            {
                builder.WithOrigins("http://example.com",
                                    "http://www.contoso.com");
            });
        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseCors(MyAllowSpecificOrigins); 

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

 

Previous code:

  • Set the policy name to "myAllowSpecificOrigins". The policy name is any name.
  • Call UseCors Extension method, which enables CORS.
  • Use lambda expression Call AddCors . Lambda takes the @ no \t 0 object. Later in this article configuration option , such as WithOrigins.

@The NO-0 method call adds the CORS service to the application's service container:

 
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
        builder =>
        {
            builder.WithOrigins("http://example.com",
                                "http://www.contoso.com");
        });
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

 

For more information, refer to the CORS policy options.

@The NO-0 method can link methods, as shown in the following code:

 
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
        builder =>
        {
            builder.WithOrigins("http://example.com",
                                "http://www.contoso.com")
                                .AllowAnyHeader()
                                .AllowAnyMethod();
        });
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

 

Note: URLs must not contain trailing slashes (/). If the URL terminates with /, the comparison returns false with no headers.

Apply CORS policy to all endpoints

The following code applies the CORS policy to all application endpoints through the CORS middleware:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Preceding code ommitted.
    app.UseRouting();

    app.UseCors();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

    // Following code ommited.
}

 

Warning

Through endpoint routing, the CORS middleware must be configured to execute between calls to @ no \\\\\\\\\. Incorrect configuration will cause the middleware to stop running normally.

see also Enable cors in Razor Pages, controllers, and methods of operation, To apply the cors policy at the page / controller / operation level.

For instructions on testing the above codes, see Test CORS .

 

Enabling Cors with endpoint routing

With endpoint routing, you can enable CORS based on each endpoint and use the extended method set of @ no \t.

app.UseEndpoints(endpoints =>
{
  endpoints.MapGet("/echo", async context => context.Response.WriteAsync("echo"))
    .RequireCors("policy-name");
});

 

Similarly, CORS can be enabled for all controllers:

app.UseEndpoints(endpoints => { endpoints.MapControllers().RequireCors("policy-name"); }); 

Enable CORS with properties

@No__t-1EnableCors @ no__t Property provides an alternative method for global application of CORS. The @ NO-0 attribute enables CORS for the selected endpoint, not all endpoints.

Use @ no \t to specify the default policy, and [EnableCors("{Policy String}")] to specify the policy.

@The NO-0 feature can be applied to:

  • Razor page PageModel
  • Controller
  • Operation method of controller

You can apply different policies to the controller / page model / operation, [EnableCors] property. When [EnableCors] attribute is applied to controller / page model / operation method and CORS is enabled in middleware, these two strategies will be applied. It is recommended not to combine strategies. Use the [EnableCors] feature or middleware in the same application.

The following code applies different policies to each method:

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors]        // Default policy.
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        switch (id)
        {
            case 1:
                return "green widget";
            case 2:
                return "red widget";
            default:
                return NotFound();
        }
    }
}

 


The following code is created CORS The default policy and name are "AnotherPolicy" Strategy:

 
public class StartupMultiPolicy
{
    public StartupMultiPolicy(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                builder =>
                {
                   
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });

        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

 

Disable CORS

@No__t-1DisableCors @ no__t-2 Property to disable CORS for controller / page model / operation.

CORS policy options

This section describes the various options that can be set in the CORS policy:

Call in Startup.ConfigureServices AddPolicy . For some options, it's best to read first How CORS works Part.

Set allowed sources

AllowAnyOrigin - allow CORS requests from all sources and any scenarios (http or https). AllowAnyOrigin is not secure because any web site can make cross domain requests to applications.

Remarks

Specifying @ No UU T 0 and @ No UU t0 as insecure configurations may result in cross site request forgery. When using these two methods to configure an application, the CORS service will return an invalid CORS response.

AllowAnyOrigin affects the prefetch request and the @ No UU t header. For more information, see Preview request Part.

SetIsOriginAllowedToAllowWildcardSubdomains - set @ no \t of the policy to a function that allows the source to match the configured wildcard field when evaluating whether to allow the source.

 
options.AddPolicy("AllowSubdomain",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
            .SetIsOriginAllowedToAllowWildcardSubdomains();
    });

 

Set allowed HTTP methods

AllowAnyMethod:

  • Allow any HTTP method:
  • Affects the prefetch request and the @ no buut 0 header. For more information, see Preview request Part.

Set allowed request headers

To allow a specific header (called the author request header) to be sent in a CORS request, call WithHeaders And specify the allowed headers:

 
options.AddPolicy("AllowHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

 


To allow all authors to request headers, call AllowAnyHeader:

 
options.AddPolicy("AllowAllHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowAnyHeader();
    });

This setting affects the prefetch request and the @ no buut 0 header. For more information, see Preview request Part.

The CORS middleware policy can only be used to match the specific headers specified by WithHeaders if the headers sent in access control request headers exactly match the headers specified in WithHeaders.

For example, consider an application configured as follows:

 app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl)); 

CORS middleware uses the following request header to reject the pre check request because content language is not listed in WithHeaders( HeaderNames):

Access-Control-Request-Headers: Cache-Control, Content-Language

The app returns a 200 OK response, but does not send the CORS header back. As a result, the browser will not attempt cross domain requests.

Set the exposed response header

By default, the browser does not expose all the response headers to the application. For more information, see W3C cross domain resource sharing (terminology): simple response headers.

The response headers available by default are:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

The CORS specification calls these headers simple response headers. To make other headers available to the application, call WithExposedHeaders:

 
options.AddPolicy("ExposeResponseHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .WithExposedHeaders("x-custom-header");
    });

 


Credentials in a cross domain request

Credentials need to be specially processed in the CORS request. By default, browsers do not use cross domain requests to send credentials. Credentials include cookie s and HTTP authentication schemes. To send credentials using a cross domain request, the client must set XMLHttpRequest.withCredentials to true.

Directly use @ no \t:

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

 

Using jQuery:

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

 

Use Extract API:

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

 

The server must allow credentials. To allow cross domain credentials, call AllowCredentials:

 
options.AddPolicy("AllowCredentials",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowCredentials();
    });

 

The HTTP response contains a @ no \t 0 header that informs the browser server to allow credentials for cross source requests.

If the browser sends credentials, but the response does no t contain a valid @ no \t 0 header, the browser does not expose the response to the application and the cross source request fails.

Allowing cross domain credentials poses a security risk. A web site in another domain can send the credentials of the logged in user to the application on behalf of the user without the knowledge of the user.

The CORS specification also states that it is no t valid to set the source to "*" (all sources) if the @ No UU t header exists.

Preview request

For some CORS requests, the browser will send other requests before the actual request. This request is called a pre inspection request. The browser can skip the pre check request if:

  • The request method is GET, HEAD, or POST.
  • The request header with 'Accept, Accept language, content language, content type' or @ no \.
  • @The header for no, if set, has one of the following values:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

The rules on the request header set for client requests apply by calling the header on the @ No UUT @ No UUT object. The CORS specification calls these header authors to request headers. The rule does not apply to headers that the browser can set, such as user agent, Host, or content length.

The following is an example of a pre check request:

OPTIONS https://myservice.azurewebsites.net/api/test HTTP/1.1
Accept: */*
Origin: https://myclient.azurewebsites.net
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net
Content-Length: 0

The pre flight request uses the HTTP OPTIONS method. It includes two special headers:

  • Access control request method: the HTTP method that will be used for the actual request.
  • Access control request headers: list of request headers applied to the actual request. As mentioned earlier, this does not include headers for browser settings, such as user agent.

The CORS prefetch request may include a @ no \\\\\\\\\.

To allow specific headers, call WithHeaders:

 options.AddPolicy("AllowHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

 

To allow all authors to request headers, call AllowAnyHeader:

 
options.AddPolicy("AllowAllHeaders",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowAnyHeader();
    });

 

Browser settings are not exactly the same access control request headers. If the header is set to @ no \ AllowAnyHeader )Any content other than, should contain at least Accept, content type, and @ no \t, as well as any custom headers to support.

Here is a sample response to a pre check request (assuming the server allows it):

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: https://myclient.azurewebsites.net
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Wed, 20 May 2015 06:33:22 GMT

The response includes a @ No UU T 0 header that lists the allowed methods and optionally a @ No UU t0 header that lists the allowed headers. If the pre check request is successful, the browser sends the actual request.

If the pre check request is rejected, the application will return a 200 OK response, but the CORS header will not be sent back. As a result, the browser will not attempt cross domain requests.

Set pre inspection expiration time

@The no header specifies the length of time that a response to a pre check request can be cached. To set this header, call SetPreflightMaxAge:

options.AddPolicy("SetPreflightExpiration",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

 

How CORS works

This section describes the HTTP message level CORS What happens in the request.

  • CORS is not a security function. CORS is a W3C standard that allows servers to relax the same source policy.
    • For example, a malicious execution component might use Block cross site scripting (XSS) And perform cross site requests to CORS enabled sites to steal information.
  • The API cannot be more secure by allowing CORS.
    • It is enforced by the client (browser). The server executes the request and returns the response, which is the client that returns the error and blocks the response. For example, any of the following tools will display the server response:
  • This is a method that enables the server to allow browsers to perform cross source XHR or Get API Request, otherwise it will be forbidden.
    • The browser (without CORS) cannot perform cross domain requests. Before CORS, use JSONP To bypass this restriction. JSONP does not use XHR, it uses < script > tags to receive responses. Allow scripts to be loaded across sources.

CORS specification Introduces several new HTTP headers that enable cross domain requests. If the browser supports CORS, these headers are automatically set for cross domain requests. To enable CORS, you do not need to customize JavaScript code.

Here is an example of a cross source request. The @ no? 0 header provides the domain of the requested site. The header of @ no \t is required and must be different from the host.

GET https://myservice.azurewebsites.net/api/test HTTP/1.1
Referer: https://myclient.azurewebsites.net/
Accept: */*
Accept-Language: en-US
Origin: https://myclient.azurewebsites.net
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

If the server allows the request, the header of @ no \t is set in the response. The value of this header can match the @ No UU T 0 header in the request, or it can be a wildcard value "*", indicating that any source is allowed:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: https://myclient.azurewebsites.net
Date: Wed, 20 May 2015 06:27:30 GMT
Content-Length: 12

Test message

Cross domain requests fail if the response does no t include a header for @ no \. Specifically, the browser does not allow the request. Even if the server returns a successful response, the browser will not provide the response to the client application.

 

Test CORS

Test CORS:

  1. Create API project . Or, you can Download the sample.
  2. Use one of the methods in this document to enable CORS. For example:
 
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    // Shows UseCors with CorsPolicyBuilder.
    app.UseCors(builder =>
    {
        builder.WithOrigins("http://example.com",
                            "http://www.contoso.com",
                            "https://localhost:44375",
                            "https://localhost:5001");
    });

    app.UseHttpsRedirection();
    app.UseMvc();
}

 



 warning

WithOrigins("https://Localhost: < port > ");; should only be used for testing similar to Download sample code Example application of.

  1. Establish web Application project( Razor Pages or MVC).  This example uses Razor Pages.  Can be in conjunction with API Created in the same solution for the project web Application.
  2. Add the following highlighted code to the index cshtml In the document:
 
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">CORS Test</h1>
</div>

<div>
    <input type="button" value="Test" 
           onclick="requestVal('https://<web app>.azurewebsites.net/api/values')" />
    <span id='result'></span>
</div>

<script>
    function requestVal(uri) {
        const resultSpan = document.getElementById('result');

        fetch(uri)
            .then(response => response.json())
            .then(data => resultSpan.innerText = data)
            .catch(error => resultSpan.innerText = 'See F12 Console for error');
    }
</script>

  1. In the above code, replace URL: 'HTTPS: / / < web app >. Azurewebsites. Net / API / values / 1' with the URL of the deployed application.

  2. Deploy the API project. For example, Deploy to Azure.

  3. Run the Razor Pages or MVC app from the desktop, and then click the test button. Use the F12 tool to view error messages.

  4. Remove the localhost source from @ No UU T and deploy the application. Or, use a different port to run the client application. For example, run in Visual Studio.

  5. Test with client applications. CORS failure returned an error, but the error message cannot be used in JavaScript. Use the console tab in the F12 tool to view the errors. "Depending on your browser, you will receive errors similar to the following (in the F12 tool console):

    • Using Microsoft Edge:

      SEC7120: [CORS] source https://localhost:44375 can't find the cross source resource access control allowed source response header https://localhost:44375

    • Using Chrome:

      Access to XMLHttpRequest of source https://localhost:44375 https://webapi.azurewebsites.net/api/values/1 has been blocked by CORS policy: there is no access control allow header on the requested resource.

Tools (such as Fiddler or Postman )Test the CORS enabled endpoint. When using the tool, the request source specified by the header of @ no \t must be different from the host receiving the request. If the request is not cross domain based on the value of the Origin header:

Topics: ASP.NET Lambda Attribute encoding Windows