CSRF Token Multiple tab issue - php

I am implementing CSRF token in my website on every post method.
But when i am accessing my webpages in different tabs then token gets change on both pages and token mismatches.
My token is stored in DOM and i am matching token using SESSION.
How to solve this.?

i change the token on every successful request
Yeah this is why we don't invalidate the token on every successful request. That doesn't just break multi-tab browsing, it also means you can't do stuff like hit the back button then submit.
“Invalidate token on every request” is the kind of bogus security recommendation you get from pentest reports where the tester hasn't found much that's really vulnerable. It's a trade-off as always whether you do, but the usability downside almost always outweighs the minimal security benefit.
You only really need to invalidate the CSRF token (along with the session token) on a privilege level change, most notably on login. This mitigates session fixation attacks, by preventing an attacker who knows the session and CSRF tokens prior to login from exploiting those tokens after you've logged in.

You can achieve this easily:
In the server side, store the CSRF tokens in session like this:
$_SESSION['csrf_tokens']['form1'] = //code to generate csrf token
While validating the token on form submit, you can check,
$_SESSION['csrf_tokens']['form1'] === $_POST['csrf_token']

Please post an example code, unless you are using ajax (which I wouldn't recommend for CSRF tokens the code shouldn't change in both tabs if you open a new tab). Also, I disagree with bobince, you are doing the right thing to implement this measure as once you have the logic in place you can easily and effortlessly use it in all your forms. The best way to implement this is to just have each token expire after a certain amount of time.
bobince: CSRF tokes are used to prevent CSRF attacks not session fixation attacks, both are different the former prevents scripts from executing actions on behalf of the user whereas the latter is an attack in which a malicious user impersonates a normal user by guessing or stealing their session id.

Generate two values – one random key (f.e. via uniqid), and a random token.
You generate both every time a form is rendered – and put them both into hidden fields. And you save the token into the session using the random key. Then when the form data is received, you check if the token send is in the session under the key send. (And if so, you delete the entry with this key after processing the form of course.)
Anything else (f.e. expiration time of tokens, binding of tokens to a certain form type out of several) you implement the same as you would before.

is unnecessarily and unsafe like this why you dont create a token based on session with openssl_random_pseudo_bytes() ,which will produce a safe token, and check if is correct or not or you can use also to expire after 2-5 min.also you can check on owasp about tokens on dom,can be easy spoffed !!!

Related

is putting token in URL secure to prevent CSRF attacks in PHP applications?

I want to use a token to prevent CSRF attacks on my website (written with PHP). I've used it in forms and it works well. But logout link is not a form; It is only a hyperlink.
Is it secure if I put the token in the query string like this:
Logout
If it has any problem, what is your suggestions and solutions ?
Yes, if the CSRF token is 'unguessable' and validated: the approach is the same in both cases.
From Wikipedia's Cross-site Request Forgery - Prevention:
Web sites have various CSRF countermeasures available .. Requiring a secret, user-specific token in all form submissions and side-effect URLs prevents CSRF; the attacker's site cannot put the right token in its submissions.
It doesn't matter if the token is from a form value or a query string parameter1. An approach that prevents CSRF by including a token in forms is adaptable to (and valid for) hyperlinks2.
1 A MitM / proxy which can intercept a URL can just as easily intercept an HTML form. This is outside the scope of a standard CSRF attack or mitigiation of such. In such cases the CSRF token value is 'knowable' and system is not secure.
2 This assumes the token is a per-user (and time-sensitive) value. A simple HMAC hash of the Session ID should be sufficient in most cases.
I think one of main disadvantages of using CSRF-token in GET requests is possibility of incompetent user to easily disclose his token by copying a link with the token and paste it in some public content like a comment/post/etc... Also GET query parameters including CSRF-tokens usually logged by HTTP servers/proxies and it introduces another risk.
So I suggest you to implement CSRF-secure links using something like this:
<form name="logout" action="logout.php" method="post">
<input type="hidden" name="token" value="9ae328eea8a72172a2426131a6a41adb"/>
</form>
...
Logout
Others made some good points. I add this answer to augment theirs.
Always filter and validate your query string name/value pairs.
Given: You have a database and you want to create a link to help dynamically get (but not change) content that a user can link to. Example: news articles, case studies, public user profiles.
Requirement: Use a query string and deter CSRF by using a token.
One of the purposes of a CSRF token is to help verify that an incoming HTTP request was generated from a page served from your domain (regardless if sessions are in play. The session token is a separate beast). A CSRF token works best when you use more than one defense vector.
Comparison: Check that a submitted token matches one in session.
Time: You can specify a token is only good for a certain period into the future.
public function setFormToken()
{
$token = $this->cipher->getFormToken(); //Some hashing algorithm.
$this->formToken = $token; //In this example, used to insert the token into HTML.
$_SESSION['token'] = $token; //Save the token for comparison upon form submission / HTTP query string processing.
$_SESSION['tokenExpireTime'] = time() + (60 * FORM_TOKEN_EXPIRE_MINUTES); //This is just an abstract example.
return;
}
Then, in addition to comparing the submitted token to the one in session, verify that the submission period is still valid.
private function hasTokenTimeLeft()
{
if(isset($_SESSION['tokenExpireTime']) && (time() < $_SESSION['tokenExpireTime']))
{
return true;
}
throw new SecurityException("Token time has expired for this POST request.", 911);
}
Sessions: If a session has expired, or is non-existent, the a false HTTP request for your content should fail.
Request Info: With HTTP POST request, some attempt to check that the token, user agent, and IP match the one from the original HTTP GET request (which means storing it in session before responding to the GET request).
Query strings, as previously mentioned, can get cached. There is no problem with that if the data is supposed to be publicly available, anyway. Think about someone bookmarking a product on an e-commerce website.
What you have to ask yourself is "Should I be able to get to this content from anywhere, anytime, through this link with a query string?" If yes, do not use a true, backend, randomly generated, CSRF token. Imagine you are running for elected office by word of mouth and people sending links to their friends and family in email (ok, bad practice). In this case, you never want to set off a "trip wire" and cause a fail condition. If a fresh token always needs to be generated on the backend first, emailing your link around will not work.
If you should not be able to get to content from outside of your security context (i.e., before logging in), then you are free to take whatever measures are necessary to fortify your CSRF token strategy using query strings.
Your log out snippet is a perfect example.
Logout
No one from outside your security context should be able to use the logout feature. You want the "trip wire" if someone emails this link around! Obviously, sessions are integrated into this solution (the token has to be stored somewhere while the user uses the page). Session duration and logging out should be managed carefully, and I say you are doing a good job.
Encryption: The only thing you could do better is encrypt the hash/token for the query string, then decrypt it and verify it upon submission. Additionally, you can break the token up into pieces, mix the pieces up, and basically use some security by obscurity string techniques to make your CSRF token more resilient.

CSRF token collisions with multiple tabs

I built CSRF protection in my application, by simply generating a random token on every page load, putting it into session, and then binding the token to the <body> tag attribute like:
<body data-csrf-token="csrf_GeJf53caJD6Q5WzwAzfy">
Then on every form action or ajax request, I simply grab the token from the body tag and send it along.
This works great, except for a huge issue. Users are opening multiple tabs of the application, and I am seeing token collisions. For example, a user loads the first page and it generates a token, then they switch tabs, load another page, which generates a new token. Finally they switch back to the first page and submit a format action. This results in an invalid CSRF token error.
What is the best way to re-architect this to prevent collisions with multiple tabs, while keeping it as secure as possible.
Is simply generating a single token upon login the correct solution, instead of generating a new token on every page load?
Assuming that your app is secured with SSL, then there is really no value created by generating new tokens on every page load. It doesn't stop an attacker who has exploited an XSS vulnerability – they'd have access to the freshly generated token anyway.
Remember what a CSRF token defends against: a malicious third-party page blindly trying to post data to your app in hopes that the user is logged in. In this kind of attack, the attacker would never have access to the CSRF token, so changing it frequently does no good.
Do not waste time and resources keeping track of multiple tokens per session. Just generate one at the start and be done.
You could use a single token upon login. As #Josh3736 points out, this works just fine.
If you really want to have one token per page, you could store an array of valid tokens in $_SESSION. You would then expire individual tokens as they are used. You could also optionally expire them after some timeout period, but that is only meaningful if the timeout is shorter than your session timeouts. But, again, what are you really accomplishing with this? A single token is perfectly fine for CSRF purposes.
I've run into this exact problem, on page load I was generating a CSRF token like this:
$_SESSION["token"] = bin2hex(random_bytes(32));
Multiple tabs was causing CSRF mismatches, so I changed to this:
if (!isset($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
Server side I do this (watered down version):
$csrf = preg_replace("/[^a-zA-Z0-9]+/", "", $_POST["token"]);
if ($csrf !== $_SESSION["token"]) {
// Give an error
die ("No valid CSRF token provided");
}
This may protect against XSS attacks, but it wouldn't stop someone going to the page, getting the PHP session ID (from headers) and the CSRF token and using a tool like Postman or WGET to put together hack API posts, etc.
That may be why this question exists... understanding the scope of what the CSRF token is for protecting against.

Session hijack resulting in token exposure

I'm trying to wrap my head around session hijacking and the use of tokens for CSRF protecting.
I use this object method in each of my scripts to check whether a session variable is set or the token matches the session token.
public function admin_index(){
session_start();
if(!isset($_SESSION["user"]) || $_GET['token']!=$_SESSION['token']) {
header("location: login/login_form.php");
session_destroy();
exit();
}
I'm new at this and my question is:
If my session id is somehow hijacked will he be able to some how also read my variable $_SESSION['token'] in the short time span after session_start and the the session data is fetched and populate in $_SESSION or is it still safe on the server?
Are session variables generally safe even though a valid session has been obtained?
Never mind the $_GET['token'] instead of POST. I'm still working on it.
Thanks
EDIT:
What I'm asking is. If a token also helps me secure my sessions the way I'm using it. If every query, link or view in my script requires a valid token and an attacker only got a hold of my session_id the tokens would be another layer of protection cause he/she would need both the id AND the token to do anything in the script, right?
And the token is secure on the server even though an attacker has acquired my session_id?
Session Hijacking and CSRF attacks are two completely different things and once someone has your access to your session they are 'you' and can access everything on your account.
A CSRF attack is an attack which forces an end user to execute
unwanted actions on a web application in which he/she is currently
authenticated
This is a social engineering and validation issue which using a token can obviously solve as it can be proved that the data was sent legitimately from your form. Using POST instead of GET will make this attack very difficult.
A Session Hijack on the other hand is where someone can use your
session, become 'you' and use your account which will allow them to do
whatever they please.
Once a malicious user has access to this session a CSRF attack is pretty much useless as it is not needed.
If you are worried about your session ids being hijacked then you can take some precautionary measures such as regenerating a users session id once they are elevated to a higher level of access. This can be done using the session_regenerate_id() PHP function. You can also check the User Agent of the browser to check if there are changes and if there are then you can simply ask the user to login and regenerate the id so it is then unknown to the attacker. Obviously there is always a chance that they will be the same user agent but it does limit the risk significantly. Encryption such as SSL/HTTPS are also an option that you may want to look at
For more information you should check out this link for some examples: http://phpsec.org/projects/guide/4.html. Hopefully this has solved your problem :)
Correct me if I'm wrong, but I'm quite sure you simply can't hijack $_SESSION values because these, unlike $_COOKIE is saved on the server and not in the webbrowser.
Therefor they can't change it either. They can close their webbrowser to remove it, but not edit it.
Session variables are stored on the server. They can not be read. However, a session can be hijacked typically by an attacker getting access to the session id of another user, and can then use that session id to impersonate them (hijack) their session.
CSRF is an entirely different concern. CSRF exploits get users to inadvertantly perform operations in a manner similar to xss exploits: I might post a bogus img link where the src is actually a url that passes parameters to a script that your browser runs on your behalf. In that case the attacker is simply getting your browser to make a request you didn't intend it to. The CSRF protection should stop this, because the user does not know what the token is, so they can't embed it into the url.

CSRF tokens vs Nonce confusion - are they the same?

In a attempt to make the current application I'm developing more secure, I've been reading about CSRF tokens and also Nonce.
My question simply is, Are CSRF tokens and Nonce the same thing? from what I could gather so far is that both these methods have different techniques to accomplish the same goal, or am I misunderstanding something?
If they are different, could you be nice enough to provide some example code or point me to some links where i can learn more about how to implementing nonces in PHP apps.
Thanks!
No, they're not the same.
Nonces prevent replay attacks (prevent eavesdropper from storing signed request and re-submitting it later, e.g. if Alice sends "Pay Bob $100", you don't want somebody to re-send that 100 times).
CSRF tokens patch HTML-specific weakness in authentication of users' action, where 3rd party website can submit forms with credentials of user viewing the site (e.g. JavaScript on evil.example.com submitting form to facebook.com using your browser, authenticated as you).
CSRF tokens need to be secret, otherwise attacker would have the missing piece required to forge a request.
Nonces don't have to be secret if they're signed with requester's secret (as long as attacker cannot replace one nonce with another).
You can allow replay of requests with CSRF tokens and still be secured against CSRF (you're interested whether that was intentional action by the user, but may not necessarily want to stop user from performing it many times).
In fact, that's very often useful property, e.g. allows users to use Back button and re-submit forms with corrected values. If you implement CSRF protection with Nonce-like mechanism, you'll get false alarms when users refresh submitted pages.
An easy way to prevent CSRF without Nonces is to put session ID in a hidden from field (not a value stored in the session, but ID of the session itself, the same that you store in the cookie [session_id() in PHP]). When the form is submitted check that form's session ID matches ID in the cookie. That is enough for CSRF, since attacker cannot know value of the cookie (CSRF only allows attackers to blindly send cookies).
Nonce is usually some random string that is added to request just to change in unpredictable way the data, which is used to calculate the signature. So nonce usually is not used by any server-side business logic.
While CSRF-token is stored somewhere on server, passed to the client and need to be returned back to the server to compare. And if matches - then OK.
So in your case the better will be to save csrf token once in a session variable like
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
and use it unchanged during the session life in all forms you have in your application.
(If you don't have random_bytes(), use random_compat to polyfill it.)
It's sort of the same thing. A "nonce" is just a one-time password itself. It can serve as cryptographic salt, but basically is just a random value. See WP:Nonce
But to sum it up, a nonce is often used as CSRF token. It's an implementation detail. The difference to other use cases is that it later gets asserted.
CSRF having some limitation.
in case if you have requirement where you want to open any page or link in new tab then CSRF won't allow. existing token will allow to open page in new tab for 5 times only.
when you will try to open 6th time it will create the new token which will not match with "server side = client side token". earlier token will expire and new token(NONCE) will create, in that case you will get 404 or 405 error.

For securing forms, when do I issue the token?

So, I have a form, to make it a little more secure and potentially help prevent CSRF attacks I want to add a random token value in a hidden field that value is also stored server side in my session data.
When should I issue a new token? Per form? Per page load where there is any form? Per session? I can render it invalid as soon as a form is successfully submitted but I'm wondering when to generate one.
I ask as if I issue it per form or per page do I not risk the chance of a duplicate token value overwriting the existing (valid) token if a user opens a separate window but submitting the first form (with the now overwritten value)?
The simplest way to prevent concurrency issues is to generate it only once per login. The chance of an attacker "guessing" your CSRF is about the same chance (or lower) as them stealing your PHPSESSID. You could also regenerate it whenever the user's access level changes, such as after they change their password or something.
If you want to be really thorough, you can generate and store an array of tokens, one for each form on the website. But if they can steal a CSRF token, they might as well have just stolen a session ID and done some real damage.
If you use one Token per session,
then you need a technique to check
duplicate submitted forms (when user
refreshes a page or click back
button).
If use it per form then if user opens
multiple pages then only the last
form will work.
I would prefer using array of
generated tokens per session, and
when a token is submitted it should be
removed. I read that this approach might be a
concern if you have high volume
traffic website.
I am not sure if you read this article before, but I think it is great resource about CSRF security:
http://shiflett.org/articles/cross-site-request-forgeries

Categories