Joomla.it Forum

Non solo Joomla... => Sviluppo => Esperimenti e test => : danielecr 19 Jul 2018, 16:51:31

: [RISOLTO] Pagine AMP - amp-consent e checkConsentHref endpoint
: danielecr 19 Jul 2018, 16:51:31
Ciao a tutti,
scrivo in questa sezione perchè l'argomento non centra una mazza con joomla, ma può magari diventare utile per qualcuno.
Ho implementato in parallelo al sito in joomla delle pagine AMP: per bloccare alcuni elementi delle pagine previo consenso del visitatore (come cookies analitici) ho utilizzato il recente "amp-consent" ( https://www.ampproject.org/docs/reference/components/amp-consent (https://www.ampproject.org/docs/reference/components/amp-consent) ).
"amp-consent" necessita di un endpoint per funzionare a dovere, per verificare lo stato del consenso dato dal visitatore.
Tuttora, e quando ho implementato le pagine AMP, non avevo trovato esempi concreti a riguardo, o comunque non chiarissimi (almeno per me) e ho cercato di seguire le indicazioni date su ampproject.org - https://www.ampproject.org/docs/fundamentals/amp-cors-requests (https://www.ampproject.org/docs/fundamentals/amp-cors-requests)

In particolare, seguendo la documentazione, si dice:
If `Origin` header is set:

1. If the origin was not `*.ampproject.org`, `*.amp.cloudflare.com` or the publisher's (aka your) origin, stop and return an error response.
2. Check the `__amp_source_origin` query parameter. If it's not the publisher's origin stop and return an error response.
3. If both checks pass, proceed to process the request.

Otherwise, if `Origin` header is NOT set:

1. Check if the request has `AMP-Same-Origin: true` header. If not, stop and return an error response.
    * This custom request header is sent by AMP runtime when making an XHR request on sameorigin (document served from non-cache URL).
2. Otherwise proceed to process the request.

It's very important that these all are done first before processing the request, this provides protection against CSRF attacks and avoids processing untrusted sources requests.

Si tratta quindi di effettuare una richiesta CORS di tipo POST a un endpoint, che ho scritto in php.

Premettendo che il dominio è indicato come mio-dominio.com, che ho un certificato https attivo e che ho i redirezionamenti da non-www a www e da http a https --> qui di seguito il codice dell'endpoint che ho scritto:

:
<?php
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
   if (isset(
$_SERVER['HTTP_ORIGIN'])) {
      
$http_origin $_SERVER['HTTP_ORIGIN'];
      
$amp_origin $_REQUEST['__amp_source_origin'];
      if ((
$http_origin == "https://www-mio--dominio-com.cdn.ampproject.org" || $http_origin == "https://mio--dominio-com.cdn.ampproject.org" || $http_origin == "https://cdn.ampproject.org" || $http_origin == "https://www-mio--dominio-com.amp.cloudflare.com" || $http_origin == "https://mio--dominio-com.amp.cloudflare.com" || $http_origin == "https://www.mio-dominio.com" || $http_origin == "https://mio-dominio.com") && ($amp_origin == "https://www.mio-dominio.com" || $amp_origin == "https://mio-dominio.com")) {
         
header("access-control-allow-credentials:true");
         
header("access-control-allow-headers:Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token");
         
header("access-control-allow-methods:POST");
         
header("access-control-allow-origin:".$_SERVER['HTTP_ORIGIN']);
         
header("access-control-expose-headers:AMP-Access-Control-Allow-Source-Origin");
         
header("amp-access-control-allow-source-origin:".$_REQUEST['__amp_source_origin']);
         
header("Content-Type: application/json");
         
$output = new \stdClass();
             
$output->promptIfUnknown true;
             echo 
json_encode($output);
      }
      else {
         echo(
"something went wrong");
      }
   }
   else {
      
$ampsameoriginvalue "";
      foreach (
getallheaders() as $name => $value) {
         if (
strtoupper($name) == "AMP-SAME-ORIGIN") {
            
$ampsameoriginvalue $value;
         }
      }
      if (
$ampsameoriginvalue == "true") {
         
$amp_origin $_REQUEST['__amp_source_origin'];
         if (
$amp_origin == "https://www.mio-dominio.com" || $amp_origin == "https://mio-dominio.com") {
            
header("access-control-allow-credentials:true");
            
header("access-control-allow-headers:Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token");
            
header("access-control-allow-methods:POST");
            
header("access-control-allow-origin:https://www.mio-dominio.com");
            
header("access-control-expose-headers:AMP-Access-Control-Allow-Source-Origin");
            
header("amp-access-control-allow-source-origin:".$_REQUEST['__amp_source_origin']);
            
header("Content-Type: application/json");
            
$output = new \stdClass();
                
$output->promptIfUnknown true;
                echo 
json_encode($output);
         }
         else {
            echo(
"something went wrong");
         }
      }
      else {
         echo(
"something went wrong");
      }
   }
}
else {
   echo 
"post only";
}
?>


Quello che fa è:
1- verifica che la richiesta sia POST, altrimenti dai un messaggio di errore
2- verifica se l'header Origin è presente
3- se l'header Origin è presente: se il valore dell'header Origin è in whitelist e se amp origin è in whitelist, imposta gli header di risposta e procedi alla richiesta CORS, altrimenti dai un messaggio di errore
4- se l'header Origin non è presente verifica se negli header di richiesta esiste Amp-Same-Origin: true
5- se Amp-Same-Origin: true è presente imposta gli header e procedi alla richiesta CORS, altrimenti dai un messaggio di errore
6- se l'header Amp-Same-Origin: true non esiste dai un messaggio di errore

Ora, mi sembra che funzioni tutto correttamente, sia nella cache AMP, sia aprendo le pagine AMP direttamente su mio-dominio.com, ma visto che è la prima volta che mi approccio a CORS, magari mi è sfuggito qualcosa, magari a livello di sicurezza.

Anche perchè fino ad oggi, tutte le volte che ho aperto questo file l'ho modificato.... ::)

Magari se qualcuno ha voglia scorrendo il codice può capire se ho seguito correttamente il flusso delle istruzioni, o se ho fatto qualche cazzata...

EDIT: ops, mi sono accorto che https://www.ampproject.org/docs/fundamentals/amp-cors-requests (https://www.ampproject.org/docs/fundamentals/amp-cors-requests) è stato aggiornato e ora c'è un esempio in javascript.
: Re:Pagine AMP - amp-consent e checkConsentHref endpoint
: Alex21 19 Jul 2018, 22:53:25
EDIT: ops, mi sono accorto che https://www.ampproject.org/docs/fundamentals/amp-cors-requests (https://www.ampproject.org/docs/fundamentals/amp-cors-requests) è stato aggiornato e ora c'è un esempio in javascript.
Sì, certo è una convalida di richiesta ajax ( xhr ), fac-simile alla tua, lato server per un server che monta node.js.
Una cosa mi stavo chiedendo leggendo il tuo codice. Se la white list è bella lunga, mi sa che ti occorre un sistema più efficiente di una sequenza di
or ( || ) per testarla.
Ciao!
: Re:Pagine AMP - amp-consent e checkConsentHref endpoint
: danielecr 21 Jul 2018, 09:37:34
Grazie del commento Alex, la whitelist non dovrebbe essere più lunga di quella scritta, ovvero comprende tutti gli indirizzi di cache e il proprio dominio (entrambi in versione www e non www).In effetti è un po' al limite..

Sarebbe più efficiente creare due array e fare il check su questi secondo te?

:
$whitelist_origin = array('https://www-my--domain-com.cdn.ampproject.org', 'https://my--domain-com.cdn.ampproject.org', 'https://cdn.ampproject.org', 'https://www-my--domain-com.amp.cloudflare.com', 'https://my--domain-com.amp.cloudflare.com', 'https://www.my-domain.com', 'https://my-domain.com');

$whitelist_amp_origin = array('https://www.my-domain.com', 'https://my-domain.com');
 
if((in_array($http_origin, $whitelist_origin)) && (in_array($amp_origin, $whitelist_amp_origin))) {
...
}
: Re:Pagine AMP - amp-consent e checkConsentHref endpoint
: Alex21 21 Jul 2018, 11:26:45
Sarebbe più efficiente creare due array e fare il check su questi secondo te?
Secondo me, sì.
Adesso so di rompere le scatole, ma in_array() è case sensitive, vuol dire che se qualche buontempone ti manda una stringa così, per esempio: 'https://Www.my-domain.com'  diventa un falso positivo, dovrebbe passare e non lo fa. La cosa ( purtroppo ) costringe ad una certa manipolazione preventiva della stringa, previo filtraggio della medesima.
Ciao!
: Re:Pagine AMP - amp-consent e checkConsentHref endpoint
: danielecr 22 Jul 2018, 09:52:54
Grazie,per maiuscole/minuscole sui domini non mi preoccuperei, perchè le richieste sono interne, e sono sempre tutte in minuscolo, quindi non c'è motivo di alterarla con lettere maiuscole (e se fosse alterata di proposito allora per me sarebbe giusto un errore 401). Visto anche l'esempio, per me è risolto, metto qui il codice definitivo, ho ancora cambiato gli errori da echo a 401, e inserito il check sui vettori.
:
<?php
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
    
$whitelist_origin = array('https://www-my--domain-com.cdn.ampproject.org''https://my--domain-com.cdn.ampproject.org''https://cdn.ampproject.org''https://www-my--domain-com.amp.cloudflare.com''https://my--domain-com.amp.cloudflare.com''https://www.my-domain.com''https://my-domain.com');
    
$whitelist_amp_origin = array('https://www.my-domain.com''https://my-domain.com');
    if (isset(
$_SERVER['HTTP_ORIGIN'])) {
        
$http_origin $_SERVER['HTTP_ORIGIN'];
        
$amp_origin $_REQUEST['__amp_source_origin'];
        if((
in_array($http_origin$whitelist_origin)) && (in_array($amp_origin$whitelist_amp_origin))) {
            
header("access-control-allow-credentials:true");
            
header("access-control-allow-headers:Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token");
            
header("access-control-allow-methods:POST");
            
header("access-control-allow-origin:".$_SERVER['HTTP_ORIGIN']);
            
header("access-control-expose-headers:AMP-Access-Control-Allow-Source-Origin");
            
header("amp-access-control-allow-source-origin:".$_REQUEST['__amp_source_origin']);
            
header("Content-Type: application/json");
            
$output = new \stdClass();
            
$output->promptIfUnknown true;
            echo 
json_encode($output);
        }
        else {
            
header("HTTP/1.1 401 Unauthorized");
            echo 
"You are not authorized";
            exit;
        }
    }
    else {
        
$ampsameoriginvalue "";
        foreach (
getallheaders() as $name => $value) {
            if (
strtoupper($name) == "AMP-SAME-ORIGIN") {
                
$ampsameoriginvalue $value;
            }
        }
        if (
$ampsameoriginvalue == "true") {
            
$amp_origin $_REQUEST['__amp_source_origin'];
            if(
in_array($amp_origin$whitelist_amp_origin)) {
                
header("access-control-allow-credentials:true");
                
header("access-control-allow-headers:Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token");
                
header("access-control-allow-methods:POST");
                
header("access-control-allow-origin:https://www.my-domain.com");
                
header("access-control-expose-headers:AMP-Access-Control-Allow-Source-Origin");
                
header("amp-access-control-allow-source-origin:".$_REQUEST['__amp_source_origin']);
                
header("Content-Type: application/json");
                
$output = new \stdClass();
                
$output->promptIfUnknown true;
                echo 
json_encode($output);
            }
            else {
                
header("HTTP/1.1 401 Unauthorized");
                echo 
"You are not authorized";
                exit;
            }
        }
        else {
            
header("HTTP/1.1 401 Unauthorized");
            echo 
"You are not authorized";
            exit;
        }
    }
}
else {
    
header("HTTP/1.1 401 Unauthorized");
    echo 
"You are not authorized";
    exit;
}
?>