I was wondering, what is the point of CSRF protection ? I mean, generating tokens and putting the hidden field with token into the form and then after the POST is done, to control the two tokens. I was testing my webpage and the whole thing I did to get around it - I had just copied the entire generated form (html source code) and changed the form action attribute. Aren't web bots doing something similar or I just don't understand something ? How to make an efficient CSRF protection ?
P.S I'm using Kohana (Security::token and Security::check)
Thank you!
From Wikipedia:
The attack works by including a link or script in a page that accesses
a site to which the user is known (or is supposed) to have been
authenticated. For example, one user, Bob, might be browsing a chat
forum where another user, Fred, has posted a message. Suppose that
Fred has crafted an HTML image element that references an action on
Bob's bank's website (rather than an image file), e.g.,
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=Fred">
If Bob's bank keeps his authentication information in a cookie, and if
the cookie hasn't expired, then the attempt by Bob's browser to load
the image will submit the withdrawal form with his cookie, thus
authorizing a transaction without Bob's approval.
By generating and including random token in your form, you can avoid this kind of attack by checking if the posted token matches the generated one (usually stored in user session).
Kohana docs say clearly how one should do it:
You can insert this token into your forms as a hidden field:
echo Form::hidden('csrf', Security::token());
And then check it when using Validation:
$array->rules('csrf', array(
'not_empty' => NULL,
'Security::check' => NULL,
));
There is already some built-in helpers for generating a security token and using it on a form. One can generate the token, insert it into a hidden field, and then use a Validator to validate against the Security helper class.
See docs: http://kohanaframework.org/3.0/guide/api/Security#token
Related
So I just read this article by Jeff Atwood and I wanted to make sure I understand it correctly as to how it applies to my use case. I am trying to validate a session for silent login. For security purposes this should be done with a POST right? Does it matter? I am just passing the sessionID and username from the cookie.
When it comes to CSRF (Cross-site request forgery), you can cause a user to take any action on any site which they are logged in to provided that the action requires only a GET. Forcing this to be done over a POST request defeats the approach of embedding an image, script tag, whatever in another page.
Even POST isn't completely secure in this scenario. There are other ways to mount a CSRF attack on a site using POST. Clickjacking/UI-Redressing enables another site to trick a user into submitting a form to a different website.
Basically the best way to validate is to add an automatically generated, hidden form element. You can store this inside your session data (Example: $_SESSION for PHP) so that you only need to generate a token at the start of a session. Of course, an attack could try do something like clickjacking (mentioned above) in combination with a iframe pointing directly to your site and possibly some JS to hide things a little.
For anything important you should re-prompt the user for their password, thereby greatly diminishing the value of any successful CSRF attacks.
I want to implement CSRF prevention in my Go web application. Users don't log in, but they do fill out forms and pay (via Stripe Checkout).
Posting something sets a key in a session variable (cookie) so they can later edit what they've posted, and a URL in an email allows them to come back when the cookie has expired and edit it again if need be.
From what I can see, I can use https://code.google.com/p/xsrftoken/ with the "double submitted cookie" method to implement CSRF prevention by:
Generate a CSRF token against an arbitrary user ID (uuid.V4() via go-uuid), like so:
if session.Values["id"] == "" {
session.Values["id"] = uuid.NewV4()
}
csrfToken := xsrftoken.Generate(csrfKey, session.Values["id"], "/listing/new/post")
... and store that in the session and render it in a hidden field in the template:
session.Values["csrfToken"] = csrfToken
...
<input type="hidden" id="_csrf" value={{ .csrfToken }}>
When the user submits the form, I need to get the ID I generated, confirm that the submitted csrfToken from the form matches the one in the session, and if so, validate it with the xsrf package to confirm it hasn't expired:
userID := session.Values["id"]
if session.Values["csrfToken"] != r.PostFormValue("csrfToken") {
http.Redirect(w, r, "/listing/new", 400)
}
if !xsrftoken.Valid(session.Values["csrfToken"], csrfKey, userID, "/listing/new/post") {
http.Redirect(w, r, "/listing/new", 400)
}
My pertinent questions are:
Should I generate a new token every time the form is rendered? Or is it acceptable to re-use a non-expired token for a single user session? Update: According to this answer I should only generate a new token per session (i.e. so the same user gets the same token on the same form, until the token expires)
Given the updated question, how do I handle the situation where a created token expires between the time the user requests the form and then submits the form? (perhaps it had 10 minutes left, and they alt+tabbed out for a while) Re-direct them back to the form (re-populated, of course!) and generate a new session id + csrf token?
Is there a different way to do this? Coding Horror indicates that SO generates a unique key for every HTML form sent to the client? How would I go about going down this route with the xsrf package, given that it wants a userID when generating a new key?
What else have I overlooked?
Should I generate a new token every time the form is rendered? Or is
it acceptable to re-use a non-expired token for a single user session?
Update: According to this answer I should only generate a new token
per session (i.e. so the same user gets the same token on the same
form, until the token expires)
It is a good idea to regenerate both the token and session ID often. Given a persistent attacker and a viable entry vector, it's just a matter of time until the attacker obtains both. If, however, at least one of both identifiers regenerates before the attacker is able to crack the current one, then no problem.
Given the updated question, how do I handle the situation where a
created token expires between the time the user requests the form and
then submits the form? (perhaps it had 10 minutes left, and they
alt+tabbed out for a while) Re-direct them back to the form
(re-populated, of course!) and generate a new session id + csrf token?
You can update cookies and CSRF tokens through AJAX if you want to give your client vast time to fill out a form.
Is there a different way to do this? Coding Horror indicates that SO
generates a unique key for every HTML form sent to the client? How
would I go about going down this route with the xsrf package, given
that it wants a userID when generating a new key?
The more tightly bound a token is to a certain action that requires authentication, the more fine-grained control you have. If you can uniquely identify each form in your application then I'd say do it.
I've created a CSRF protection package for Go called nosurf. Here's how it handles the areas you mentioned:
Token is created by taking bytes from CS PRNG and encoding them using base64. It is not regenerated for every page load or every form, though there is a user-callable function for regenerating the token.
It is then stored in a cookie (not a session, as it's a generic middleware not intended for any specific framework only). The cookie lasts one year, but you can easily modify this duration.
nosurf takes care of cancelling the request and either returning 403 or calling your custom failure handler (if set). You don't have to have if CsrfCheckOk(r) { ... } or anything like that in your code.
Sadly, it doesn't address token expiring inbetween the page load and the form submission.
So that's it, even though I'm not sure it is the best way to handle CSRF all-around. A package for a specific framework might handle it better in some ways due to tight integration.
My bookmarklet can be called from any website and basically allows the user to insert a row into his collection from afar - if he is logged in.
Now I want to enable CSRF protection for my site and since a bookmarklet is basically non-forged cross site request, I thought about how I could tell it apart from forged ones.
It's not a high-security environment, but I'm also interested in principle.
I thought I had a way to do it figured out, but then realised that it had problems galore.
Original idea
generate a random key that is included in the bookmarklet-link. The key's hash is saved in the database. The random key allows access ONLY to the privilege of insertion into this collection and can't be used anywhere else.
the bookmarklet loads a longer script from my server, so I could supply a CSRF prevention token this way
require the user to be logged in
Problems
If I have the bookmarklet key, do I need counter-CSRF tokens?
Is there any way that I could protect the bookmarklet key if the user clicks his bookmarklet on a malicious website?
I don't want username and password to be stored in the bookmarklet link, because anybody who has access to the computer would get the password as well then, so I decided on the random key.
but if I store only the hash, I cannot generate the same bookmarklet link twice, so when the user wants a bookmarklet in another browser/computer he tediously has to import the link from the old one or break support for the old one.
but I shouldn't store the cleartext key, because someone who gains access to the database could use this key to insert rows into accounts that don't belong to him.
Possible solution I could ask the user to provide his password anytime he creates the bookmarklet and hash the password many times and put that hash in the URL and put the hash of that hash my database. But of course this opens much worse security holes.
I could do this with something like "Mother's maiden name" instead
I cannot use bcrypt for hashing because of the random salt, right? What hash function would be right? Or would you dismiss the whole idea?
If I leave the bookmarklet key out, a malicious website could simply embed the bookmarklet and extract a valid CSRF token from it, right?
Better ideas? Or can't you have CSR without the F?
Edit, specified use case
One thing I simply didn't think about, is the usage of an iframe as suggested by Sripathi Krishnan.
I had not specified my use case, so yes, an iframe is a valid solution to the aforementioned
problem.
However, actually my bookmarklet at the moment does do some basic interaction with the website at runtime (meaning the form is there already and the user can change his selection in the website DOM which should change the form). I'm ready to dismiss this functionality for my use case, if it turns out, there's no reasonably-secure way to tell apart forged from non-forged cross-site requests - but I'm still interested on a theoretical level.
You can't make cross-site requests without eliminating the forgery part. But for your use case, I don't think you need cross-site requests.
Lets assume your service allows users to bookmark any pages he wishes. The job of the bookmarklet would be to save {url, title} into the database. At the same time, you want to prevent a malicious website automatically saving urls for a user who is logged in.
Here's what I would do to solve this -
Create a page on your domain that has a standard html form with regular CSRF protection. This page takes in the parameters {url, pagetitle}, but will only save this tuple when the user explicitly clicks the "save" button.
The bookmarklet's job is to load the iframe
Once the iframe loads, its a normal same-origin request
This is more or less what Google reader does as part of its bookmarklet. Here is the code for its bookmarklet - notice it doesn't have any tokens
javascript:
var b=document.body;
var GR________bookmarklet_domain='http://www.google.com';
if(b&&!document.xmlVersion) {
void(z=document.createElement('script'));
void(z.src='http://www.google.com/reader/ui/link-bookmarklet.js');
void(b.appendChild(z));
}
else{}
EDIT :
You can still support interactions with the iframe approach. The bookmarklet executes in context of the website, so it has access to the DOM. You can interact however you want with the website. When you are ready to save, open up the Iframe. The iframe will be a sort-of confirmation screen with just one save button.
The trick is to delay creation of the iframe. You only create the iframe when the user is ready to save.
If the bookmarklet is being stored in a web browser then the idea of including a secret key which hashed in the database is a good idea. This will prevent CSRF, although it is not ideal because in a sense this is a session id that doesn't time out. It should be clear that this token is only being used for this bookmarklet action. Despite this limitation it is unlikely that you will experience an attack. Adding HTTPS will make this stronger because it will be harder to spill this token.
If the bookmarklet is originating from a 3rd party site, then there is nothing you can do. This is the definition of CSRF.
I have a site which will show sensitive information. I am using Anti Forgery Tokens etc to protect against XSRF in POSTS. However I am worried about someone being able to view sensitive info from a GET. What is the recommended practice for protecting read only data sent via a GET in .Net MVC 2?
If you are sure that GET requests are read-only, then you have nothing to worry from XSRF. Its not possible to steal information from another website using just XSRF, and so you don't need to protect urls via a token. In fact, using tokens in the URL is going to make it impossible to use bookmarks.
Having said that, you should be 100% sure there are no XSS vulnerabilities in your app. If there are, an attacker doesn't need to bother with XSRF and unpredictable tokens.
The XSRF protection on POST data (using tokens like you said) should work on GET data as well. From a hacker's point of view a GET forgery is much easier than POST forgery (at the first you only post a link, at the second you need to point to a malware website with hidden iframe and autosubmit forms), but both of them fail if tokens are checked.
Example:
posting a link like this:
www.domain.tld/page.aspx?get=data&token=token_given_to_hacker
shouldn't return anything, or just an error for those who got other tokens. This way no sensitive action is made for the wrong people.
This is xsrf, with this you can't steal information, but make others submit forms and take action which result only the hackers know. For example:
Changing email, on an email form, to the email of the hacker. Let's assume you have a GET form, with 1 field: the new email (for the sake of simplicity). When submitted the URL looks like this:
www.domain.tld/page.aspx?email=myemail#otherdomain.tld
Now, if a hacker posts a link on your forum with the URL:
www.domain.tld/page.aspx?email=hackers_email#yetotherdomain.tld
Everyone who clicks on it, will get a new email, of the hackers. But if you put a token there, and you'll check it every time, the action will be taken only for those who's the same token was given on page request, in this case the link will work only for the user who posted it, and no one else.
You could also protect sensitive forms, like changing email, password and so on with a password field. Note, that captcha is not much of a help here.
I have a web application which has basic authentication - username, password, session and stuff. However I have a specific need to prevent users from spoofing the POST requests (even for logged in users). In my application, I am specifically validating the user's session before accepting the POST data (taking care of XSS and stuff also).
Like:
if(user session exists)
{
// handle the data POSTed
}
else {
// ...
}
I am storing the sessions IDs in the database. Is there anything else I should be aware of to prevent bogus POST requests or is this sufficient?
I am specifically validating the user's session before accepting the POST
If you mean what is normally meant by ‘session’: a persistent token stored in a cookie that identifies the user and associated session data, then no, that's not enough. That cookie is sent automatically by the browser even if the POST request was provoked by another (attacker) site.
The keyword you are looking for here is Cross-Site Request Forgery or XSRF, where an authenticated user can be made by an attacker (via scripting or other methods) to make a GET or POST request to your site. Such requests are not generally distinguishable from legitimate requests. (Some people try to do so though checking the HTTP referrer data, but this is unreliable.)
These attacks are not quite as immediately damaging as server-side (SQL, command) or client-side (HTML, JavaScript) injections, but they are more common than both: few web programmers both to include the proper countermeasures, unfortunately. Until they get their sites compromised by an XSRF hole anyway.
There are various way to defend against XSRF, but the only really effective approach is to include in each submittable form a secret value that the third-party attacker won't know. This is often known as a post key, as mentioned by Eimantas.
There are various ways to generate such secret information. A simple approach is to add a randomly-generated code to each user's account details, then put that in a hidden field in the form and check for its presence in the submission. eg in PHP:
<form method="post" action="delete.php"><div>
<input type="hidden" name="key" value="<?php echo(htmlspecialchars($user['key'])); ?>"/>
<input type="submit" value="Delete" />
</div></form>
if ($_POST['key']!=$user['key'])
// error
An attacker won't know the key for that user so can't make a link/form that contains it.
You could also use a cryptographic hash function on the user's ID with a server-secret key, rather than keeping a separate code. With a hash, you can also throw in other stuff like an expiry time so that forms have to be submitted within a certain timeframe. Or you can generate a one-use transaction key, which you can also use to make sure you can't submit the same form twice (for stopping double-posting).
You could try generating post keys for each post request. Sort of additional param that shows that post request is valid and was executed from a form on your page.
If you are building valid POST requests in Javascript in the user's browser, there is not much you can do to prevent a determined user from submitting bogus POSTs to your server. The user has a valid session id that he can use to make a POST request. He also has access to all of the code and all the other data that code has access to for building the request.
You can't rely on browser-side code to secure your system. The security has to be enforced at the server. For example, all operations on objects should be authenticated and authorized.
Use a CAPTCHA image.
The web is built on REST, which by definition is all about transferring state from one point to another. Someone with enough time on their hands could craft a POST request that emulates an active session.
Like all secure requests, CAPTCHA is validated server-side.
In my current application I have some code which is sent to the browser and the browser then posts back and must not be able to modify it. What I do is to append a secret string to the value, get the SHA1 checksum of that full string, and then require the browser to send back both the value and the checksum. I'm pretty sure this is how .NET does viewstate, too.
If user session is long-lived, you are still susceptible to XSRF. You need to take measures against that too.
If you are on .NET, check out AntiForgeryToken,
http://msdn.microsoft.com/en-us/library/dd492767.aspx
When accepting user input, the zero-level thing you need to do, before storing things in the database is to make sure you run the data via the mysql_real_escape_string($MyPostData) function.
Also, it is good for every variable/data you want to accept via POST to validate it programmatically based on its type and what you intend to do with it.
These are two main rules for making sure there's no 'funny' business coming from the user: making sure you work with valid variables AND making sure data that gets to the database is verified and escaped properly.
With your model (and especially if you use integer numbers for your session IDs) an attacker can easily submit a request on behalf of another user (e.g. decrement your own session ID and you are already someone else provided this session ID exists).
You need to have a unique session key/guid associated with each session ID and stored both in the DB and on the client in the cookies. Each time your client submits a request you should read not only session ID but also the session GUID and then validate them both against your database values.
In addition to that you may also want to consider some XSRF mitigation strategies.