@Jascha damn this is awesome question, and I don't mind getting the -3.
–
rookMay 16 '10 at 4:00

1

Franky, there is no way to prevent someone from submitting form data from "somewhere else". If you are talking about CSRF, then checking the Origin header or using tokens is the right thing to do, but no, you cannot prevent me from submitting the form values via Telnet no matter how much you try. You can only build foolproof technologies on the top of your system to keep the kiddies away.
–
TowerMay 16 '10 at 6:52

2

@Jascha, if you want to get responses for the code sample, then I feel that's a different question altogether. I'd propose to take it out of the question (and optionally post it as a new one), as the original question already created a lot of discussion. Please let's keep focussed on the REFERER part. Thanks.
–
ArjanMay 16 '10 at 7:25

In fact checking the referer is very common to see on embedded network hardware where Memory is scarce. Motorola does this for their Surfboard Cable Modems. I know this first hand, because I hacked them with csrf and then they patched it using a referer check. This vulnerability received a severity metric of 13.5 and according to the Department of Homeland Security this is the most dangerous CSRF vulnerability ever discovered and in the top 1,000 most dangerous software flaws of all time.

Rules mean nothing. It it comes from the browser or the user it should never ever ever be trusted. That's just how it is.
–
Josh KMay 16 '10 at 3:16

8

after some thought, I believe Rook is correct. When it comes to browsers and security, I always assume some browser, somewhere, is going to screw this up, but per the google reference, they all get it right. So unless you're talking about custom executables, or if you want to be totally safe -- which is completely defensible -- checking referer might be good enough.
–
Jeff Atwood♦May 16 '10 at 3:18

7

Even then, this would block innocent clients/proxies as well which doesn't send the expected referer by configuration/nature.
–
BalusCMay 16 '10 at 3:24

4

@Long: Please re-read my many comments. My point is that if you get information from the browser it should never be trusted. Ever. Can I make it ultra bold? ****Never****, ****ever****. Guess not, but I hope you get my point.
–
Josh KMay 16 '10 at 4:02

6

This is wrong for two reasons. First, the header is often omitted. Second, if the CSRF attack happens on the actual site, the referrer will be correct. For example, placing an image on the actual site in some comments area can launch a CSRF attack. Other than that, the claims of some people about rules are irrelevant. They probably do not understand what CSRF really is.
–
TowerMay 16 '10 at 6:42

Nope - HTTP_REFERER can be freely spoofed on client side and is not a reliable indicator of where a request came from.

Update: I misread the part about cross site forgery: For this, checking the referer is a valid security measure, because CSRF rely on manipulated links pointing to protected pages (that the attacked user has privileges on). User @Rook is correct.

The only exception is if the attack can happen from within the web application that is being attacked, e.g. by injecting malicious JavaScript code. In that case, a referer check is useless because the attack is coming from a "safe" URL, but so is arguably a solution based on a session or one-time token, because the token is in reach of the malicious JavaScript and can be easily retrieved.

However, using a one-time token is highly preferable to protect against this kind of attacks because HTTP_REFERER is stripped out by some proxies.

@Jascha the most adequate way would be using a session or a one-time token that gets set in the form, and gets checked when you process the form. (You would store the token in a database.) That way, you could at least make sure that people have to request your form first (if that really is of help.)
–
Pekka 웃May 15 '10 at 19:20

When an adversary can inject javascript to run in the actual domain instead of requesting from another domain, it's XSS, not CSRF. The solution is simple, don't allow people to inject javascript in your page.
–
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳May 20 '10 at 19:08

I'm using that as well. I'm just so terrified I'm looking to add as many failsafes as possible.
–
Howard ZoopaloopaMay 15 '10 at 19:18

Have you considered using some form of captcha?
–
gurun8May 15 '10 at 19:21

That is going to be level three. I thank you kindly for your responses.
–
Howard ZoopaloopaMay 15 '10 at 19:25

2

If you're going hog wild like that with multiple levels of security, throwing in a HTTP_REFERER checker into the mix won't hurt anything. It might be bit overkill but if it makes you or your client sleep better at night, so be it, right? Using it as a single line of defense is not the best choice.
–
gurun8May 15 '10 at 19:56

Exactly. The funny thing is, this time the client is me, and it makes me more anxious as it's my own reputation on the line. I don't want to enter the social scene with egg all over my face.
–
Howard ZoopaloopaMay 16 '10 at 0:07

Whilst it is impossible to spoof a Referer in another user's browser, it is easy to spoof a lack-of-referrer (eg. using a meta-refresh), in addition to some user-agents not sending a Referer at all.

So either you allow missing-referrer and have non-watertight XSRF protection, or you require a referrer that matches your site, in which case you take a big hit to accessibility. That hit might be acceptable if the only person using the script is you, and you know you'll always be using a browser/firewall/proxy/etc combination that passes referrers through reliably. But for anything you expect other people to use, it's generally not a good idea.

Referer is quite a weak anti-XSRF mechanism. Much better to use a per-user/event token issued by the server that must come back to the server to validate the submission.

Don't use setAttribute on HTML attributes. There are bugs in IE that stop it working in some cases, and there are some attributes that don't do what you think. For example, setting the value attribute is not the same as setting the value property. The property holds the current value of the form field; the attribute only holds the ‘default value’ of the field, to which it will be reset if an <input type="reset"> is used. This maps to the defaultValue property. In some browsers, setting the default value also sets the value, but this is non-standard and not to be relied upon.

Use the DOM Level 1 HTML properties, they're both more readable and more reliable:

Use json_encode to create values for JavaScript literals. Although you can be sure an MD5-sum will not contain characters special to JS like ' or \, or the </ sequence that ends a <script> block (against which the HEX_TAG argument is protecting), it's not the output template's job to know what the session token may contain. This is a safe way to output any string into a <script> block.

See this question for an approach to generating anti-XSRF tokens that requires no extra token-storage in the session or database.

Unfortunately, the holytext encourages to provide an option to disable the referrer (but you still can't invent your own mechanism of what a referrer is); so indeed, someone could disable referrers on his browser, thus denying himself access to your site.

Your solution is secure, but users can legitimately complain if your site only works with referrers enabled.

This is very sad because it now means that there is no sane way to ensure your site isn't secure against CSRF.

Alternative

The only other thing you can really do is put a nonce in each authenticated request to your web service. This however is somewhat dangerous because you have to make sure every request point on your web service validates the nonce. However, you can use a framework to do this for you, to somewhat migitate this annoyance. Stack Overflow itself seems to be using nonces.

However

Referrers in principle are a bit more secure because you can just apply a global rule that says no request can take place unless the referrer is in the same domain. This is acceptable if you are willing to drop users who disable referrers.

Contrary to what nonsense people are saying here, you can't issue an HTTP request with a spoofed header from non-privileged browser code. As expected, Flash was able to do this once to bypass anti-csrf that relies on referrer, but it was patched. Why was it patched? Because the HTTP RFC dictates what the referrer is, and thus, you are not allowed to change the meaning of it in your client code, lest your client be insecure.

Someone here even claimed that a Java applet can issue arbitrary headers over HTTP to another domain, but that is simply not the case because a sandboxed Java applet is not allowed to make requests to anything, except the domain on which it was loaded from. He quickly removed his comment before anyone could correct him...

Case in point

A web browser is an HTTP client. An HTTP client must conform to the HTTP RFCs; thus it has to adhere to what the RFC states a referrer header should look like. Since a web browser is an HTTP client, any application embedded in a web browser must not be able to make requests that violate HTTP protocol. The fact is that every violation of a standard is a potential security hole.

In any case: there is no proper way to determine weather a request is from your domain or an adversary's domain. This is just one of the many sad defects of the web. You must use one of these mentioned workarounds.

What nonsense! You say yourself Flash was able to do this once to bypass anti-csrf that relies on referrer, but it was patched. So what about the people who are using the unpatched version? You clearly do not understand what we are saying. ANY REQUEST CAN BE FORGED. The actual TCP packets could be manipulated... then what?
–
Nathan OsmanMay 16 '10 at 5:00

NO. Flash is known for it's constant stream of critical security flaws. And the rest of what you said is irrelevant.
–
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳May 16 '10 at 5:07

4

the forgery issue is less relevant than the fact that some proxies (or crazily configured browsers) will strip all referrers, as BalusC noted in a comment on the question. So if you demand a certain referer, you may be blocking legitimate users as well.
–
Jeff Atwood♦May 16 '10 at 6:21

@Long: You don't think that some people never update their software? What dream land are you living in? In theory it's secure, in practice if it's so important then there should be additional methods in place to secure it, namely use a SESSION and a unique auth_token in the form.
–
Josh KMay 16 '10 at 6:37

@Josh L, most browsers are coded in C, and have the occasional stack smash vulnerability and if not, semantic vulnerabilities. Using referrer is just as secure as an auth token. Jeff is correct, the only bad thing that can happen is the user gets blocked. It's just as likely for a browser to break referrer rules as it is for the browser to have some other vulnerability.
–
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳May 16 '10 at 7:18

Actually, you can use a dynamic address for that form. Like when user go to your form you forward him to a page with random address such as www.something.com/form.php forwarded to www.something.com/form.php?13mklasdkl34123 where 13mklasdkl34123 is randomly generated for each user and store it in $_SESSION. Upon receiving form submit, you can check the referrer address is the address you generated for the user. Referrer can be spoofed, but a dynamic referring cannot as the spoofer cannot know the address unless he (individually) visit your form page.

NO. 1. Referrer being spoofable is not an issue here. The only issue about the referrers solution is that it denies service to some users. 2. Your solution is flawed. If you do it this way, you MUST generate a new token for every request. This means each time you load a page, the server stores a token corresponding to your session id, and all links outputted by the server have that token in them. When you load a new page, the process is repeated. You do not use the same token for each request, or you can be owned in several ways such as screenshots and referrals to other sites.
–
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳May 16 '10 at 15:04

correction to my previous comment: There are actually ways to use a single token, but this is not one of them.
–
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳May 22 '10 at 13:24