Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- +50
- 1. Check the ORIGIN header
- As specified by OWASP, this is not enough but recommended :
- Although it is trivial to spoof any header from your own browser, it is generally impossible to do so in a CSRF attack, except via an XSS vulnerability. That's why 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 :
- The Origin header is considered helpful against JSON data theft and CSRF attacks. The information provided by Origin--a bit of contextual request-creation information--should provide hints to web servers about trustworthiness of requests [...]
- 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']));
- }
- 1. (bis) Check the REFERER 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.
- BE CAREFUL with the checking which always need to be really specific : do no check just example.com or api.example.com but the full https://example.com. Why ? 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. Sanitize validate user input.
- You always must filter espace 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
- As @blue112 said, it is one of the most elementary security principles.
- enter image description here
- shareimprove this answer
- edited May 23 '17 at 12:02
- Community♦
- 111 silver badge
- answered Jun 19 '16 at 23:47
- Ivan Gabriele
- 4,32822 gold badges2828 silver badges4949 bronze badges
- 1
- X-Requested-With is a non-standard header which never tells you what URL initiated the request. That's Origin, which is easily forged if the attacker isn't doing a CSRF attack. – Quentin Jun 21 '16 at 23:48
- I had to change the session array key to "Csrf-Token", otherwise it got renamed on my server to "Csrftoken" in the apache headers. – Dennis Heiden May 23 '17 at 16:37
- is it secure enough to use openssl_random_pseudo_bytes() for generating random bytes? – wpcoder Oct 25 '17 at 19:16
- 1
- @wpcoder I'm not an expert in cryptographic security. However the PHP official documentation and some googling tells me that "pseudo-random" is less random than "cryptographic random". Moreover, Symfony and Laravel (based on Foundation) are also both using random_bytes. – Ivan Gabriele Oct
Add Comment
Please, Sign In to add comment