-1

As new Opera 65 came few days ago with address bar redesign, I have noticed an issues on my web page. While typing or copying an address into the bar, Opera sends requests to server, however, I am not able to capture the requests in PHP, as it seems, Fetch API is used under the hood.

Is there any way to deny or block the Fetch API requests in PHP 7 or Apache 2.4? In other words, block the requests on server side produced by Opera while typing / copying (PHP preferred)?

Particularly, I need to exclude GET requests providing an action with hash key in a query (test in the sample bellow). When the address is copied (from mail for e.g.) to the address bar, Opera sends the request "in the background", the request is executed, however after submitting the address by Enter, second request returns error, because of forbidden operation (hash key is not valid anymore).

From Apache log:

127.0.0.1 - - [29/Nov/2019:01:56:08 +0100] "GET /? HTTP/1.1" 200 179736
127.0.0.1 - - [29/Nov/2019:01:56:08 +0100] "GET /?t HTTP/1.1" 200 179813
127.0.0.1 - - [29/Nov/2019:01:56:08 +0100] "GET /?te HTTP/1.1" 200 179808
127.0.0.1 - - [29/Nov/2019:01:56:08 +0100] "GET /?tes HTTP/1.1" 200 179819
127.0.0.1 - - [29/Nov/2019:01:56:08 +0100] "GET /?test HTTP/1.1" 200 179823

From Wireshark (one of the requests):

/?test HTTP/1.1
Host: sk.localhost
Connection: keep-alive
Sec-Fetch-Site: none
Sec-Fetch-Mode: no-cors
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36 OPR/65.0.3467.48
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

Used technologies: PHP 7.3.7, Apache/2.4.39

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Dom
  • 532
  • 1
  • 9
  • 23
  • Wow. Are you certain you're not leaning on your 'enter' key? If Opera doesn't consider this behaviour to be a bug I think I'll be blocking Opera useragents. Consider my _flabbers_ to be thoroughly _ghasted_. – Sammitch Nov 29 '19 at 01:37
  • There's also the angle that `GET` requests by definition are supposed to be idempotent. If the request is changing state it should be a `POST`, `PUT`, `PATCH` or what-have-you. – Sammitch Nov 29 '19 at 01:41
  • I am pretty sure. Requests are sent each time you type a letter in the query. And it does not change anything about my requirement - to deny this type of requests. – Dom Nov 29 '19 at 01:44
  • You’re not making much sense here. A request is a request is a request - whether it was made using Fetch, or anything else, should be highly irrelevant to how you basically handle it on the server side. _“I am not able to capture the requests in PHP”_ - what have you actually tried then? Since the path is just `/` here, you’d have to handle this in whichever script is responsible for handling your website “root” document then. Could simply be the index.php in the main folder, could be something else, if you are using an actual router of some sorts … – 04FS Nov 29 '19 at 09:18
  • I don’t think this something Opera implemented intentionally - guess it is either a bug, or something is misconfigured. Sending requests like this based on what the user is typing would make sense to get search result previews, many sites /services (Google, WikiPedia, …) provide specific URLs to request that kind of data for a “type-ahead prediction”. – 04FS Nov 29 '19 at 09:22
  • @04FS You are no no right. I can distinguish AJAX requests. Than I want to distingush *fetch()*... In debug mode I was not able to break at the begining of index. It seemed like the PHP was not involved at all... however, the reponse was full page. I do not see the request in Opera developer window as well. The only way to capture the requests was Wireshark and Apache log... I will try to log the first line on index.php. – Dom Nov 29 '19 at 10:58
  • _“It seemed like the PHP was not involved at all... however, the response was full page”_ - very unlikely to happen, if PHP was _not_ involved, right? Depending on how exactly you are _triggering_ your debugger, this particular request might simple not send the necessary headers, cookies or whatever. – 04FS Nov 29 '19 at 11:52
  • You are right, the debugger was not triggered. Now I have a solution... – Dom Nov 30 '19 at 15:37

1 Answers1

0

The requests can be denied (response sent) by Apache rewrite conditions or PHP response based on parsed headers.

Opera sends two Fetch API headers while typing:

Sec-Fetch-Site: none
Sec-Fetch-Mode: no-cors

List of all headers can be found at https://w3c.github.io/webappsec-fetch-metadata/.

To not sent a full response (not engage PHP) for requests with this headers, you can use Apache 2.4 mod rewrite module:

RewriteCond %{HTTP:Sec-Fetch-Site} ^none$ [NC]
RewriteCond %{HTTP:Sec-Fetch-Mode} ^no-cors$ [NC]
RewriteRule ^ - [R=204,L]

or send response via PHP (sample using Kohana/Koseven FW):

    $header_sec_fetch_site = $this->request->headers('Sec-Fetch-Site');
    $header_sec_fetch_mode = $this->request->headers('Sec-Fetch-Mode');
    if (isset($header_sec_fetch_site, $header_sec_fetch_mode)
            && $header_sec_fetch_site == 'none'
            && $header_sec_fetch_mode == 'no-cors')
    {
        $message = 'Header ' . $header_sec_fetch_mode . ' received. No content for this request.';
        Log::instance()->add(Log::NOTICE, $message);
        throw HTTP_Exception::factory(204, $message, array(
                ':uri' => Request::current()->uri(),
            ));
    }

The headers should be available in global variables:

$_SERVER['HTTP_SEC_FETCH_DEST']
$_SERVER['HTTP_SEC_FETCH_SITE']
$_SERVER['HTTP_SEC_FETCH_USER']
$_SERVER['HTTP_SEC_FETCH_MODE']

Dom
  • 532
  • 1
  • 9
  • 23