Skip to content

CORS – Cross-Origin Resource Sharing

The following article is a copy of article written by Lydia Hallie

When we place a request for a particular website, the browser first has to make a request to a server in order to fetch that data! the client sends an HTTP request with all the information that the server needs in order to send that data back to the client.

Lets say we’re trying to fetch some user info on www.mywebsite.com from a server that’s located at api.website.com

But when we want to request the same server from a different domain (www.anotherdomain.com) we get an error saying “Access to fetch has been blocked by CORS policy”

Same-Origin Policy

Web enforces same-origin policy. i.e we can only access resources that are located at the same origin as the origin of our request!

But why same-origin policy exists?

if the web is open, people will be able to access virus links and will be able to get their data compromised. With Same-Origin policy, we are able to reduce the attacks like:

  • XSS aka Cross-Site Scripting: It is a method where an attacker injects malicious script into the trusted website to get user-sensitive data. This is mostly mitigated by treating all incoming data as untrusted data and thus perform operations like (validations, String Escape Check, and String Sanitization).
  • CSFR aka Cross-site request forgery: A method when user clicks on unsafe links which he received via mail, instant message, or some website. Causing the browser to perform an unwanted action on another trusted site where the user is authenticated. This is possible when the browser automatically sends authorization resources, such as session cookies, IP address, or similar with each request. To mitigate this we could leverage CSFR token and SameSite.

Visualization of Attack:

let’s say our attacker has a website: www.evilwebsite.com which mimics your bank website. Unknowingly you tried transferring the amount, if Same-Origin Policy is not there the attacker can send all the amount to his account.

But with the same-origin policy, this can be mitigated as evilwebsite.com and bank.com don’t share the same origin.

These problems are mainly caused due to scripting, so the same-origin policy was introduced which later was adapted by Browsers as a standard.

A resource is cross-origin when its located at a different (sub)domain, protocol, or port.

Client Side CORS Handling:

When a request is made client automatically adds an extra header to our HTTP request “Origin” for CORS request.

So if our client is on the acceptable CORS list we can access it. In case if our Server doesn’t have CORS enabled? How are we going to access it?
One solution is to use Proxy Requests.

New WebApi Project:

mkdir CORS
cd CORS
mkdir CORS server
cd "CORS Server"
dotnet new webapi

HomeController.ts

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

namespace cors_server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        [Route("FetchValues")]
        [HttpGet]
        public IEnumerable<string> FetchValues()
        {
            return new string[] { "TestValue1", "TestValue2" };
        }
    }
}

New Angular Project

cd CORS
ng new AngularProxy

test.service.ts:

import { environment } from './../environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'text/plain' })
};

@Injectable({
  providedIn: 'root'
})
export class TestService {

  constructor(private http: HttpClient) { }

  baseURL = environment.apiUrl;


  getTestMessage() {
    return this.http.get<Observable<string>>(this.baseURL + 'Home/FetchValues', httpOptions);
  }
}

BaseURL is declared in environment.ts

export const environment = {
  production: false,
  apiUrl: "https://localhost:5001/api/"
};

AppComponent.ts

import { TestService } from './../_services/test.service';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'AngularProxy';
  testMessage$: Observable<string>;

  constructor(private testService: TestService) { }

  ngOnInit() {
    this.getTestMessage();
  }

  getTestMessage() {
    this.testService.getTestMessage().subscribe(data => {
      console.log(data);
    })
  }
}

AppModule.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Lets try accessing the angular app

dotnet watch run
ng serve --open

Checking our Developer tools we see that we are getting CORS issue

Now lets add the proxy.config.json file to our angular solution

{
    "/api/*": {
        "target": "https://localhost:5001",
        "secure": false,
        "logLevel": "debug"
    }
}

This configuration file specifies that any HTTP request which starts with the /app/ path will be sent to the proxy which will redirect it to the target hostname.
The secure option is used to enforce usage of SSL.

Add proxyConfig key to angular.json

        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "AngularProxy:build",
            "proxyConfig": "src/proxy.conf.json"
          },
          "configurations": {
            "production": {
              "browserTarget": "AngularProxy:build:production"
            }
          }
        },

Update our test.service.ts

import { environment } from './../environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'text/plain' })
};

@Injectable({
  providedIn: 'root'
})
export class TestService {

  constructor(private http: HttpClient) { }

  baseURL = environment.apiUrl;


  getTestMessage() {

    //return this.http.get<Observable<string>>(this.baseURL + 'Home/FetchValues', httpOptions);
    return this.http.get<Observable<string>>('api/Home/FetchValues', httpOptions);
  }
}

Now we don’t get any errors on our console and we are also getting the data.

Server Side Handling:

At the server end we can let which client can access our data.

Startup.cs

Add the below statements to your webapi’s startup file.

public void ConfigureServices(IServiceCollection services)
{
     //CORS handling
     services.AddCors();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
     app.UseCors(policy => policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:4200"));
}

test.service.ts

update the angular test service back to using the baseurl and check the console. you will see that the error is not occurring.

return this.http.get<Observable<string>>(this.baseURL + 'Home/FetchValues', httpOptions);
//return this.http.get<Observable<string>>('api/Home/FetchValues', httpOptions);

When the server has enabled CORS, Incoming requests are classified into simple requests (GET, POST) where headers are sent with the requests and we don’t have custom headers. Preflight request (Complex requests) (PUT, PATCH, DELETE) where the server receives preflight requests and sends back an empty response with several CORS headers, here browser checks and decides to allow the request to send the request or not.

Published inCS FundamentalsUncategorised

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *