I have to process a web page. This web page is based on YII framework, and the login page is protected by CSRF tokens. When I pass the login credentials by POST method. I get the error 400 and The CSRF token could not be verified message.
I don't know how to by pass this protection. I don't understand the mechanism. When I login by the Chrome browser, I see what the POST message look like. It has 4 parameters: CSRF key, login, password, an one empty variable. How the browser gets the proper CSRF key to be sanded back?
I have a login and password for this web page, and I can login as normal user. Only the login page is protected against CSRF. Can I use the cookie (how to do that) created by browser on normal login, give this cookie to cURL and start processing URLs behind login page?
MrMgr Answer in his comments. I've put it here to help other people easily identify the answer.
The CSRF key is generated for session and it is inside LOGIN page as plain text. I can copy it from the source code, of the login page, and provide to cURL script to be past as POST variable. The CSRF Key doesn't change after every page refresh, a KEY is valid until logout. On logout the CSRF key is sanded to server for termination.
Source
CSRF tokens are in place to make this precise action difficult. You need a better way to spoof being a browser with PHP. To do that, store all cookies in what is generally called a "cookie jar." PHP's implementation of curl has that capability. All curl requests routed to this site should use this cookie jar from now on.
Next you need to parse the login page to grab all fields that are submitted. This includes the username, password, CSRF, and other hidden fields. Make sure you have values for each one. If it's not supposed to be entered by you (e.g. hidden inputs), scrape the login page's HTML and put those fields into variables you can pass along in the login POST. Also be sure to send the url of the login page you scraped as the referrer in the login POST.
Parsing html can be tedious, but libraries like SimpleHTMLDOM should make it simple if you're familiar with CSS selectors.
Related
I'm having an issue with CSRF tokens on externally linked pages.
I have a chat bot in whatsapp that sends a user a one-time link to the site. They click the link and need to enter a PIN on the site in order to confirm a request (we do this so the user's PIN is never saved in plain text on whatsapp).
Like all forms on the site, the PIN page has a hidden CSRF token that gets submitted too. The CSRF token is generated in part with the session id, and on submission part of the validation is checking its for the current session.
I am seeing an issue where on some submissions of the PIN page that the CSRF validation is failing due to the session id being different from the initial page load request.
My guess on what is happening is that the user has an active session with the site already (lets call that session 001). They later get sent the whatsapp link which they click on. It looks like sometimes the browser does not send the existing session cookie (I assume due to some tightening on the SameSite policy stuff), so when the user lands on the PIN page the site sees it as a new visit and creates a new session 002. Then on submit of the form the browser sends the original "session 001" cookie. So the token CSRF token was generated with "session 002", but validated against session 002.
(It's probably worth noting this is a legacy system and no SameSite policy is being explicity set)
I don't want to be logging session ids in production so I can't think of a way to verify this is actually what is happening. I haven't been able replicate this behaviour in my dev environment yet.
Does this sound like a known thing that I am just not understanding well? Any ideas on ways to work around this issue?
Sessions don't work between browser tab reloads as a conceptual model (IMO) https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage. In your use-case, a session is between browser tab reloads (both by refresh, hard refresh or navigation by external link) barring reloads by 'back' button presses. So in short, you need your own session UUID generator and management. Try using UUID classes implemented in most web-server platforms.
localhost may work differently due to http stipulations, although I can't figure out why this wouldn't be reproducible in localhost.
In CSRF form protection we use session right. Let consider if I have login PHP at the same login page. And I want to make secure my login form with CSRF.
At one page how we add two sessions. ?
One session we will start when user login and second we use at login CSRF.
How it possible. Please help to send sample demo.
Thanks in advance.
Although the question is vague I'll take a stab at the answer. To protect yourself from CSRF here is what you need to do in principle in it's simplest form.
When an initial request is made on your login page where you render the login form, generate a unique token/hash server side and store it in your session or perhaps another datastore.
When you render the login page store this token client side either in a cookie or as a hidden form field.
When the user fills up the form and submits the request, First ensure the unique token is valid then authenticate the user. If the unique token doesn't match then they either tampered with the token or are trying to make post request in an automated fashion.
CSRF is most applicable for users that have logged in because if the token were to fall into wrong hands worsts things can happen. So in that case once the person has logged in for every active user session you will generate a unique token for every request to ensure that it's actually the authenticate users that making the request.
Hope this helps!
I have recently been working with a way to stop CSRF attacks happening by using a token. To my understanding:
1) User logs in, set session cookie to logged in and generate CSRF token and save it to the session
2) User submits form (with token) and it should match the token in session
Hypothetically lets say I have page1.php which has a sql SELECT to get all the users account information and then within the same page I also have an ajax button to page2.php which changes the account information. Obviously I protect page2.php from CSRF (because this is a post request) but how do I protect against page1.php? If this page was to get called from a remote source by ajax or putting an iframe on an attackers website, surely this would print all of the victims account information?
If this is the case how come I can't seem to find anything on protecting all pages from CSRF attacks and I only find resources for protecting against CSRF attacks in ajax?
Suppose you have a page at http://application.com/mypage with some data and a CSRF token generated. Attacker creates http://attacker.com/attack, and when a valid user of application.com visits, makes a request (either via ajax or in an iframe, doesn't matter) to application.com in order to get hold of application data. Standard csrf.
The reason this won't work for the attacker is the same origin policy. When the victim user is on attacker.com, the request to application.com will be cross-domain. If it's an iframe, data will be displayed, but attacker.com will have no access to it, it will only be displayed for the user that could have a look on application.com anyway. If it's an ajax call, the same applies, javascript on attacker.com will have no access to the response, ensured by the browser.
For ajax calls, access to cross-domain responses can be explicitly enabled by the access-control-allow-origin and related response headers (CORS) sent by application.com in the response.
Note that despite being cross-domain, the call will still be made (preflight requests come into play in some cases, but let's not go into that now). It's only the response that will be inaccessible for the attacker, and that is enough to prevent csrf.
(Also as a sidenote, application.com should prevent being displayed in an iframe by for example sending an x-frame-options: sameorigin header to prevent clickjacking and similar attacks, but that was not the question.)
I am not sure I get the point but the token for CSRF should be a nonce, which changes on each call.
Regarding a call coming from some other place, the ajax request should also check that the call comes from a valid user with the correct entitlments as you would do with any "regular" call to the site.
If this is the case how come I can't seem to find anything on
protecting all pages from CSRF attacks and I only find resources for
protecting against CSRF attacks in ajax?
What you ussually do is to set a nonce in a hidden input of the forms you use in "regular" pages.
Hypothetically lets say the page has a sql select on it and does not
request post. Now if this page was to get called from a remote source
by ajax or putting an iframe on their website, surely this would print
all of the victims data?
Make sure that all requests that hand out "confidential" information (either as a webpage or as data for an ajax call (or whatever in the future) do validate that there is a valid session active. If it's not, make sure the server side portion does not hand out anything confidential but instead throw an error and make sure your ajax client understands it and does the right thing for normal users (like telling them they're not logged in and offering them to fix that).
If this is the case how come I can't seem to find anything on
protecting all pages from CSRF attacks and I only find resources for
protecting against CSRF attacks in ajax?
For regular post requests typically the form is output with a hidden input field that holds the CSRF token. That's all.
BUT do make sure the code processing the request validates that the hidden field is present and filled in with the right value.
Also make sure that any request that modifies things is CSRF protected. E.g. that delete button in a non-ajax context should be protected with sending and validation of the CSRF token (hence the button ends up as a form with a hidden field for the CSRF.
What I've understood, CSRF-Attacks works like this:
When you check if a user is logged in to your website you check if a validate cookie is set and matched to the database.
A 3rd part sends the user to a site in your website, for example "changePassword.php"
The site "changePassword.php" will check if the user is logged in by checking the cookie is matching to the database, if it is set, the page will "change the password". It is set since you logged in to the website in another tab therefore the password will be changed.
To prevent this websites are using a Token that is generated random by the webpage, and stored as a session, then your webpage sends this token with the request, as a parameter. Since that make the website know the user really visited the website and performed the action.
The question is:
Instead of sending a token as a parameter. Can you just send the logged in cookie as a parameter?
Since cookies are webiste-specific, I can not see a way another page would be able to send this cookie as a parameter to your website.
Yes you can, assuming "logged in cookie" is a cryptographically secure session identifier.
This method of CSRF defence is known as Double Submit Cookies:
Double submitting cookies is defined as sending a random value in both
a cookie and as a request parameter, with the server verifying if the
cookie value and request value are equal.
Normally this value is separate than the session ID, but there is no reason not to use it if your session IDs are static.
In a CSRF attack, the attacker can only submit the victim's cookies to the target site using the victim's browser. There is no way of the attacker actually reading the cookie value, nor a copy of this value set as a POST parameter. So, double submit cookies is a good CSRF defence.
Note that using a GET parameter is not recommended, as this value can be leaked in the referer header.
I found out why this wont work, if you send your cookie as a parameter there will always be a way for the attacker to use that (jquery?)function, or similar. Therefore you will always have to get the token from the webpage. You could maybe print the cookie and then get it that way, but it would just be easier to make a session token as an hidden input to the website.
I have a question in mind.
Is this possible? I need to put the login page of our other site into an iframe. and when logged in you will see a graph.
What I need is when I click the hyperlink is it will show the page in the iframe but autofill and auto- logged in the account so that what they will see is not the login page but the graph inside it.
Is there a way to pass the username and password in the iframe and trigger the logged in button or auto logged in?
Can do this in PHP or jquery?
Regards to all and thanks in advance.
The only caveat in Treffynnon's answer is that it requires you to have access to both systems to create a sort of "login code" web service that the two can use to communicate. If you only have access to the first site (the one that you want to include the iframe on), what I would do is the following (please note that this is not as secure as Treffynnon's answer):
load the page without the iframe
using ajax, query your server (over SSL!) for the username and password that you'll need to submit to the other server.
dynamically create your iframe, populated with a form that is basically identical to the login form on the other server, submitting with the same "method" to the same "action" as the other form. Then dynamically submit it. This should set the appropriate login cookies on the client so that they are logged in. Make sure you use SSL everywhere.
dynamically redirect the iframe to the page that has the chart you want to see.
obliterate any javascript variables that held secure login information.
Potential issues:
Some login processes are extremely finicky, and may be impossible to log in with using this method.
You're dramatically increasing the number of attack vectors on your site. For example: step 2, querying the login credentials, assumes that your security is adequate to prevent people querying for data they shouldn't have access to.
All of this assumes that it would be OK for the end user to access the login credentials, say, they have one username and password that they use at both sites. If you have 1 site-wide login that you're using to give your end users access to something they wouldn't otherwise get, then this will expose your login details to all of your users and you shouldn't do it. In that case, your only option is to do something completely on the back end. If you have access to the code for both sites, use Treffynnon's suggestion, otherwise you'll have to access and download the chart on the back end and re-display it for your users.
Do not expose the password rather create a unique key you can send over with them to authenticate them on the page that will be displayed in the iframe. This assumes that both systems are aware of the user and the unique key.
When you setup the iframe you can set its source to include some URL parameters:
<iframe src="http://example.org/api?key=<?php echo $unique_key; ?>">
This will get you around cross domain issues that a javascript method of doing this would throw up.
The first what you can try, is sending the username and password with GET method in iframe. Like src=yourscript.php?user=me&pass=secret so you can try to auth the user on the other side.