Category: PHP

What is CORS?

Cross-Origin Resource Sharing (CORS) is a security mechanism that is based on HTTP headers. The idea is to restrict resources to be loaded or interacted with from the same domain or from all.

When you have an API that maybe called via ajax request from one of your domains you can either use the following which is not very secure because it can be called by ANY site. This is very convenient because it will always allow requests but it's unsecure.

header('Access-Control-Allow-Origin: *');

Is there a better way? Yes, of course.

How to send CORS response headers in PHP more securely?

As always it comes down to validation and sanitization of the received data.

Each valid ajax request must send the Origin request header along with the rest of the request. PHP (or Apache) makes that available as $_SERVER['HTTP_ORIGIN'] variable.

The code below checks several fields until it finds one. The default CORS is localhost so if the request is not correct it will just send a not very useful CORS header.

Then the code sanitizes the value and finally checks it if it ends in known sites i.e. your domains.

You need to put your sites in the regular expression below (see variable $allowed_domains_regex) and separate them with a comma. The Origin header contains the site without any path or variables attached to it. That's why we're checking if it ends in a specific domain. The very end is optional as it should match lots of domains.

Then just use this function in your code.

Usage

Orbisius_CORS_Util::sendCors();

Use the following code in a php file and load it.

<?php

/**
 * To request a customization or hire us contact us at - https://orbisius.com/contact
 * @see Blog post https://orbisius.com/7146
 * @author Svetoslav (Slavi) Marinov
 * @copyright (c) 2022-3000 All Rights Reserved.
 * @license MIT
 */
class Orbisius_CORS_Util {
    /**
     * Orbisius_CORS_Util::sendCors();
     * @return void
     */
    public static function sendCors() {
        if (headers_sent()) {
            return;
        }

        $inp_origin = '';
        $cors_origin = 'localhost'; // * is not secure
        $allowed_domains_regex = '#(YOUR_SITES|SEPARATED_BY_PIPE_BECAUSE_OF_REGEX)(\.[a-z]{0,2})?$#si';

        // Allow ajax requests from my domains only
        if (!empty($_SERVER['HTTP_ORIGIN'])) {
            $inp_origin = $_SERVER['HTTP_ORIGIN'];
        } elseif (!empty($_SERVER['HTTP_HOST'])) {
            $inp_origin = $_SERVER['HTTP_HOST'];
        } elseif (!empty($_SERVER['SERVER_NAME'])) {
            $inp_origin = $_SERVER['HTTP_HOST'];
        }

        $inp_origin = strip_tags($inp_origin);
        $inp_origin = trim($inp_origin);

        if (!empty($inp_origin) && preg_match($allowed_domains_regex, $inp_origin)) {
            $cors_origin = $inp_origin;
        }

        header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Credentials: true');
        header("Access-Control-Allow-Headers: Origin,Content-Type,Authorization,Cache-Control,X-Requested-With,X-Auth-Token,X-XSRF-TOKEN");
        header('Access-Control-Allow-Origin: ' . $cors_origin); // safe? smart? to allow access from anywhere?
    }
}

Using WordPress

If you're using WordPress you can use send_origin_headers() function to send the proper CORS headers.

Do you see how this can be improved in any way?

image credit: Scott Webb (unsplash) id: yekGLpc3vro

Join our mailing list

Get important news about cool products we release and invitations to beta test new products

Find out the Social networks we're on

Black and Cyber Monday Deal: Use blackfriday22 code at checkout for 40% off
Static file optimization by StaticOptimizer