How to Enable CORS with HTTPOnly Cookie to Secure Token?


In this text, we see how to allow CORS (Cross-Origin Resource Sharing) with HTTPOnly cookie to protected our entry tokens.

Nowadays, backend servers and frontend purchasers are deployed on other domain names. Therefore the server has to allow CORS to enable purchasers to be in contact with the server on browsers.

Also, servers are imposing stateless authentication for higher scalability. Tokens are saved and maintained at the client-side, however now not at the server facet like consultation. For safety, it’s higher to retailer tokens in HTTPOnly cookies.

Why are Cross-Origin requests blocked?

Let’s think that our frontend software deployed at https://app.geekflare.com. A script loaded in https://app.geekflare.comcan handiest request same-origin sources.

Whenever we strive to ship a cross-origin request to some other area https://api.geekflare.com or some other port https://app.geekflare.com:3000 or some other scheme http://app.geekflare.com, the cross-origin request gets blocked through the browser.

But why an analogous request blocked through the browser be despatched from any backend server the usage of curl request or despatched through the usage of equipment just like the postman with none CORS drawback. It’s in fact for safety to give protection to customers from assaults like CSRF(Cross-Site Request Forgery).

Let’s take an instance, think if any consumer logged in their very own PayPal account of their browser. If we will be able to ship a cross-origin request to paypal.com from a script loaded on some other area malicious.com with none CORS error/blocking off like we ship the same-origin request.

Attackers can simply ship their malicious web page https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account  through changing it to short-URL to disguise the true URL. When the consumer clicks a malicious hyperlink, the script loaded within the area malicious.com will ship a cross-origin request to PayPal to move the consumer quantity to the attacker PayPal account gets accomplished. All the customers who’ve logged in to their PayPal account and clicked this malicious hyperlink will lose their cash. Anyone can simply scouse borrow cash and not using a PayPal account consumer wisdom.

For the above explanation why, browsers block the entire cross-origin requests.

What is CORS(Cross-Origin Resource Sharing)?

CORS is a header-based safety mechanism utilized by the server to inform the browser to ship a cross-origin request from relied on domain names.
The server enabled with CORS headers used to keep away from cross-origin requests blocked through browsers.

How CORS works?

As the server already outlined its relied on area in its CORS configuration. When we ship a request to the server, the reaction will inform the browser the asked area is relied on or now not in its header.

Two varieties of CORS requests are there:

  • Simple request
  • Preflight Request

Simple Request:

CORS-simple request flow tells that it sends a cross-origin request but when it received response. It checks for headers.

 

  • The browser sends the request to a cross-origin area with beginning(https://app.geekflare.com).
  • The server sends again the corresponding reaction with allowed strategies and allowed beginning.
  • After receiving the request, the browser will test the despatched beginning header worth(https://app.geekflare.com) and won access-control-allow-origin worth(https://app.geekflare.com) are an analogous or wildcard(*). Otherwise, It will throw a CORS error.

Preflight Request:

CORS-Preflight Request Image which show the flow of cross-origin request with OPTIONS preflight request before sending actual request for verifying headers.

  • Depending at the customized request parameter from the cross-origin request like strategies(PUT, DELETE) or customized headers or other content-type, and so on. The browser will come to a decision to ship a preflight OPTIONS request to test whether or not the true request is secure to ship or now not.
  • After receiving the reaction(standing code: 204, this means that no content material), the browser will test for the access-control-allow parameters for the true request. If the request parameters are allowed through the server. The precise cross-origin request despatched and won

If access-control-allow-origin: *,then the reaction is permitted for all origins. But it’s not secure until you wish to have it.

How to allow CORS?

To allow CORS for any area, allow CORS headers to enable beginning, strategies, customized headers, credentials, and so on.

The browser reads the CORS header from the server and permits precise requests from the customer handiest after verifying request parameters.

  • Access-Control-Allow-Origin: To specify actual domain names(https://app.geekflate.com, https://lab.geekflare.com) or wildcard(*)
  • Access-Control-Allow-Methods: To enable the HTTP strategies(GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) that handiest we’d like.
  • Access-Control-Allow-Headers: To enable handiest particular Headers(Authorization, csrf-token)
  • Access-Control-Allow-Credentials: Boolean worth used to enable cross-origin-credentials(cookies, authorization header).
  • Access-Control-Max-Age: Tells the browser to cache the preflight reaction for a while.
  • Access-Control-Expose-Headers: Specify headers which can be obtainable through client-side script.

For enabling CORS in apache and Nginx webserver, observe this instructional.

Enabling CORS in ExpressJS

Let’s take an instance ExpressJS app with out CORS:

const categorical = require('categorical');
const app = categorical()

app.get('/customers', serve as (req, res, subsequent) 
  res.json(msg: 'consumer get')
);

app.publish('/customers', serve as (req, res, subsequent) 
    res.json(msg: 'consumer create')
);

app.put('/customers', serve as (req, res, subsequent) 
    res.json(msg: 'User replace')
);

app.pay attention(80, serve as () 
  console.log('CORS-enabled internet server listening on port 80')
)

In the above instance, now we have enabled customers API endpoint for POST, PUT, GET strategies however now not the DELETE manner.

For simple enabling CORS within the ExpressJS app, you’ll set up the cors

npm set up cors

Access-Control-Allow-Origin

Enabling CORS for all area

app.use(cors(
    beginning: '*'
));

Enabling CORS for a unmarried area

app.use(cors(
    beginning: 'https://app.geekflare.com'
));

If you wish to have to enable CORS for beginning https://app.geekflare.com and https://lab.geekflare.com

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ]
));

Access-Control-Allow-Methods

For enabling CORS for all strategies, forget this selection within the CORS module within the ExpressJS. But for enabling particular strategies(GET, POST, PUT).

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    strategies: ['GET', 'PUT', 'POST']
));

Access-Control-Allow-Headers

Used to enable headers rather then defaults to ship with precise requests.

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    strategies: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token']
));

Access-Control-Allow-Credentials

Omit this in case you don’t need to inform the browser to enable credentials on request even on withCredentials is ready to true.

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    strategies: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true
));

Access-Control-Max-Age

To intimate the browser to cache the preflight reaction knowledge within the cache for a specified 2d. Omit this in case you don’t need to cache the reaction.

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    strategies: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600 
));

The cached preflight reaction might be to be had for 10 minutes within the browser.

Access-Control-Expose-Headers

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    strategies: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    uncoveredHeaders: ['Content-Range', 'X-Content-Range']
));

If we put the wildcard(*) in uncoveredHeaders, it received’t disclose the Authorization header. So now we have to disclose explicitly like under

app.use(cors(
    beginning: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    strategies: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    uncoveredHeaders: ['*', 'Authorization', ]
));

The above will disclose all headers and Authorization header too.

What is an HTTP cookie?

A cookie is a small piece of knowledge that the server will ship to the customer browser. On later requests, the browser will ship the entire cookies comparable to an analogous area on each and every request.

Cookie has its characteristic, which will also be outlined to make a cookie paintings another way as we’d like.

  • Name Name of the cookie.
  • worth: information of cookie respective to cookie-name
  • Domain:  cookies might be despatched handiest to the outlined area
  • Path: cookies despatched handiest after the outlined URL prefix trail. Suppose if now we have outlined our cookie trail like trail=’admin/’. Cookies now not despatched for the URL https://geekflare.com/expire/ however despatched with URL prefix https://geekflare.com/admin/
  • Max-Age/Expires(quantity in 2d): When must the cookie expires. A life-time of the cookie makes the cookie invalid after the required time.
  • HTTPOnly(Boolean): The backend server can entry that HTTPOnly cookie however now not the client-side script when true.
  • Secure(Boolean): Cookies handiest despatched over an SSL/TLS area when true.
  • identicalSite(string [Strict, Lax, None]): Used to allow/prohibit cookies despatched over on cross-site requests. To know extra information about cookies identicalSite see MDN. It accepts 3 choices Strict, Lax, None. Cookie protected worth set to true for the cookie configuration identicalSite=None.

Why HTTPOnly cookie for tokens?

Storing the entry token despatched from the server in client-side garage like native garage, listed DB, and cookie (HTTPOnly now not set to true) are extra inclined to XSS assault. Suppose if any one in all your pages is vulnerable to an XSS assault. Attackers might misuse consumer tokens saved within the browser.

HTTPOnly cookies are handiest set/get through server/backend however now not at the client-side.

Client-side script limited to entry that HTTPonly cookie. So HTTPOnly cookies aren’t inclined to XSS Attacks and are extra protected. Because it’s handiest obtainable through the server.

Enable HTTPOnly cookie in CORS enabled backend

Enabling Cookie in CORS wishes the under configuration within the software/server.

  • Set Access-Control-Allow-Credentials header to true.
  • Access-Control-Allow-Origin and Access-Control-Allow-Headers must now not be a wildcard(*).
  • Cookie identicalSite characteristic must be None.
  • For enabling identicalSite worth to none, set the protected worth to true: Enable backend with SSL/TLS certificates to paintings within the area call.

Let’s see an instance code that units an entry token in HTTPOnly cookie after checking the login credentials.

const categorical = require('categorical'); 
const app = categorical();
const cors = require('cors');

app.use(cors( 
  beginning: [ 
    'https://app.geekflare.com', 
    'https://lab.geekflare.com' 
  ], 
  strategies: ['GET', 'PUT', 'POST'], 
  allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], 
  credentials: true, 
  maxAge: 600, 
  uncoveredHeaders: ['*', 'Authorization' ] 
));

app.publish('/login', serve as (req, res, subsequent)  
  res.cookie('access_token', access_token, 
    expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //2d min hour days 12 months
    protected: true, // set to true in case your the usage of https or samesite is none
    httpOnly: true, // backend handiest
    identicalSite: 'none' // set to none for cross-request
  );

  res.json( msg: 'Login Successfully', access_token );
);

app.pay attention(80, serve as ()  
  console.log('CORS-enabled internet server listening on port 80') 
); 

You can configure CORS and HTTPOnly cookies through imposing the above 4 steps on your backend language and webserver.

You can observe this instructional for apache and Nginx for enabling CORS through following the above steps.

withCredentials for Cross-Origin request

Credentials(Cookie, Authorization) despatched with the same-origin request through default. For cross-origin, now we have to specify the withCredentials to true.

XMLHttpRequest API:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.geekflare.com/consumer', true);
xhr.withCredentials = true;
xhr.ship(null);

Fetch API:

fetch('http://api.geekflare.com/consumer', 
  credentials: 'come with'
);

JQuery Ajax:

$.ajax(
   url: 'http://api.geekflare.com/consumer',
   xhrFields: 
      withCredentials: true
   
);

Axios :

axios.defaults.withCredentials = true

Conclusion

I am hoping the above article is helping you know the way CORS works and allow CORS for cross-origin requests within the server. Why storing cookies in HTTPOnly is protected and the way withCredentials utilized in purchasers for cross-origin requests.

Related Articles

Leave a Reply

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

Back to top button