Cakephp 4 : CORS [workaround/solved]

Hi All,
I’m having trouble using Cakephp as a REST API server for an Angular application.

I’m using cakephp 4.1.5.

If i put this headers in bootstrap.php file the apis call responds without problems:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, PUT, PATCH, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: *');
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    exit(0);
}

But this isn’t a solution that i can manage in production.

So I’m trying with https://github.com/ozee31/cakephp-cors that can’t manage the OPTION and seem not working.

Reading the documentation https://book.cakephp.org/4/en/controllers/request-response.html#setting-cross-origin-request-headers-cors i can’t understand how to make this work for all the REST API application.

I’m stuck in the problem with a lot of confusion, can someone help me to find a way?

Hi all,
I have managed a simple solution taking as example the library of ozee31 .

I create a middleware with this particular piece of code:

if ($request->getHeader('Origin')) {
    $response = $response
        ->withHeader('Access-Control-Allow-Origin', $this->_allowOrigin($request))
        ->withHeader('Access-Control-Allow-Credentials', $this->_allowCredentials())
        ->withHeader('Access-Control-Max-Age', $this->_maxAge());

    if (strtoupper($request->getMethod()) === 'OPTIONS') {
        $response = $response
            ->withHeader('Access-Control-Expose-Headers', $this->_exposeHeaders())
            ->withHeader('Access-Control-Allow-Headers', $this->_allowHeaders($request))
            ->withHeader('Access-Control-Allow-Methods', $this->_allowMethods())
            ->withStatus(200,__('Say cheese!'));
    }

}

The solution is the last row in OPTIONS if: the plugin don’t send any reply with code 200 (OK) and any text so the browser don’t see the response as required.

I don’t know if this is THE solution, but for me it works.

Be free to suggest or update it if you have any idea.

I do want to point out that I hope you have the setup done right by adding this to your config:

'Cors' => [
    'AllowOrigin' => ['https://your.awesome.site', 'https://your.second.site'],
],

As opposed to just slapping in the dreaded *.
Not doing this may potentially lead your users at a pretty severe risk.

CORS Explanation

Disabling CORS means that if I want to attack people using your website (https://your.awesome.site) I can do so from my own website by adding some malicious JavaScript to it.
In this example, let’s assume you’re a bank and I am in need of some cash.

$.ajax({
  url: 'https://your.awesome.site/api/v1/transfer-money',
  method: 'POST',
  data: { from: '1234', to: '5678', amount: 50 }
});

Implying you have no further protections (like CSRF Tokens, requiring a user to enter his/her security code, a captcha etc.) this will just wire over 50 bucks (why just 50? most people have 50 bucks on their account so it’s less likely to be blocked due to insufficient funds… also 50 bucks will raise less suspicion) to my account just like that.

By using CORS (and in particular, only allowing domains you strictly own), the client will just step in before the actual request (it happens in 2 stages: pre-flight and flight, CORS check happens during pre-flight, actual data transfer in flight) and say: “hol up cowboy, we’re not gonna continue this request” and the user won’t lose 50 bucks (sad me).

Hi @EDWD ,@FinlayDaG33k i have created middleware and add follwoing code but not work my api call , the cors error throwing .

 public function addHeaders(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        if ($request->getHeader('Origin')) {
            $response = $response
                ->withHeader('Access-Control-Allow-Origin', $this->_allowOrigin($request))
                ->withHeader('Access-Control-Allow-Credentials', $this->_allowCredentials())
                ->withHeader('Access-Control-Max-Age', $this->_maxAge());

            if (strtoupper($request->getMethod()) === 'OPTIONS') {
                $response = $response
                    ->withHeader('Access-Control-Expose-Headers', $this->_exposeHeaders())
                    ->withHeader('Access-Control-Allow-Headers', $this->_allowHeaders($request))
                    ->withHeader('Access-Control-Allow-Methods', $this->_allowMethods());
            }

        }

        return $response;
    }