PHP: Secure a Rest Service with a Token mixed with Timestamp - php

I have a rest service that my website calls it and I want to secure it from calling outside of my website as much as possible.
I want to create a token mixed with timestamp, so the user can only call the service in 10 minutes (for example) with the token that generated in the server.
Let me explain it with some pseudo codes:
1) Server: The token is generated in the server using a private key and timestamp:
// The token is valid only for 10 minutes after 'time'
$token = encrypt($pKey, timestamp); // Server Time
2) Client: We put the token in the javascript variable and use it in our request with the timestamp of the client:
var token = '<?= $token ?>';
var params = {
token : token,
time : timestamp, // Client Time
data : mydata
}
3) Server: If the time parameter mixed with token is not equal to 10 minutes token, the request is invalid:
// I'm Stuck Here
$something = decrypt($pKey, $_POST['token'], $_POST['time']);
if ($something != $tenMinutes) { // Invalid Request }
The Question:
1) Is this senario O.K? If Yes, What is the exact solution? If No, So What is the solution?
2) There is another senario to secure the requests that I've seen in the AWS: http://docs.aws.amazon.com/AmazonS3/latest/dev/S3_Authentication2.html
How can I implement this in PHP?

After you've got token from the client you need to check two things: validity of the token and its timestamp.
There are two scenarios:
Make timestamp part of the token:
function getToken($timestamp) {
return $timestamp . encrypt(getPKey(), $timestamp);
}
$token = genToken(time());
And then validate it:
$token = $_POST['token'];
function validate($token) {
$timestamp = substr($token, 0, 10);
return
(genToken($timestamp) == $token)
&& ($timestamp >= time() - 600);
}
Save generated token in database along with timestamp and after getting token from the client check corresponding timestamp.

Related

Php: How to store a token and retrieve it for comparing?

Hi I am creating a token to compare against some record on the database to log a user in. I am using token = openssl_random_pseudo_bytes to set this random string, but how its stored on the browser and database seem to be different. How do I compare these two strings?
Set the value token first using a cookie way
$value = "23asdoq23ARDAsdadq2";
// cookie will expire when the browser close
setcookie("tokenCookie", $value);
// cookie will expire in 1 hour or set it to your needs
setcookie("tokenCookie", $value, time() + 3600);
or set the duration to 0, so that cookie duration will end only when users browser is close
setcookie("tokenCookie", $value, 0);
then retrieved the cookie and compare selected token value from the database with the cookie token.
if(!isset($_COOKIE['tokenCookie'])) {
$tokenCookie = $_COOKIE['tokenCookie'];
// Assume you already retrieved the token value from the database and stored it to a variable then you just need to compare it..
if( $tokenCookie == $token_value_from_database ){
echo true;
}else{
echo false;
}
}

REST API security design

I have made an PHP rest api. I want to connect to the API via an IOS and Android app. But I don't know how to secure everything.
I register the device at my database when the app fires for the first time
Table devices:
id random enabled
1 12345 1
Every device has an id and an random. The random value is unique in that table. The actual device receives the id and random value.
What I have right now:
I validate each request at the php side:
private function validateUrl(){
$url = $_SERVER['REQUEST_URI'];
$signature = isset($_GET['signature']) ? $_GET["signature"] : null;
$url = str_replace('&signature=' . $signature, '',$url);
$url = "" . $url;
$correctSignature = md5($url . "TNynVX9k2HqYSXnd");
if($signature != $correctSignature){
echo die(json_encode([array('status' => "not valid")]));
}
}
The request at (in this case) the IOS side:
private func random () -> Int {
var result = "";
for _ in 1...3 {
let randomNumber = arc4random_uniform(99)
result += String(randomNumber);
}
return Int(result)!;
}
private func md5(string string: String) -> String {
var digest = [UInt8](count: Int(CC_MD5_DIGEST_LENGTH), repeatedValue: 0)
if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
CC_MD5(data.bytes, CC_LONG(data.length), &digest)
}
var digestHex = ""
for index in 0..<Int(CC_MD5_DIGEST_LENGTH) {
digestHex += String(format: "%02x", digest[index])
}
return digestHex
}
func createUrl(url : String) -> String {
var newUrl = url;
newUrl += "?&random=\(random())";
let secret = "TNynVX9k2HqYSXnd"
let signature = md5(string: newUrl + secret)
newUrl += "&signature=" + signature;
return newUrl;
}
This works great but as you see I have an static API key. Something where I have my concerns about. So I thought maybe I can create an API key based on the id and random from my database. Is that more secure?
Something like:
func createUrl(url : String) -> String {
var newUrl = url;
let signature = md5(string: [device id here] + [device random here])
newUrl += "&signature=" + signature;
newUrl += "&deviceId=" + [device id here];
return newUrl;
}
And at my PHP side I can get the deviceId property from the url. Compare it to the database, retrieve the id and random value. MD5 them. Compare that to the signature. And when there is an match it's ok. Otherwise not. Is that an solid implementation?
Or in simple terms. Can I replace the api key with the combination of id + random?
Looks like you are using the same secret client side and server side, which can be an issue since anyone who has access to the *.apk or *.ipa could disassemble it and find the token, which is pretty easy with open source tools (https://github.com/iBotPeaches/Apktool). Especially on Android where the APK is pretty much a jar with some other assets. Are you generating the random token client side? Since it looks to be an int, that's only 32 bits of entropy, not enough to be secure.
Usually, the token is created server side rather than client side. There are many different ways of doing this. One is a JSON Web Token (JWT) which basically encodes data like an id or expires data into a token which is signed with a private key. Only the server knows the private key, and thus it's the trusted source for creating them, but others can have access to a public key which can be used to verify the token.
If you don't want to deal with JWT's and signing, a second option is just opaque tokens that are created server side. The key is a large amount of entropy like a large UUID.
There is a lot of information on asymmetric algorithms that generate a mathematically linked public/private key pair.
You can read more here: https://www.moesif.com/blog/technical/restful-apis/Authorization-on-RESTful-APIs/

How to renew access token with OAuth Refresh token?

I'm creating simple app for Uber.
And I've already solved 3 steps from this API: https://developer.uber.com/docs/authentication
But now I want to refresh token (step 5).
I receive access_token, refresh_token, expires_in values, and I can't understand how to set up timer to refresh user's token with refresh_token when the expires_in time expire.
Here I provide my example of code, where I want to renew access token with refresh_token.
<?php
session_start();
require_once 'uber_b.php';
require_once 'config.php';
if(isset($_GET['code'])) {
// try to get an access token
$code = $_GET['code'];
$url = 'https://login.uber.com/oauth/token';
/*
* Create row for function setPostData (uber_b.php)
* All of this rows will be used to build our request
*/
$params = array(
"code" => $code,
"client_id" => $client_id,
"client_secret" => $client_secret,
"redirect_uri" => $redirect,
"grant_type" => "authorization_code"
);
//create example of class
$request = new HttpPost($url);
//Connect this class with our settings
$request->setPostData($params);
//Send our request to Uber
$request->send();
//Receive response
$responseObj = json_decode($request->getHttpResponse());
//Execute parameters from answer
$user_token = $responseObj->access_token;
//Refresh token
$refresh_token = $responseObj->refresh_token;
//Time for token
$expires_in = $responseObj->expires_in;
echo "User's token: " . $user_token;
echo "<br>";
echo "Refresh token is: " .$refresh_token ;
echo "<br>";
echo "Time: ".$expires_in;
echo "<br>";
echo "<a href='order.php'>Order a car</a>";
}
//Refresh token
if(isset($responseObj))
{
$exp_time = time()+2592000;
try {
//insert into database
$stmt = $db->prepare('INSERT INTO token2 (exp_time)
VALUES (:exp_time)
');
$stmt->execute(array(
':exp_time' => $exp_time
));
} catch(PDOException $e) {
echo $e->getMessage();
}
}
if(time() >= $exp_time)
{
//Parameters for Uber refresh token request(step 5)
$r_params = array(
"client_id" => $client_id,
"client_secret" => $client_secret,
"redirect_uri" => $redirect,
"grant_type" => "refresh_token",
"refresh_token" => $refresh_token
);
$r_request = new RefreshToken($url);
$r_request->setPostData($r_params);
$r_request->send();
$refresh = $refresh_token;
$r_responseObj = json_decode($r_request->Refresh());
echo "New Refresh token: " . $r_responseObj->refresh_token;
echo "<br>";
}
?>
As you see, I don't provide timer function, because I don't understand how to make it right.
So, can you explain me how to correctly renew access_token using refresh_token?
Maybe I have to use setcookie(), or something else to solve this task?
First of all, according to the STEP FIVE: REFRESHING TOKENS from the Authentication docs, to refresh the access token you need to HTTP POST the following parameters:
client_secret=YOUR_CLIENT_SECRET
client_id=YOUR_CLIENT_ID
grant_type=refresh_token
redirect_uri=YOUR_REDIRECT_URI
refresh_token=REFRESH_TOKEN
to the https://login.uber.com/oauth/v2/token API endpoint
You are also sending the code parameter, which means you will receive an error because of this additional parameter which fails to validate on Uber's server.
The error you get will be
HTTP/1.1 401 UNAUTHORIZED
{"error": "invalid_grant"}
To be able to check the token validity, after you've exchanged the code with the access token, you need to:
Persist somewhere the "access_token", "expires_in", "refresh_token" and a UNIX timestamp in seconds, representing the current time and date, let's call it "itime".
The location where you can store this information is multi varied: session, file, database, memory cache, etc., depending on how you want to handle token expiration and automatic user login if you already have their token saved and it's not expired, then you don't have to make the user login again.
Before each HTTP request that you make to the Uber API you first check that the access token hasn't expired by retrieving the "itime" and "expires_in" from the storage location for the access token of the user and making sure that the time() < "itime"+"expires_in" holds true.
If the condition is false, then you need to refresh the access token.
Keep in mind that refreshing the access token also changes the refresh token, so you need to replace the current "access_token", "refresh_token" and "expires_in" for the current user identified by the uuid.
Given that the current expiration time for an access token is 30 days (2592000 seconds) I'd suggest that you store the access token data inside a database table, because PHP sessions are short lived (by default session.gc_maxlifetime is 1440 seconds/24 minutes in php.ini) and the user will not stay on your app/website all that time either.
Moreover, checking the expiration time is not enough if you allow the user to login to Uber from multiple devices/places, because after each user login all the previous access/refresh tokens are invalidated. So, after the user signs in on the second device you might have an invalid access/token with a valid expiration time on the first device.
In this scenario, to determine if the access token is still valid I'd suggest you make an additional HTTP request to the GET /v1/me endpoint using the access token. If it works, the token is still valid, otherwise it expired/was invalidated, and you need to request a user re-login on the first device.
tl;dr You don't need a timer, you need to check before each HTTP request to the Uber API that the access token is still valid.
To do this, you need to persist the token details + UNIX timestamp.

GET Json API Results

I searched for this but most of the questions related to this are for API's with other services.
I'm building an API that allows game developers to send and retrieve user info from my database.
I was finally able to put together the API, but now I need to call the API.
1st when the game initiates, it sends us the game developers key their developer id and game id.
//Game loads, get developer key, send token and current high score
// == [ FIRST FILTER - FILTER GET REQUEST ] == //
$_GET = array_map('_INPUT', $_GET); // filter all input
// ====================================== //
// ============[ ACTION MENU ]=========== //
// ====================================== //
if(!empty($_GET['action']) && !empty($_GET['user']) && !empty($_GET['key']) && !empty($_GET['email']) && !empty($_GET['password'])): // if key data exists
switch($_GET['action']):
//athenticate game developer return and high score
case 'authenticate':
$db = new PDO('mysql:host=localhost;dbname=xxxx', 'xxxx', 'xxxx');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$st = $db->prepare("SELECT * FROM `game_developers_games` WHERE `id` = :gameid AND `developer_id`=:user AND `key`= :key AND `developer_active` = '1'"); // need to filter for next auction
$st->bindParam(':user', $_GET['user']); // filter
$st->bindParam(':key', $_GET['key']); // filter
$st->execute();
$r = $st->fetch(PDO::FETCH_ASSOC);
if($st->rowCount() == 0):
$return = array('DBA_id'=>'0000');
echo json_encode($return);
else:
$token = initToken($_GET['key'],$_GET['user']);
if($token == $r['API_Token']):
$return = array(
'DBA_id'=>$token,
'DBA_servertime'=>time(),
'DBA_highscore'=>$r['score'],
);
echo json_encode($return);
endif;
endif;
break;
Here's the script the game developer will have to add to their game to get the data when the game loads. Found this on another stackoverflow question but it's not working.
$.getJSON("https://www.gamerholic.com/gamerholic_api/db_api_v1.php? user=1&key=6054abe3517a4da6db255e7fa27f4ba001083311&gameid=1&action=authenticate", function () {
alert("aaa");
});
Try adding &callback=? to the end of the url you are constructing. This will enable jsonp that is accepted by cors.
$.getJSON("https://www.gamerholic.com/gamerholic_api/db_api_v1.php?user=1&key=6054abe3517a4da6db255e7fa27f4ba001083311&gameid=1&action=authenticate&callback=?", function () {
alert("aaa");
});
As per cross domain origin policy you cannot access cross domain url using jquery getJson function.
A callback is required to manage cross domain request using json and it needs to be handled on the server as well as the client end.
Also make sure to check the response using firebug or similar tool because as of now it is returning response code as 200.
I am mentioning two threads here which can guide you the right way
Jquery getJSON cross domain problems
http://www.fbloggs.com/2010/07/09/how-to-access-cross-domain-data-with-ajax-using-jsonp-jquery-and-php/

Secure Token Process

I am currently in the mist of developing a website using PHP and MYSQL. It is a private website therefore registrations must be allowed using emails. In simple terms if a new user has to be registered, the administrator has to go into the system and add an email address to be registered.
What I want to do is to create a token or a pass value when this does happen.
Here are the steps:
Administrator adds an email to the system
A unique token value is created (e.g. 1234567890)
The token value is then sent to the users email
the user goes on the link provided and enters his email and the token value
If Success - User is allowed to register
If Fail! - Token is regenerated and send again to that email address
What I really want to know is what would be the best practice to create a token and how can we ensure to create a unique token every time an email is registered.
For further security can I ensure that each token only live for a couple of hours. But would this prevent unauthorized access into the system, or this is a bad idea for securing my website?
My thoughts of creating a unique token: Use hashing algorithms that use SALT so the results cannot be predicted or decrypted (Problems with MD5)
Any help or a lead towards the right direction would be greatfull.
I like this method of generating a cryptographically secure pseudo-random number generator or (CSPRNG) for PHP. It was written by Scott:
<?php
function crypto_rand_secure($min, $max) {
$range = $max - $min;
if ($range < 0) return $min; // not so random...
$log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd >= $range);
return $min + $rnd;
}
function getToken($length=32){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
for($i=0;$i<$length;$i++){
$token .= $codeAlphabet[crypto_rand_secure(0,strlen($codeAlphabet))];
}
return $token;
}
?>
In terms of adding a timeout, I recommend taking care of this in the database. Add a column that is called like registration_timeout and then use mysql's addtime() function to set this colmn to the current time stamp + however long you want the timeout to be.
Also keep in mind that temporary email accounts are trivial to use (http://www.mailinator.com , http://www.guerrillamail.com, ect...), so asking for someone to register an email account doesn't mean anything. Further more a user account could end up on http://www.bugmenot.com .

Categories