Log in

View Full Version : looking for alternatives to HTTP_REFERRER



james438
06-14-2013, 12:12 AM
What is a good alternatives to http_referrer?

I have an edit script on my site where the admin (almost always myself) can edit a page and submit the changes. The content to be edited may be found on one of two pages. After the information is submitted I have a bit of javascript+php that redirects back to the page that the content was being edited on. The redirect will go to the main page of my site or the archives page.

Recently my redirects stopped working. After some debugging I saw that $_SERVER[HTTP_REFERRER] was returning as blank. I thought this was an issue with my php.ini settings, but then I discovered that it was my browser (Opera) that had decided to turn it off at some point. I didn't know my browser could do that. Since it can I have decided to look into alternatives to HTTP_REFERRER.

After enabling it in my browser for the time being I did a small test with:


<script type="text/javascript">
document.write(location.referrer);
document.write(document.referrer);
</script>

resulting in:


undefinedhttp://www.mysite.com/testa.php
as opposed to
undefinedwhen Opera has "send referrer information" unchecked.

The offending code I was using that caused me to look into this in the first place was:


<script type="text/javascript">
window.location = "<?php print $url; ?>"
</script>

where $url was empty. Is there any error in the small snippet of code above? The above, if echoed, is causing my server to shut down for approximately 5+ minutes, which is silly. I could contact my hosting service, but I figure if I can debug this on my own with an alternative to HTTP_REFERRER I'll forget contacting my hosting service. They are not nearly as useful in customer service as they have been.

djr33
06-14-2013, 12:47 AM
There isn't an alternative, not one that works like that. It's optional information that may not be provided by the user (or might not be known to the server, but I think that's rare).

Try <?php print_r($GLOBALS); ?> or (simpler, but might miss something) print_r($_SERVER);
Once you see that output, you'll know what you have to work with. Assuming you can't change the server/PHP configuration (and assuming your browser is behaving normally, which it probably is), what you see is what you have to work with.

If you want to come up with an alternative to this, I can think of two possibilities:
1. Store and pass along the page's URL when loading a new page-- this works with a link (add a variable in the URL) or with a form (add a hidden field). It only works with links and forms, though-- not something like the back button or manually typing a URL (which is probably not a problem for you). It also requires somehow dynamically building all links/forms, but that's mostly just cutting-and-pasting. This again puts the burden on the browser client-side, so that is good and bad, probably more good than bad in this case.

2. Track everything serverside. Use sessions and store $_SESSION['lastpage'] = $_SERVER['REQUEST_URI'];
That will allow you to track the current page, which will then be available until the next page. So, above that you can add:
echo 'Last page: '.$_SESSION['lastpage']; (Or whatever you'd want to do with it; and obviously you'll need to check that it's set-- it won't always be, at least not until your second page load.)
The result here is that you'll be tracking server-side activity-- loading any page counts as the "last page", so it doesn't matter what tab it's in-- it just matters what the last requested URI was. So if you use multiple tabs, this would not work well at all. But it's simple, so especially if you're using this yourself (only), maybe that would work.


Note that in both cases you don't need to necessarily store a URL. Depending on your ultimate goal, it might be simpler (or more useful) to store something like the subdomain or the current directory, or simply a binary value for "in admin CP" or "not in admin CP", or whatever you need.

james438
06-14-2013, 01:34 AM
I like option 2.

print_r($_SERVER); had somewhat interesting results. In Opera if I go to Settings --> Preferences --> Advanced --> Network --> uncheck "send referrer information" $_SERVER[HTTP_REFERRER] will be absent. I have since rechecked "send referrer information" as a temporary fix until I get my pages rewritten.

djr33
06-14-2013, 04:24 AM
That will work out well, assuming you always only use one page at a time (or if those multiple pages are equivalent in the relevant sence-- if you're only using the Admin CP, for example, and that's all you check about the referrer, then that won't cause any problems-- unless you were to preview the regular site at the same time). You could also add some kind of time limit, such as checking that within the past 5 minutes you have visited a relevant page. But that would require (obviously) a slightly different kind of code-- probably checking if a session variable $_SESSION['I_have_been_to_page_X'] has been set by Page X.

One minor note: there's a(n official) typo in the PHP name for "referrer". it's HTTP_REFERER. If you ever spell it correctly, the value will not be defined. So that's odd. In your post, you did spell it correctly (thus incorrectly for PHP; that's never going to be defined); if you did the same in your script, that's the problem, but it sounds like you are sometimes getting results, so you probably know that. (Personally I know this is an issue and I just double-check every time I write the variable name. A quirk of PHP...)



Oh, and by the way, there is a third option:
Check that the referrer is NOT equal to some particular value. In other words, allow either the correct value or no value (not sent). This would not work as any kind of security (you shouldn't rely on the referrer for security anyway!!), but it would help you to guess that some users/visits don't correspond to what you're looking for. Assuming mostly cooperating visitors, that could be a simple solution. But if you know your browser doesn't work, and this is mostly for you, then it would be logically equivalent to not including it in the first place! But it might help someone (or you, if things change).

traq
06-14-2013, 04:27 AM
I like option 2.
As do I. It is far more reliable, and also gives you another opportunity to combat XSRF attacks (by confirming that you "really did" just give a user a copy of that form to fill out, for example).

And yes, the referer header is entirely optional, and users can send any value they wish (it's just a matter of knowing how to do it).


$_SERVER[HTTP_REFERRER]
Also, I don't know if you're spelling it that way intentionally or not, but keep in mind that (for "historical reasons (https://tools.ietf.org/html/rfc1945)") it is correctly misspelled "REFERER" (and, therefore, it is perfectly normal for $_SERVER['HTTP_REFERRER'] to be empty).

whoops, cross-post!

Daniel: it's not PHP's misspelling; the HTTP spec actually defines it that way - link to the RFC above. :)

djr33
06-14-2013, 05:35 AM
Daniel: it's not PHP's misspelling; the HTTP spec actually defines it that way - link to the RFC above.Ah. Well, it's someone's misspelling [abbreviation?], and PHP is going along for the ride I guess :)

traq
06-14-2013, 06:36 AM
yup... no one caught it until it was too late to correct, so now, "that's how you spell it"

james438
06-14-2013, 06:42 AM
I misspelled it in my post. In my code it is spelled correctly. The HTTP_REFERER has nothing to do with security and is only involved with a redirect to the page where the content was originally being viewed from for convenience. In almost all (and quite possibly all) circumstances this is only used by the admin, me.

djr33
06-14-2013, 07:43 AM
The HTTP_REFERER has nothing to do with security and is only involved with a redirect to the page where the content was originally being viewed from for convenience.In this case, I think the technically best solution would be my (1) from above, by submitting the current page along with the rest of a form. However, that might not be easy to integrate into your system, and you might not need 100% reliability.
(Alternatively, something I often do is simply submit to the same page I'm currently viewing. This means integrating the receiving script into that page's code, but usually that's relatively easy with an include() that processes the form, and can be included into any page, such as the one you're on.)