-
JAVASCRIPT, PHP > requêtes AJAX, CORS, fetch
- On va devoir passer la valeur true à la propriété withCredentials de notre objet XMLHttpRequest ;
- Le serveur doit renvoyer un en-tête contenant Access-Control-Allow-Origin: * qui signifie que la ressource demandée est accessible depuis n’importe quel domaine.
- La méthode text() retourne la réponse sous forme de chaine de caractères ;
- La méthode json() retourne la réponse en tant qu’objet JSON ;
- La méthode formData() retourne la réponse en tant qu’objet FormData ;
- La méthode arrayBuffer() retourne la réponse en tant qu’objet ArrayBuffer ;
- La méthode blob() retourne la réponse en tant qu’objet Blob ;
- <Server key>: The server key => Use {{ server_key }} using symfony implementation in Twig template or <?php echo $_SESSION['skey']; ?> using PHP implementation.
- <Activation state>: The activation state (0 or 1).
- <Action when the request successes>: Action to do when the request successes.
- none: défaut.
- request: guard pour l’en-tête obtenu d’une requête (Request.headers (en-US)).
- request-no-cors: guard pour l’en-tête obtenu d’une requête créé avec Request.mode no-cors.
- response: guard pour l’en-tête obtenu d’une réponse (Response.headers (en-US)).
- immutable: majoritairement utilisé pour les ServiceWorkers; retourne un objet en-tête en lecture seule.
- Response.status (en-US) —Un entier (valeur par défaut 200) contenant le code de statut de la réponse.
- Response.statusText (en-US) — Une chaine de caractères (valeur par défaut "OK"), qui correspond au message du statut HTTP.
- Response.ok —vu précedemment, c’est un raccourci pour vérifier que le code de statut et bien compris entre 200 et 299 inclus. Retourne un Boolean (en-US).
- ArrayBuffer
- ArrayBufferView (en-US) (Uint8Array et ses proches)
- Blob/Fichier
- chaine de caractères
- URLSearchParams
- FormData
- SSL Cert/HTTPS running on the domain.
- Certain AJAX request will only work if the PHP Session is valid across the site (used on both login.php and api.php in this example).
- Rate limiting on api.php when accessing functions.
- PHP PDO prepared statements when interacting with the database inside api.php.
- Encrypts sensitive data inside api.php (not relevant to the question).
CROSS-DOMAIN - CORS
Consiste à ajouter des en-têtes HTTP afin de permettre à un agent utilisateur d’accéder à des ressources d’un serveur situé sur une autre origine que le site courant. Pour une requête de type GET, il va falloir réunir deux conditions :
# Wide-open CORS config for nginx location / { if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # Custom headers and headers various browsers *should* be OK with but aren't add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # Tell client that this pre-flight info is valid for 20 days add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } }
Source: Michiel Kalkman, https://michielkalkman.com/snippets/nginx-cors-open-configuration.html
Crée une première requête Ajax
let xhr = new XMLHttpRequest(); xhr.open("get", "adresse ou url") xhr.responseType = "json" xhr.send()
FETCH
fetch('/data.json') .then(response => response.json()) .then(data => { console.log(data) }) .catch(err => ...)
Réponse
fetch('/data.json') .then(res => { res.text() // response body (=> Promise) res.json() // parse via JSON (=> Promise) res.status //=> 200 res.statusText //=> 'OK' res.redirected //=> false res.ok //=> true res.url //=> 'http://site.com/data.json' res.type //=> 'basic' // ('cors' 'default' 'error' // 'opaque' 'opaqueredirect') res.headers.get('Content-Type') })
Request options
fetch('/data.json', { method: 'post', body: new FormData(form), // post body body: JSON.stringify(...), headers: { 'Accept': 'application/json' }, credentials: 'same-origin', // send cookies credentials: 'include', // send cookies, even in CORS })
Catching errors
fetch('/data.json') .then(checkStatus)
function checkStatus (res) { if (res.status >= 200 && res.status < 300) { return res } else { let err = new Error(res.statusText) err.response = res throw err } }
Using with node.js
const fetch = require('isomorphic-fetch')
See: isomorphic-fetch (npmjs.com)
References
Request et Response représentent la requête et la réponse. Headers représente les en-têtes de requête et de réponse tandis que le Body fournit un ensemble de méthodes nous permettant de gérer le corps de la requête et de la réponse.
On va pouvoir lui passer en argument facultatif un liste d’options sous forme d’objet littéral pour préciser la méthode d’envoi, les en-têtes, etc.
La promesse va être résolue dès que le serveur renvoie les en-têtes HTTP, avant même qu’on ait le corps de la réponse.
La promesse sera rompue si la requête HTTP n’a pas pu être effectuée. En revanche, les renvois 404 ou 500 sont considérées comme normales et ne pas empêcher la promesse d’être tenue.
On va donc devoir vérifier le statut HTTP de la réponse. Pour cela, on va pouvoir utiliser les propriétés ok et status de l’objet Response renvoyé.
La propriété ok contient un booléen : true si le statut code HTTP de la réponse est compris entre 200 et 299, false sinon.
La propriété status va renvoyer le statut code HTTP de la réponse (la valeur numérique liée à ce statut comme 200, 301, 404 ou 500).
Pour récupérer le corps de la réponse, nous allons pouvoir utiliser les méthodes de l’interface Response en fonction du format qui nous intéresse :
Expliquons ce code ensemble. Tout d’abord, la méthode fetch() a besoin d’un argument obligatoire, qui correspond à l’URL des ressources à récupérer. On utilise ici une/url (remplacez bien évidemment par une vraie URL en pratique).
fetch() retourne ensuite une promesse contenant la réponse (si tout se passe bien). On ne peut pas exploiter la réponse renvoyée dans cette promesse en l’état : il faut indiquer le format de réponse souhaité. Ici, on choisit JSON avec response.json().
response.json() renvoie également une promesse contenant la réponse à votre demande en JSON. On utilise JSON.stringify() pour transformer notre objet JSON en une chaine JSON et on affiche cette chaine.
Finalement, on traite les erreurs avec le bloccatch et on affiche l’erreur rencontrée si on en rencontre effectivement une.
Passer des options à fetch()
method: méthode utilisée par la requête. Les valeurs possibles sont GET (défaut), POST, etc.) ;headers: les en-têtes qu’on souhaite ajouter à notre requête ;body: un corps qu’on souhaite ajouter à notre requête ;referrer: un référant. Les valeurs possibles sont "about:client" (valeur par défaut), "" pour une absence de référant, ou une URL ;referrerPolicy: spécifie la valeur de l’en-tête HTTP du référent. Les valeurs possibles sont no-referrer-when-downgrade (défaut), no-referrer, origin, origin-when-cross-origin et unsafe-url ;mode: spécifie le mode qu’on souhaite utiliser pour la requête. Les valeurs possibles sont cors (défaut), no-cors et same-origin ;credentials: les informations d’identification qu’on souhaite utiliser pour la demande. Les valeurs possibles sont same-origin (défaut), omit et include ;cache: le mode de cache qu’on souhaite utiliser pour la requête. Les valeurs possibles sont default (défaut), no-store, reload, no-cache, force-cache et only-if-cached ;redirect: le mode de redirection à utiliser. Valeurs possibles : follow (défaut), manual, error ;integrity: contient la valeur d’intégrité de la sous-ressource de la demande. Valeurs possibles : "" (défaut) ou un hash ;keepalive: permet à une requête de survivre à la page. Valeurs possibles : false (défaut) et true ;signal: une instance d‘un objet AbortSignal qui nous permet de communiquer avec une requête fetch() et de l’abandonner.CROSS-DOMAIN
var BrowserWindow = require('browser-window') var win = new BrowserWindow({ webPreferences: { webSecurity: false } })
--
AJAX: HOW SECURE AJAX CALLS WITH JQUERY?
When you integrate AJAX technology in your web application, you are exposed to security problem on call requests. Here is a simple solution to secure your calls…
IMPLEMENTATION
1. Server side: Generate random key
The aim is to generate a random key and save it in session then use it during the build of javascript function.
Using Symfony implementation
// AcmeBundle/Controller/PluginController.php // Controller Action public function indexAction(Request $request) { $session = $request->getSession(); if (!($session->isStarted())) { $session->start(); } $server_key = $session->get('skey'); if(empty($server_key)){ $server_key = $this->randomString(); $session->set('skey', $server_key); } return $this->render('AcmeBundle:Plugin:index.html.twig', array( 'server_key' => $server_key // Pass random key to twig template )); } // Random string generator private function randomString($length = 10){ $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $charactersLength = strlen($characters); $randomString = ""; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString; }
Using PHP implementation
session_start(); // Random string generator function randomString($length = 10){ $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $charactersLength = strlen($characters); $randomString = ""; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString; } if(!isset($_SESSION['skey'])){ $_SESSION['skey'] = randomString(); }
2. Client side: Build javascript function
A POST request is created with parameters active which enable the plugin.
$.ajax({ url: "http://www.myapp.ext/ajax/myplugin?skey=<Server key>", method: 'POST', data: {active: <Activation state> } }).done(function () { //<Action when the request successes> });
where:
3. Server side: Treatment of request
When the HTTP resource is called, a check with the server key is done.
Using Symfony implementation
// AcmeBundle/Controller/PluginController.php // Controller Action public function activationAction(Request $request) { if($request->isMethod('POST')) { $session = $request->getSession(); if (!($session->isStarted())) { $session->start(); } $server_key = $session->get('skey'); $client_key = $request->query->get('skey'); // >>>> Security check if(empty($server_key) || empty($client_key) || ($server_key != $client_key)) { return new Response("Unauthorized", Response::HTTP_UNAUTHORIZED); } //Treatment to do to (de)activate plugin return new Response("Accepted", Response::HTTP_ACCEPTED); } return new Response("Bad Request", Response::HTTP_BAD_REQUEST); }
Using PHP implementation
session_start(); // >>>> Security check if(empty($_SESSION['skey']) || empty($_POST['skey']) || ($_SESSION['skey'] != $_POST['skey'])) { //Treatment when a bad user do the request } else { //Treatment to do to (de)activate plugin }
CONCLUSION
Here is a simple solution to secure your AJAX calls against unexpected request.
You can adapt this strategy to other technologies if you want.
Other solutions exist (more complex and more safe) but this one is sufficient against main attacks.
See you in the next article…
—
How to make secure AJAX call
AJAX is a modern way to building web application where most of the code runs at client side for better performance and user experience. So how can we make secure AJAX call to protect from CSRF (Cross-site request forgery) attack?
Introduction
In modern era of web development, developers are more focused on high performance for better user experience. AJAX is one of the modern way of building web application where most of the code runs at client side and post only required data on server. Now few questions come in mind as follows:
1. Is AJAX post call is a standard practice?
2. Is AJAX call is secure? Or how we can make secure AJAX call?
3. Which one is better HTTP Post (form submit, a traditional way) or AJAX POST (a modern way)?
Background
Post back is traditional way to doing things on web application where whole page postback on form submission. In this approach most of the codes runs at sever side. AJAX is a modern way to building web application where most of the code runs at client side for better performance and user experience. In AJAX, only required data post to server instead of posting whole page.
Post back & Ajax both create HTTP request so it is not right to say one is less secure than other. In both requests attacker can inject script using CSRF (Cross-site request forgery). AJAX calls are itself protect CSRF using “Common Origin Policy” when CORS is disabled and JSONP requests are blocked.
To prevent CSRF attack one step ahead, we can implement Anti Forgery token similar to MVC framework. AJAX calls can be called from web application as well as from MVC.
In MVC, @html.antiforgerytoken() can be called on form load which store one key in hidden field and other key in cookie and using ValidateAntiForgeryToken filter, we can validate anti forgery token. The form token can be a problem for AJAX requests, because an AJAX request can send JSON data, not HTML form data. One solution is to send the tokens in a custom HTTP header.
Using the code
Sample Server side Code to generate Anti forgery token.
C#
Copy Code
<code> /// <summary> /// Get AntiForgery token /// </summary> /// <returns>one token into secure & HTTP only cookie & other hidden fields.</returns> public static string GetAntiXsrfToken() { string cookieToken, formToken; Antiery.GetTokens(null, out cookieToken, out formToken); var responseCookie = new HttpCookie("__AJAXAntiXsrfToken") { HttpOnly = true, Value = cookieToken }; if(FormsAuthentication.RequireSSL && HttpContext.Current.Request.IsSecureConnecti on) { responseCookie.Secure = true; } HttpContext.Current.Response.Cookies.Set(responseCookie); return formToken; } </code>
Sample Server side Code to validate Anti forgery token.
C#
Shrink ▲ Copy Code
<code>/// <summary> /// Validate Anti Forgery token coming from secure cookie & request header /// </summary> static void ValidateAntiXsrfToken() { string tokenHeader, tokenCookie; try { // get header token tokenHeader = HttpContext.Current.Request.Headers.Get("__RequestVerificationToken"); // get cookie token var requestCookie = HttpContext.Current.Request.Cookies["__AntiXsrfToken"]; tokenCookie = requestCookie.Value; AntiForgery.Validate(tokenCookie, tokenHeader); } catch { HttpContext.Current.Response.Clear(); HttpContext.Current.Response.StatusCode = 403; HttpContext.Current.Response.End(); } } </code>
Sample code to get Anti forgery token (one part) and save into hidden field.
HTML
Copy Code
<code><input name="__RequestVerificationToken" type="hidden" value="<%= CommonUtils.GetAntiXsrfToken() %>" /></code>
Sample client side code to pass one part to Anti Forgery token into request header from hidden field and another part will go automatically from client cookie if request is generated from same origin.
JavaScript
Shrink ▲ Copy Code
<code>function CallServer(baseUrl, methodName, MethodArgument, callback) { $.ajax({ type: "POST", url: baseUrl + methodName, data: MethodArgument, contentType: "application/json; charset=utf-8", async: false, dataType: "json", headers: {'__RequestVerificationToken': $("input[name='__RequestVerificationToken']").val() }, success: function (data) { if (callback != undefined && typeof (callback) === "function") { callback(data.d); } }, error: function (data) { if (data.status == 401 || data.status == 403) window.location.href = "../Common/accessdenied"; else if (data.status == 419) { displayUserMessage(commonMessage.RE_SESSIONINFO_NOT_FOUND, true); window.location.href = "../Common/logout"; } else displayUserMessage(commonMessage.SERVICE_NOT_RESPONDING, true); } }); }</code>
Finally, Call ValidateAntiXsrfToken() function before processing the each AJAX request at server side.
Reference
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet
This article was originally posted at http://stackoverflow.com/questions/39199129/is-ajax-post-an-acceptable-technique-for-changing-server-state/39205336#39205336
—
5 Tips to Secure AJAX PHP Call
Now a days, we always use Ajax for getting data without refreshing a page in our project. Ajax is a good approach to load data smoothly on page without reloading.
Simply Getting ajax request response is not secure way. So, in this tutorial we will see how to secure ajax request or how to make secure Ajax call
Step 1. First of all check request is AJAX request or not.
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { echo "AJAX request"; } else{ echo "Not AJAX"; }
Step 2. Check Referer : Give response only when AJAX request is from your own url
if(!empty($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER']=="www.mywebiste.com/ajaxurl") { //AJAX Request from own server }
Step 3. Use Post Method in AJAX
Always Use Post method while sending request and check $_POST variable before giving response
Step 4. Use Token While Sending Ajax Call
session_start(); $token = md5(rand(10000,99999)); $_SESSION['token'] = $token; //store it as session variable
Step 5. Use Two way encryption
Send encrypt data and before giving result check request with decrypt.
Learn how to use two way encryption in PHP.
Simple Two Way Encryption in PHP
fetch
Détection de la fonctionnalité
Le support de l’API Fetch peut être détecté en vérifiant l’existence de Headers, Request, Response ou fetch() sur la portée de Window ou de Worker. Par exemple, vous pouvez faire cela dans votre script :
if (window.fetch) { // exécuter ma requête fetch ici } else { // Faire quelque chose avec XMLHttpRequest? }
Créer une requête fetch
Une requête fetch basique est vraiment simple à initier. Jetez un coup d’œil au code suivant :
const myImage = document.querySelector('img'); fetch('flowers.jpg') .then(function(response) { return response.blob(); }) .then(function(myBlob) { const objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; });
Ici nous récupérons une image à travers le réseau et l’insérons dans un élément <img>. L’utilisation la plus simple de fetch() prend un argument — le chemin de la ressource que nous souhaitons récupérer — et retourne une promesse (promise) contenant, en réponse, un objet (de type Response).
Bien sûr, il s’agit seulement d’une réponse HTTP, pas exactement de l’image. Pour extraire le contenu de l’image de la réponse, nous utilisons la méthode blob() (en-US) (définie sur le mixin Body, qui est implémenté autant par les objets Request que par les objets Response).
Note : Le mixin Body a aussi des méthodes similaires pour extraire d’autres types contenu ; pour plus d’informations regardez la section Corps.
Un objet objectURL est ensuite créé à partir du Blob extrait, puis est inseré dans img.
Les requêtes Fetch sont controllées par la directive connect-src du Content Security Policy (en-US) plutôt que par la directive de la ressource dont il s’agit de la récupération.
Fournir des options à la requête
La méthode fetch() accepte un second paramètre, optionnel ; un objet init qui vous permet de contrôler un certain nombre de réglages :
var myHeaders = new Headers(); var myInit = { method: 'GET', headers: myHeaders, mode: 'cors', cache: 'default' }; fetch('flowers.jpg',myInit) .then(function(response) { return response.blob(); }) .then(function(myBlob) { var objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; });
Reportez-vous à fetch() pour la liste complète des options disponibles, et plus de détails.
Vérifier que la récupération a réussi
Une promesse fetch() va retourner une TypeError quand un problème réseau s’est produit. Cependant, il peut aussi s’agir d’un problème de permission ou quelque chose de similaire — un code HTTP 404 ne constitue pas une erreur réseau, par exemple. Un bon test de la réussite de fetch() devrait inclure la vérification que la promesse soit résolue, puis vérifier que la propriété Response.ok ait la valeur true. Le code devrait ressembler à ce qui suit:
fetch('flowers.jpg').then(function(response) { if(response.ok) { response.blob().then(function(myBlob) { var objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; }); } else { console.log('Mauvaise réponse du réseau'); } }) .catch(function(error) { console.log('Il y a eu un problème avec l\'opération fetch: ' + error.message); });
Fournir votre propre objet requête
Plutôt que de transmettre le chemin de la ressource que vous souhaitez récupérer avec l’appel fetch(), vous pouvez créer un objet de requête en utilisant le constructeur Request(), et le transmettre à la méthode fetch() en tant qu’argument:
var myHeaders = new Headers(); var myInit = { method: 'GET', headers: myHeaders, mode: 'cors', cache: 'default' }; var myRequest = new Request('flowers.jpg',myInit); fetch(myRequest,myInit) .then(function(response) { return response.blob(); }) .then(function(myBlob) { var objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; });
Request() accepte exactement les mêmes paramètres que la méthode fetch(). Vous pouvez même lui transmettre un objet Request existant pour en créer une copie :
var anotherRequest = new Request(myRequest,myInit);
C’est très pratique, si le corps de la requête et de la réponse ne sont utilisés qu’une fois seulement. Cette manière de faire une copie permet de ré-utiliser la requête/réponse, en changeant juste les options du init si nécessaire.
Note : Il y a aussi une méthode clone() (en-US) qui créer une copie. Cela a une sémantique légèrement différente à l’autre méthode de copie— La première va échouer si l’ancien corps de la requête a déjà été lu (même pour copier une réponse), alors qu’avec clone() non.
En-têtes (Headers)
L’interface Headers vous permet de créer vos propres objets d’en-têtes via le constructeur Headers() (en-US). Un objet en-tête est un simple ensemble de plusieurs clé-valeurs:
var content = "Hello World"; var myHeaders = new Headers(); myHeaders.append("Content-Type", "text/plain"); myHeaders.append("Content-Length", content.length.toString()); myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
On peut atteindre le même résultat en transmettant un tableau de tableaux ou un objet littéral au constructeur:
myHeaders = new Headers({ "Content-Type": "text/plain", "Content-Length": content.length.toString(), "X-Custom-Header": "ProcessThisImmediately", });
Le contenu peut être interrogé et récupéré:
console.log(myHeaders.has("Content-Type")); // true console.log(myHeaders.has("Set-Cookie")); // false myHeaders.set("Content-Type", "text/html"); myHeaders.append("X-Custom-Header", "AnotherValue"); console.log(myHeaders.get("Content-Length")); // 11 console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"] myHeaders.delete("X-Custom-Header"); console.log(myHeaders.getAll("X-Custom-Header")); // [ ]
Certaines de ces opérations sont seulement utiles dans ServiceWorkers (en-US), mais elles fournissent une bien meilleur API pour la manipulation des en-têtes.
Toutes les méthodes d’en-tête provoquent une erreur TypeError si un nom d’en-tête utilisé n’est pas un nom d’en-tête HTTP valide. Les opérations de mutation vont provoquer une erreur TypeError si il y a une protection immutable (voir ci-dessous). Sinon elles vont échouer en silence. Par exemple :
var myResponse = Response.error(); try { myResponse.headers.set("Origin", "http://mybank.com"); } catch(e) { console.log("Ne peut pas prétendre être une banque!"); }
Un bon cas d’utilisation des en-têtes est de vérifier que le type de contenu récupéré est correct avant de poursuivre le traitement. Par exemple:
fetch(myRequest).then(function(response) { var contentType = response.headers.get("content-type"); if(contentType && contentType.indexOf("application/json") !== -1) { return response.json().then(function(json) { // traitement du JSON }); } else { console.log("Oops, nous n'avons pas du JSON!"); } });
Protection (Guard)
Puisque les en-têtes peuvent être envoyés dans les requêtes et reçus dans les réponses, et ont diverses limitations sur quelles informations peuvent et doivent être mutables, les objets en-tête ont une propriété guard. Ce n’est pas exposé au Web, mais cela définit quelle opération de mutation est autorisée sur l’objet en-tête.
Les valeurs possibles de la propriété guard sont:
Note : Vous ne pouvez pas ajouter ou définir sur une requête protegée une en-tête Content-Length. De manière similaire, ajouter Set-Cookie dans l’en-tête de réponse n’est pas autorisé: les ServiceWorkers ne sont pas autorisés à gérer des cookies via des réponses synthétisées.
Réponses
Comme vous l’avez vu ci-dessus, des instances de Response sont retournées quand la promesse de fetch() est résolue.
Elles peuvent aussi être programmées dans le code via JavaScript, mais c’est seulement utile concernant les ServiceWorkers (en-US), quand vous retournez, pour une requête reçue, une réponse personnalisée en utilisant la méthode respondWith() (en-US):
var myBody = new Blob(); addEventListener('fetch', function(event) { event.respondWith(new Response(myBody, { headers: { "Content-Type" : "text/plain" } }); )});
Le constructeur Response() (en-US) prend deux arguments optionnels —le corps de la réponse, et un objet d’options (similaire à l’objet que Request() accepte).
Les propriétés de réponse les plus communes que vous allez utiliser sont:
Note : La méthode statique error() (en-US) retourne simplement une réponse d’erreur. De manière similaire, redirect() (en-US) retourne une réponse de redirection vers une URL spécifique. Elles sont aussi pertinentes pour les Service Workers.
Corps
Autant une requête qu’une réponse peut contenir un corps avec des données. Un corps est une instance de n’importe lequel des types suivants:
Le mixin Body définit les méthodes suivantes pour extraire le corps (implémenté autant par la Request que par la Response). Elles retournent toutes une promesse qui sera éventuellement résolue avec le contenu actuel.
Ceci rend l’usage de données non textuelles plus facile qu’avec XHR.
Le corps des requêtes peut être défini en passant les paramètres du corps:
var form = new FormData(document.getElementById('login-form')); fetch("/login", { method: "POST", body: form })
Les Requêtes et Réponses (et par extension la fonction fetch()), vont tenter de déterminer le type de contenu. Une requête va automatiquement définir un en-tête Content-Type si rien n’est défini dans le dictionnaire [NDLT: configuration d'initialisation].
ENVOIS SECURISES
die($_SERVER["HTTP_HOST"] . $_SERVER["HTTP_REFERER"]);
$request_url=( $_SERVER["HTTP_REFERER"]);
I currently have (for the login example I have included below):
The example
function loginUser() { var process = "loginUser"; var data = $("form").serializeArray(); data[1].value = SHA512(data[1].value); // sha then encrypt on api.php page data = JSON.stringify(data); $("#loginButton").html('<i></i> Login'); $.ajax({ type: "POST", url: "api.php", data: {"process": process, "data": data}, success: function(data) { if (data.response.state == "success") { // if api.php returns success, redirect to homepage } else { // if api.php returns failure, display error } }, error: function(jqXHR, textStatus, errorThrown, data) { // error handling }, dataType: "json" }); }
use https to ensure integrity
1. Check the ORIGIN header
As specified by OWASP, checking headers is a reasonable first step in your CSRF defense, but since they aren’t always present, its generally not considered a sufficient defense on its own.
And by Mozilla : Checking the HTTP_ORIGIN header could be written as :
header('Content-Type: application/json'); if (isset($_SERVER['HTTP_ORIGIN'])) { $address = 'http://' . $_SERVER['SERVER_NAME']; if (strpos($address, $_SERVER['HTTP_ORIGIN']) !== 0) { exit(json_encode([ 'error' => 'Invalid Origin header: ' . $_SERVER['HTTP_ORIGIN'] ])); } } else { exit(json_encode(['error' => 'No Origin header'])); }
Again from OWASP : If the Origin header is not present, verify the hostname in the Referer header matches the site’s origin. Checking the referer is a commonly used method of preventing CSRF on embedded network devices because it does not require a per-user state.. This method of CSRF mitigation is also commonly used with unauthenticated requests [...]
Checking the HTTP_REFERER is also quite simple in PHP with $_SERVER['HTTP_REFERER'], you can just update the above code with it.
NOTE : do no check just example.com or api.example.com but the full https://example.com, because you could spoof this check with an origin like api.example.com.hacker.com.
2. Generate CSRF tokens
A well-explained answer specific to PHP has been given there, in short :
Generate the token :
session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }
Add it in your generated views via a meta (like Github) :
<meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
Setup jQuery ajax calls to include this token :
$.ajaxSetup({ headers : { 'CsrfToken': $('meta[name="csrf-token"]').attr('content') } });
Server-side check your AJAX requests :
session_start(); if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } header('Content-Type: application/json'); $headers = apache_request_headers(); if (isset($headers['CsrfToken'])) { if ($headers['CsrfToken'] !== $_SESSION['csrf_token']) { exit(json_encode(['error' => 'Wrong CSRF token.'])); } } else { exit(json_encode(['error' => 'No CSRF token.'])); }
Most PHP frameworks have their own CSRF implementation, which more or less lay upon the same principle.
3. validate user input.
You always must
filterespace inputs and validate them.4. Protect your server
Limit the number of your requests. - Use https as much as possible. - Block bad queries. - Protect POST requests.
5. Never trust user input
That’s Origin, which is easily forged if the attacker isn’t doing a CSRF attack
I had to change the session array key to "Csrf-Token", otherwise it got renamed on my server to "Csrftoken" in the apache headers.
is it secure enough to use openssl_random_pseudo_bytes() for generating random bytes?
Using AJAX is just as safe as posting data with, for instance, a form.
Using HTTPS prevents man-in-the-middle to see your user data, so the actual data sent by the user are safe.
securing your server side using session, rate limiting, data validation, fail2ban, log monitoring…