Allow access from specific referer only in .htaccess

I have a website with a wordpress installation and I'm using modrewrite to display clean urls. I want to protect a page (/thank-you-for-your-purchase/) with .htaccess so that if the page is linked to from my shopping cart system (1shoppingcart.com), the user can see the page. If anyone else tries to access the page, they will be denied access.

I tried the following:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/thank-you-for-your-purchase/?(.*)$ [NC]
RewriteCond %{HTTP_REFERER} !^http://www\\.mydomain\\.com/(.*) [NC,OR]
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/(.*) [NC]
RewriteRule ^.*$ http://www.mydomain.com/ [R=301,L]
</IfModule>

The above almost worked as expected; when 1shoppingcart.com (mcssl.com) sent the user to mydomain.com/thank-you-for-your-purchase/ the user would be denied access and sent to the home page. Is this because 1shoppingcart.com is doing a redirect, therefore the referer is blank?

Please help!

mar,

<font color='"Red"'><IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /</font>
RewriteCond %{REQUEST_URI} ^/thank-you-for-your-purchase<font color='"Red"'>/?(.*)$</font> [NC]
RewriteCond %{HTTP_REFERER} !^http://www\\.mydomain\\.com/<font color='"Red"'>(</font>.*<font color='"Red"'>)</font> [NC,OR]
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/<font color='"Red"'>(</font>.*<font color='"Red"'>)</font> [NC]
RewriteRule ^.*$ http://www.mydomain.com/ [R=301,L]
<font color='"Red"'></IfModule></font>

Do you really need the red lines? (Don't guess, the answer's NO!)

Now, I'm sure that you know that {HTTP_REFERER} is infamous for it's lack of reliability. If you need to use it ... so be it.

RewriteCond %{HTTP_REFERER} !^http://www\\.mydomain\\.com/.* [NC,OR]
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/(.*) [NC]
RewriteRule ^/?thank-you-for-your-purchase http://www.mydomain.com/ [R=301,L]

Now, if that doesn't work because mcssl.com's redirecting, WHERE is it redirecting from? Yes, run a test and capture the $SERVER['HTTPREFERER'] value and use that in place of mcssl.com.

Regards,

DK

DK,

RewriteCond %{HTTP_REFERER} !^http://<font color='"Red"'>www\\.</font>mydomain\\.com/.* [NC,OR]
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/<font color='"Red"'>(</font>.*<font color='"Red"'>)</font> [NC]
RewriteRule ^/?thank-you-for-your-purchase http://<font color='"Red"'>www.</font>mydomain.com/ [R=301,L]

From your code, I removed what is in red above.

Something doesn't seem to be working right. I created a test file at mydomain.com/test.html with a link to /thank-you-for-your-purchase/ and I was redirected to mydomain.com. It should NOT redirect me because the referer is from mydomain.com. Am I right?

Is there a more reliable way to do this or do I still have something wrong?

mar,

RewriteCond %{HTTP_REFERER} mydomain\\.com/ [NC,OR]
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/ [NC]
RewriteRule ^/?thank-you-for-your-purchase index.php [R=301,L]

You don't need to match the entire {HTTP_REFERER} string in the RewriteCond statements, just match the code in the regex part of the statement. THEN, the RewriteRule's redirection was an absolute URL which MUST be displayed.

Now, what is the {HTTP_REFERER} for mcssl.com?

Regards,

DK

The referer for mcssl.com is:
https://www.mcssl.com/app/postAuthorize.asp?MerchantID=11111&OrderID=d52670781f6cce5&ClientID=11111111&IP=111.111.111.111&adid=0

The referer for my test page would be:
http://mydomain.com/test.html

I added some comments to the code so it's easier for me to explain what I THINK is going on.

# If referer does not contain mydomain.com, or
RewriteCond &#37;{HTTP_REFERER} <font color='"Red"'>!^</font>mydomain\\.com/ [NC,OR]
# If referer does not contain https://www.mcssl.com, then
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/ [NC]
# Send to homepage instead of mydomain.com/thank-you-for-your-purchase/
RewriteRule ^/?thank-you-for-your-purchase index.php [R=301,L]

I tested this from the test page above and it still sends the user to the homepage, even though the referer contains mydomain.com

Could the wordpress rewrite be messing things up even though it comes after the above code?

Here's what the wordpress rewrite looks like:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

I also just noticed that on a meta refresh, the referer is blank. In 1shoppingcart.com, it sends the user to the thank you page with the following meta refresh:

<meta http-equiv="REFRESH" content="3; url=http://mydomain.com/thank-you-for-your-purchase/">

Of coarse it provides a link to "Continue" if the page doesn't refresh, but no one is going to click the continue link immediately.

The whole point of this was to ensure that the thank you page could only be visited when the user was being sent from 1shoppingcart.com (mcssl.com), this way I could be certain my conversion statistics were as accurate as possible.

Am I S.O.L.?

mar,

The referrer for mcssl.com is: https://www.mcssl.com/app/postAuthorize.asp. The rest is the query string which will contain specific information relating to that purchase (and your ID - go back to your post and delete the query string but REMEMBER the contents of that string so you can do something useful with it).

The referrer for my test page would be: http://mydomain.com/test.html

# If referer does not contain mydomain.com, or
RewriteCond &#37;{HTTP_REFERER} !<font color='"Red"'>^</font>mydomain\\.com/ [NC,OR]
[INDENT]The start anchor, the ^, says that it has to BEGIN with mydomain which it will NOT if there is a www. in front.  DELETE the ^.[/INDENT]
# If referer does not contain https://www.mcssl.com, then
RewriteCond %{HTTP_REFERER} !^https://www\\.mcssl\\.com/ [NC]
[INDENT]Ditto - except that they likely require the www for their secure server certificate to work.[/INDENT]
# Send to homepage instead of mydomain.com/thank-you-for-your-purchase/
RewriteRule ^/?thank-you-for-your-purchase index.php [R=301,L][INDENT]That should work fine.[/INDENT]

Was this with the www?

Absolutely!

Aw, except for the extraneous @#$%, this merely tells apache that, if the URI does not match a file or directory, send the request to the Home Page.
[INDENT][ :mad: rant :mad: ]
The extraneous @#$% explained:

<IfModule mod_rewrite.c> - this is to protect against a 500 error if you don't have mod_rewrite enabled. If you do, it's YOUR responsibility to remove it ... and the </IfModule> closing tag. If you don't, you have no business playing with code (IMHO).

RewriteEngine On - this is ONLY necessary if you'd "commented out" a block of mod_rewrite code with RewriteEngine off. Did you? I thought not! DELETE!

RewriteBase / - that's to tell mod_alias where to find your files. Since that says that they're where they're supposed to be, WHY make Apache work harder EVERY TIME A FILE IS REQUESTED by telling it what it already knows?

The message is to be a good webmaster and know what your code is doing (and get rid of the garbage).
[ :x /rant :x ][/INDENT]
Now, back to the problem. Is that meta refresh code in your page (on your site) or theirs? What {HTTP_REFERER} do YOU see (on your site's return landing page)? Nothing? Tell the mcssl people to change your return landing page to assign a query string (which should get passed along) OR put it into YOUR landing page's meta refresh statement.

Regards,

DK

Hi David,

The wordpress rewrite does this: every request (that's not a real file or directory) gets sent to the request processing script index.php (NOT the homepage, it just happens to be the DirectoryIndex). Index.php then obtains the request from the env var PATH_INFO and loads the right stuff to process the request and prints the result out. It is a correct rewrite and not a wrong one.

marbie,

If referer does not contain mydomain.com, or

RewriteCond %{HTTP_REFERER} !^mydomain\.com/ [NC,OR]

If referer does not contain https://www.mcssl.com, then

RewriteCond %{HTTP_REFERER} !^https://www\.mcssl\.com/ [NC]

Send to homepage instead of mydomain.com/thank-you-for-your-purchase/

RewriteRule ^/?thank-you-for-your-purchase index.php [R=301,L]

Your misunderstanding of HTTP_REFERER is what's causing the problem here.

HTTP_REFERER looks like this: protocol://domain/blah.

e.g http://domain.com/sillybiz

Your first HTTP_REFERER rewritecond checks if the HTTP_REFERER starts with "mydomain.com". That will never happen, because it starts with http://mydomain.com.

The other misunderstanding of HTTP_REFERER is that you are using it as a trusted source. I hope you don't have anything important on that thank-you page because HTTP_REFERER is modifiable on the clientside. Google "How to spoof HTTP_REFERER" if you're curious.

Actually, if I delete that, I get a Not Found error with wordpress.

I removed that, but I needed to include the / below:

RewriteRule ^/?thank-you-for-your-purchase <font color='"Red"'>/</font>index.php [R=301,L]

It's on their site.

Yes, it's blank when it refreshes.

Well, I don't think I could get 1shoppingcart.com to assign a query string.

Thank you so much for your help!

@ Lin

Thanks for your input. The rewrite is working but I think wordpress is keeping me from catching the actual referer. I think the only way to solve this would be to modify wordpress so I could store the correct referer as a global variable and then pass that when wordpress loads and prints the result.

mar,

From your response, I have to think that you didn't post all the mod_rewrite in your DocumentRoot's .htaccess. No worries, though, and I thank you for giving those bits a go as they are correct (without intervening code, changes in directory level, etc, which were not evident).

My suggestion about the meta refresh was based on my Authorize.net experience where I needed to register a return landing page with them (perhaps in the POST codes you're sending?). With Authorize.net, it would be a simple matter to add a query string which should not be lost when they POST their return codes. I'm sure that they all do it differently so ... can you check the return codes (they should at least indicate success and a transaction code and, as LinhGB pointed out, that would be FAR MORE RELIABLE than {HTTP_REFERER}).

I guess the whole thread boils down to WHY you'd use the unreliable {HTTP_REFERER} to do what appears to be an important check. Best to find another way - like capturing and examining the contents of the POST codes sent back by mcssl.com to your secure server.

Regards,

DK

Just remember that REFERER can be set to anything at all by your visitors and a growing number of people are setting it to be always blank so as to increase the securitry/privacy of their system.

Yea, I've now learned how unreliable HTTP_REFERER is.

DK, thanks for your pointers. I'm looking into seeing if I can get the POST codes from 1shoppingcart.com.