Search code examples
phprestcurlhttp-head

Validate curl CURLOPT_HTTPHEADER request on server side


I have developed a small webservice which will take user query and returns data as json response. Everything working fine. But I was wondering how to set and compare Authorization token from the client side as part of the curl header. This is my code to send request . Still I didn't set any validation thing on FetchData.php. I want to implement that on FetchData.php. How to implement that ?

$query = "select * from product_details where id = 2";
$query_params = array("query" => $query); 
$url = "http://localhost/api/fetchData.php?" . http_build_query($query_params, '', "&");
$curl = curl_init();
$token = 'ABCD123456';
$header[] = 'Authorization: Token '.$token;
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url,
CURLOPT_HTTPGET=>1,
CURLOPT_FAILONERROR=> 1,
CURLOPT_RETURNTRANSFER=> true,
CURLOPT_HTTPHEADER=>$header
));

$resp = curl_exec($curl);
if($resp === false)  
{
    $responseJson = new stdClass(); 
    $responseJson->status =  "FAILED";
    $responseJson->message = 'Send error: ' . curl_error($curl);
    throw new Exception(curl_error($curl)); 
}
else{
    $resp = curl_exec($curl);
}

In the above code I have added this authorization token . But acutually this was not set on server side I mean in fetchData.php.

$token = 'ABCD123456';
$header[] = 'Authorization: Token '.$token;

so my question is how to set up this authorization token thing in fetchData.php and validate the token which was from client side? Any help would be greatly appreciated.


Solution

  • if there is only 1 token, you can just hardcode it,

    if(hash_equals(hash('sha384',$_SERVER['HTTP_AUTHORIZATION'],true),hash('sha384','the-real-token',true)){
        // correct token!
    
    }else{
        // wrong token
    }
    

    however, if you have multiple tokens, you should save them pre-hashed in a DB, here's an SQLite example with the tokens "foo", "bar", and "baz":

    $db = new PDO('sqlite::memory:', '', '', array(
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ));
    $db->exec('
    CREATE TABLE tokens(id INTEGER PRIMARY KEY AUTOINCREMENT,
    token TEXT,
    hash BLOB);');
    $stm=$db->prepare("INSERT INTO TOKENS(`token`,`hash`) VALUES(:token,:hash);");
    
    $tokens=["foo","bar","baz"];
    foreach($tokens as $token){
        $stm->execute(array(':token'=>$token,':hash'=>hash('sha384',$token,true)));
    }
    

    with this you can check if a token is valid by running

    $stm=$db->prepare("SELECT EXISTS(SELECT 1 FROM hashes WHERE hash=?);");
    $stm->execute([hash('sha384',$_SERVER['HTTP_AUTHORIZATION'],true]);
    if($stm->fetch(PDO::FETCH_NUM)[0]==="1"){
        // valid token!
    }else{
        // invalid token =(
    }
    

    now you might wonder

    why bother hashing the tokens at all, why not just save them in plaintext?
    it's about protecting against timing attacks, the hashes does length padding, so hackers cannot use timing attacks to infer the actual length of the tokens (which could help attackers a lot in performing a bruteforce/dictionary attack), and there is no correlation between the actual token, the input string, and the number of correct bits for a timing attack, and even if the hackers manage to infer that the first 8 bits of the hash is definitely 10010111, it gets progressively more difficult to find a string that hashes to known-bits+1 for each bit that gets inferred, with the end result being that attacking this scheme with timing attacks should be completely infeasible (at least with sha2-384)