2

I am building a 2FA system for my website.

  • The login sits on the root at example.com

  • The 2FA script sits in example.com/2FA/verify.php

The verify.php is composed of 2 parts on the same page, separated by an IF THEN ELSE statement

  • Part1: enter email and send token.

  • Part2: after clicking send, same page refreshes and user can enter his token and click verify, then is redirected to authorized main page or not.

I am rying to restrict hotlinking and direct access to the verify.php For that purpose, i have tried to put this on top of my page:

$ref1 = $_SERVER['HTTP_REFERER'];
$ref2 = $_SERVER['PHP_SELF'];

if($ref1 !== 'https://example.com/' || $ref1 !== $ref2) {
  header('Location: https://example.com');
  session_destroy();
}

The direct access to verify.php works well, it gets redirected to the root. Unfortunately, i get the same result when the user enters his email and clicks send. The part2 of verify.php should appear, but gets redirected to the root.

How can i modify my top php snippet so that same page origin gets recognized and the script can complete ?

Regolith
  • 2,944
  • 9
  • 33
  • 50
John Kerby
  • 83
  • 7
  • Please see: [How reliable is HTTP_REFERER?](https://stackoverflow.com/questions/6023941/how-reliable-is-http-referer) So using it is this way is not recommended. – KIKO Software Aug 02 '19 at 06:36
  • Why exactly do you have problems with hotlinking or direct access? – KIKO Software Aug 02 '19 at 06:38
  • Please read the question. Direct access is the issue. First access to verify.php works fine, referrer gets checked correctly. But when the second part of the script on verify.php gets executed, instead of completing gets redirected to the root, because it sees the referrer this time as the page (verify.php) itself. – John Kerby Aug 02 '19 at 06:42
  • I have read the question. My first comment is to inform you that `HTTP_REFERER` is unreliable, and should not be used this way. The second comment is there simply out of curiosity: Why would you want to try to prevent direct access? Comments usually do not provide an answer to your question. – KIKO Software Aug 02 '19 at 06:46
  • I appreciate your comment and get your point. Now, how would you modify the snippet so the verify.php does not get redirected upon script exec reload ? – John Kerby Aug 02 '19 at 06:54

1 Answers1

3

What you can do is using a session to check that the client has visited another page of your site first.

On the starting index.php page, from which you allow access, you can put this:

<?php

session_start();

$_SESSION['verifyStatus'] = "AccessGranded";

then on the verify.php page you can do:

<?php

session_start();

if (!isset($_SESSION['verifyStatus']) || 
    ($_SESSION['verifyStatus'] != "AccessGranded")) {
     header('Location: https://example.com');
     die;
}

All this does though is check that the client has visited the index.php page before the verify.php page. It doesn't check that it is right before it.

To make sure that index.php was the previous page we need to expand a bit on this idea. I will use a random token for this. In the index.php you use:

<?php

session_start();

$_SESSION['verifyToken'] = bin2hex(random_bytes(16));

On this same page, when you link to verify.php you have to provide this token as a parameter. Like this:

echo '<a href="verify.php?token='.$_SESSION['verifyToken'].'">Go to verify</a>';

Then we the client gets to the verify.php page you have two versions of the token, one in $_SESSION['verifyToken'] and one in $_GET['token']. They must be the same. So in verify.php you can do:

<?php

session_start();

if (!isset($_SESSION['verifyToken']) ||
    !isset($_GET['token']) ||   
    ($_SESSION['verifyToken'] != $_GET['token'])) {
     header('Location: https://example.com');
     die;
}

And now it works, more or less, as required. Then when you go to verify.php again, you again have to use the token in the URL, just like I showed above, so that verify.php accepts the reload again.

On the second visit to verify.php you could reset the token in the session:

<?php

session_start();

unset($_SESSION['verifyToken']);

which means the client cannot access verify.php again, unless the index.phppage is used.

KIKO Software
  • 15,283
  • 3
  • 18
  • 33
  • Just implemented it. Works like a charm. Excellent answer, the logic is also the correct one. Thanks a lot for your effort. Please contact me per DM, i'd be happy to have further exchanges with you. Have a great day. – John Kerby Aug 02 '19 at 07:12