6

I have hosted two domains on the same server, domain A and domain B.

Domain A will generate the unique access token to the content of domain B.

Domain A

<?php
  //http://php.net/manual/en/function.phpversion.php
  //echo 'Version of PHP: ' . phpversion();

  session_start();
  //$expiry_timestamp = time() + $expiry;
  //https://davidwalsh.name/random_bytes //https://secure.php.net/random_bytes
  //$token = bin2hex(random_bytes(64)); 
  $token = bin2hex(openssl_random_pseudo_bytes(64));
  //$time_token = 12000;
  //$time_token = srand(floor(time() / $time_token));
  //echo $token;
  $_SESSION['token']=$token;
?>

<html>
    <head>
    </head>

    <body>  
        <a href= "domainB.com/content1.php?token=<?php echo $_SESSION['token']; ?>">Content 1</a>
    </body>
</html>

The process of generating a token seems to be the right one, it has been easy to generate it.

Now comes my problem, how can I validate the generated token from domain A to domain B ?. The generated token must only be valid for the content that generated the token, the token must not be valid for other content, the token must be unique so that user can not share access to another user if it is not from his or her computer, the token must be valid only for 4 hrs of access after 4 hrs the token will no longer be valid to display the content must generate a new token to access again.

Can this process be done using a cookie without using a database?

Maybe identifying both domains A and B using a key, something like that

$APP_SECRET_KEY = "key code secret";
  • 1
    You don't want to use a database but can you create a file for example to check data? You generate token from domain A -> you create a file with user + token data -> domain B check this file -> after 4h you erase the file – Mickaël Leger Aug 10 '18 at 13:03
  • 1
    It's easy enough to pass the token from one domain to another, but you're going to need some kind of shared server-side resource (so it can't be tinkered with) accessible to both domains - that could be a shared fileserver if you're not using a database. – CD001 Aug 10 '18 at 13:03
  • @CD001can you explain me what I should modify on my server? –  Aug 10 '18 at 13:07
  • @MickaelLeger It looks as easy as you say, but how should that process be implemented :( –  Aug 10 '18 at 13:09
  • Where I work we use AWS s3 (https://aws.amazon.com/fr/s3/), it's really easy to create a file, upload a file, read a file or delete a file. I'm sure there is other process to do it but don't know them sorry :s – Mickaël Leger Aug 10 '18 at 13:21
  • It's hard to say without knowing your exact architecture - are you running both web servers on the same filesystem? For example, one Apache install running 2 VirtualHosts? If so, it should be simple to just save the token in `/tmp` (say using `file_put_contents()`). Then to ensure domain B is getting the correct token you could pass the parameter across as something like `token=asdasd:qweqwe123` where `asdasd` would be the (randomly generated) name of the file in `/tmp` and `qweqwe132` would be the token value (in the file). Bit rough round the edges but that's a basic premise. – CD001 Aug 10 '18 at 13:41
  • @CD001 this information serves you: I have hired a hostgator hosting, have the two domains used in that server, the structure is what I have in the question, is what I had in mind and I did, but the biggest problem is the validation. –  Aug 10 '18 at 16:25
  • why don't you save token into a file and read it on domain B? – lot Aug 14 '18 at 17:45

2 Answers2

8

Using a shared secret key is a good approach here.

I tend to use HMAC when I need to generate and validate a token (e.g.: E-Mail verification) and don't want to store it in a DB. Plus, HMAC is built in to PHP, so no library is needed here.

The idea is, on top of your data, you add a signature to verify that this token was created by your application on Domain A. You generate the token the same way again on Domain B to verify it.

Example:

Shared function to generate the token:

function buildVerificationToken($expires, $content)
{
    // Same function on both domains
    $APP_SECRET_KEY = 'key code secret';  // Maybe move that out of source code

    $tokenData = [
        'expires' => $expires, // Include it in signatur generation to prevent user from changing it in URL
        'content' => $content, // Create different token for different content
        'ip' => $_SERVER['REMOTE_ADDR'], // Identify the browser to make it not shareable. Best approach I could think of for this part.
    ];

    $serialized = json_encode($tokenData);

    return hash_hmac('sha256', $serialized, $APP_SECRET_KEY);
}

Generate the token on Domain A:

<?php
$expires = time() + (4 * 3600); // +4h
?>
<a href= "domainB.com/content1.php?expires=<?php echo $expires; ?>&token=<?php echo buildVerificationToken($expires, 'content1'); ?>">Content 1</a>

Verify it on domain B:

$providedExpires = (int) $_GET['expires'];
$providedToken = $_GET['token'];

$verificationToken = buildVerificationToken($providedExpires, 'content1'); // Build token the same way

if (!hash_equals($verificationToken, $providedToken)) { // hash_equals instead of string comparison to prevent timing attacks
    // User provided forged token, token for another content, or another IP
    die('Bad token'); // However you want to handle this
}

if (time() > $providedExpires) { // Check expiry time. We can trust the user did not modify it as we checked the HMAC hash
    die('Token expired'); // However you want to handle this
}

// User is allowed to see content1
Tobias K.
  • 2,997
  • 2
  • 12
  • 29
1

Json Web Token (JWT) seems to fit your requirements. Both applications use one secret key to exchange tokens with encrypted data one another.

Example use-case:

  • Let the secret key $secret="secret"
  • The raw data tells us the Unix timestamp when the token is generated (the iat field), the user id (the sub field) and the content id (the content field)
$data = [
  "sub" => "1234567890",
  "iat" => 1516239022,
  "content" => 1
];

Application A encodes the raw data with the secret key using HS256 algorithm ($token = jwt_encode($raw, 'HS256', $secret)). The output $token will be:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJjb250ZW50IjoxfQ.idM7d2fgmJVk3WjANwG-Gt6sY0lyE3eTvpKRpwITHRs

You can parse the token to see its content in the JWT home page.

The token is send to the application B. This application decodes the token with the same algorithm and shared secret key ($raw = jwt_decode($token, 'HS256', $secret)). The raw data will be available in the application B. This data can be used to validate the token:

  • Read the user id from sub field and check if it is correct
  • Read the content id from content field and check if it is correct
  • Read the timestamp when the token is generated from the iat field and check if it is in the last 4 hours.

There are several PHP libraries implement JWT for you.

Hieu Le
  • 8,288
  • 1
  • 34
  • 55
  • 1
    It is not possible to do it without using third-party libraries :( –  Aug 10 '18 at 16:22
  • 1
    You can encrypt your data to create a token yourself. The essential idea is that both application share one secret key, the key is used to encrypt the data . – Hieu Le Aug 11 '18 at 01:19
  • 1
    It would be possible for you to give me an example of how to validate the data using both keys written as indicated in your comment. –  Aug 11 '18 at 05:35