6

I want to detect whether or not a user is viewing a secure page and redirect if not (for logging in).

However, my site travels through a proxy before I see the server variables and the proxy (right now) is telling me that $_SERVER['HTTPS'] is 'on' when the URI clearly indicates otherwise. It also shows 'on' when the user is navigating 'securely'.

Navigating through http:// and https:// both output that $_SERVER['SERVER_PORT'] = 443.

I don't have the ability to make any changes to the proxy so I want to know:

  • Does PHP have any other options for me to detect the truth or...
  • Am I stuck to resort to JavaScript's mechanisms for detection and redirection.

I mined this question for ideas but they mostly revolve around the $_SERVER['HTTPS'] variable being trustworthy. Bah!

It appears that this question is experiencing at least something similar, but s/he was able to resolve it by adapting an apache solution.

Are there any other PHP SERVER variables or tricks available to detect what the user's URI begins with? The only difference between the $_SERVER variables when my site is viewed http versus https are the following:

  • _FCGI_X_PIPE_ (appears random)
  • HTTP_COOKIE (sto-id-47873 is included in the non-secure version but I did not put it there)
  • REMOTE_ADDR (This and the next two keep changing inexplicably!)
  • REMOTE_HOST
  • REMOTE_PORT ('proxy people', why are you continually changing this?)

Are any of these items strong enough to put one's weight upon without it splintering and causing pain later? Perhaps I shouldn't trust anything as filtered through the proxy since it could change at any given time.

Here is my plan to use JavaScript for this purpose; is it the best I have?

function confirmSSL() {
    if(location.protocol != "https:") {
        var locale = location.href;
        locale = locale.replace(/http:\/\//,"https://");
        location.replace(locale);
    }
}
<body onLoad="confirmSSL()">...

I think if the user has JavaScript disabled in my community, then they hopefully know what they are doing. They should be able to manually get themselves into a secure zone. What sort of <noscript> suggestions would be commonplace / good practice? Something like this, perhaps?:

<noscript>Navigate using https://blah.more.egg/fake to protect your information.</noscript>

PHP solutions that work (with good explanation) will be given preference for the correct answer. Feel free to submit a better JavaScript implementation or link to one.

Many thanks!

Community
  • 1
  • 1
veeTrain
  • 2,915
  • 2
  • 26
  • 43
  • Only JS thing I could think of is to use `/^http:\/\//` as your regex, just to make sure it matches the protocol at the beginning of the string (in case it's anywhere else as well, like a "redirect to" param – Ian Feb 07 '13 at 20:41
  • And if the proxy intercepts and alters the request before it gets to your app, then I can't see any way for you to reliably detect its true nature. – Ian Feb 07 '13 at 20:45
  • 1
    I would put the redirection function as topmost as possible in your document or rather execute it as early as possible. It certainly doesn't need to wait for the body to load. – MCL Feb 07 '13 at 20:46
  • Thanks Ian; great thought about the `^`! MCL, what event would you suggest? onbeforesomething? – veeTrain Feb 07 '13 at 20:47
  • 3
    What sort of proxy is it? That sounds just wrong, so if it is not misconfigured, a complaint/bug report to the manufacturer/project would be a good idea, even if you can't change it. That said, make sure the proxy administrator at least knows about the problem - I've found that config changes can occasionally happen even in the most change-averse corporate environments! – halfer Feb 07 '13 at 20:48
  • 2
    @veeTrain I would just put it directly under the head tag and call it immediately. I don't see any reason why you should bind this to a concrete event. – MCL Feb 07 '13 at 20:49
  • @MCL; sounds great. I'll definitely take that into consideration. – veeTrain Feb 07 '13 at 20:49
  • @halfer; sorry, my knowledge of the proxy is still just that it is called 'proxy' and that it intercepts my web traffic first and generates a 'standard' URI rather than displaying my particular server name. I am looking / trying to contact someone 'on the inside' and your advice will be glad to have when I find them. Thanks! – veeTrain Feb 07 '13 at 20:52
  • And if you haven't already, make sure you have a solution for Javascript being disabled. Not sure how much of your login and/or website uses Javascript, but relying on Javascript isn't always the best. If you didn't know, you could look into ` – Ian Feb 07 '13 at 20:56
  • @Ian; thanks for the idea. I updated my question with my assumptions about the non-JS crowd but I will probably take your advice as well. Thanks! – veeTrain Feb 07 '13 at 21:06
  • 1
    If you are able to collect some request data, you could also consider optimizing your output for the most probable case. If the number of initial non-SSL requests is higher, it would be beneficial to first send a page that **only** contains some JS and brutally redirects everyone to the SSL location. If the number of initial SSL requests is higher, you could always send your payload right away and redirect only if necessary. – MCL Feb 07 '13 at 21:14
  • Excellent idea, @MCL; my page is so light and volumes so low that it might not be worth it (in this case) but I might come back to this idea in the future – veeTrain Feb 07 '13 at 21:19
  • Also, why don't you just provide a link to the SSL location in your ` – MCL Feb 07 '13 at 21:21

3 Answers3

3

Although already partially discussed in the question's comments, I'll summarize some suggestions concerning the redirection logic in JavaScript:

  1. Generally using window.location instead of location is advisable, an explanation can be found here.
  2. Regex seems like a bit of an overkill for a simple replacement of the protocol.
  3. The redirection logic should be executed as soon as possible, because in the event of redirection, every additional document processing is unnecessary.
  4. Browsers with JavaScript disabled should at least show a notification prompting the user to switch to https.

I suggest using the following code (adopted from here), which is short and efficient:

<head>
    <script type="text/javascript">
        if (window.location.protocol != "https:") {
            window.location.href = "https:" + window.location.href.substring(window.location.protocol.length);
        }
    </script>
    ...
</head>
<body>
    ...
    <noscript>Please click <a href="https://my-cool-secure-site.com">here</a> to use a secure connection!</noscript>
    ...
Community
  • 1
  • 1
MCL
  • 3,985
  • 3
  • 27
  • 39
1

Just use the client-side approach. If your proxy is non-configurable, then that option is out. Detecting and redirecting via js is fine.

Geuis
  • 41,122
  • 56
  • 157
  • 219
1

There is also a way to achieve the redirection without client side javascript. This method can be especially helpful, if JavaScript is disabled in the client's browser.
The steps are pure PHP and pretty simple:

  • Start a session
  • If it's a fresh session, redirect to the https location
  • If the session isn't new, it can be assumed, that the user has been redirected

Example code:

<?php
    session_start();
    if( !isset($_SESSION['runningOnHttps']) ) {
        $_SESSION['runningOnHttps'] = true;
        header('Location: https://my-cool-secure-site.com');
    }
?>

Naturally, you could restrain this functionality to those browsers with JavaScript disabled in order to create a kind of 'hybrid mode': Whenever there is a new session with a non-JS browser, make a request to some kind of callback script that notifies the server to send a location header:

some_landingpage.php sends an initial <noscript> containing a hidden iframe, which will load redirect.php:

if( !isset($_SESSION['checkWasMade']) ) {
    $_SESSION['checkWasMade'] = true;
    echo '<noscript>
            <iframe src="redirect.php" style="visibility: hidden; position: absolute; left: -9000px;"></iframe>
          </noscript>';
}

A request to redirect.php will let you know, that JavaScript is disabled and give you the chance to force redirection by sending a Location header (see above) with the next actual request.

As a matter of course, this method will only work reliably, if the protocol won't change (magically?) during one session.

UPDATE:
All of the above-mentioned method for handling non-JavaScript user agents could be rendered moot by an even neater approach:
I just learned, that <noscript> can also be included inside the <head>, which allows one to just redirect via <meta> tags.

Hence, some_landingpage.php could send an initial meta-refresh inside <noscript>:

// The following echo must appear inside the html head
if( !isset($_SESSION['checkWasMade']) ) {
   $_SESSION['checkWasMade'] = true;
   echo '<noscript>
            <meta HTTP-EQUIV="REFRESH" content="0; url=https://my-cool-secure-site.com"> 
        </noscript>';
}
MCL
  • 3,985
  • 3
  • 27
  • 39