Search code examples
phpauthenticationhmachmacsha1

How to authenticate a post request in php


I am trying to implement the HTTP Post Authentication, Which mentioned on this document https://www.clearslide.com/view/mail?iID=YS7LCS8XDPCABFR453DE , I am not able to understand what exactly i have to do to get this working, I tried to dump $_REQUEST and $_SERVER variables.

This is the output i am getting for this $_REQUEST

Array
(
    [emailpitchsent] =>
)

And this the output for $_SERVER

Array
(
    [HTTP_HMAC] => D4L1ICmRMii32PdCryBkpSNdxY5XDxC_OXsDTEucyzU
    [HTTP_DATE] => Thu, 12 Nov 2015 00:50:05 PST
    [HTTP_SHA1] => GTRFkX7JYVtDQgvrQeXJmHaCF24=
    [CONTENT_LENGTH] => 262
    [CONTENT_TYPE] => application/json; charset=UTF-8
    [HTTP_HOST] => myhost.com
    [HTTP_CONNECTION] => Keep-Alive
    [HTTP_USER_AGENT] => Apache-HttpClient/4.5.1 (Java/1.7.0_55)
    [PATH] => /sbin:/usr/sbin:/bin:/usr/bin
    [SERVER_SIGNATURE] => <address>Apache/2.2.31 (Amazon) Server at myhost.com Port 80</address>

    [SERVER_SOFTWARE] => Apache/2.2.31 (Amazon)
    [SERVER_NAME] => myhost.com
    [SERVER_ADDR] => 0.0.0.0
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => 0.0.0.1
    [DOCUMENT_ROOT] => /var/www/vhosts/myhost.com/httpdocs
    [SERVER_ADMIN] => [email protected]
    [SCRIPT_FILENAME] => /var/www/vhosts/myhost.com/httpdocs/clearslide.php
    [REMOTE_PORT] => 47400
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => POST
    [QUERY_STRING] => emailpitchsent
    [REQUEST_URI] => /clearslide.php?emailpitchsent
    [SCRIPT_NAME] => /clearslide.php
    [PHP_SELF] => /clearslide.php
    [REQUEST_TIME] => 1447318205
)

This i the content of clearslide.php

<?php
$req_dump = print_r($_REQUEST, TRUE);
$ser_dump = print_r($_SERVER,TRUE);
$fp = fopen('request.log', 'a');
fwrite($fp, $req_dump);
fwrite($fp, $ser_dump);
fclose($fp);

What i have to do now to get this thing working, How can i authenticate that request and get the data?.

Thanks


Solution

  • You can get the raw json body with file_get_contents('php://input') or $HTTP_RAW_POST_DATA. Here is a sample of how to verify the signature from the headers. Also, here is a script that can generate a fake request for testing. Let me know if you have any more trouble. :)

    Verify the signature

    <?php
    // https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.20.0/com/google/api/client/util/Base64#encodeBase64URLSafeString(byte[])
    function urlsafe_b64encode($string) {
        $data = base64_encode($string);
        $data = str_replace(array('+','/', '='), array('-','_',''), $data);       
        return $data;
    }
    
    function extract_message(){
      $verb = $_SERVER["REQUEST_METHOD"];
      $sha1 = $_SERVER["HTTP_SHA1"];
      $content_type = $_SERVER["CONTENT_TYPE"];
      $request_time = $_SERVER["HTTP_DATE"];
      $path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);     
      return "$verb\n$sha1\n$content_type\n$request_time\n$path";
    }
    
    function calculate_signature($to_sign, $api_key) {
      return urlsafe_b64encode(hash_hmac('sha256', $to_sign, $api_key, true));
    }
    
    function get_recieved_signature() { return $_SERVER["HTTP_HMAC"]; }
    
    function verify_signature($recieved, $calculated) { return $recieved == $calculated; }
    
    $api_key = "apikey";
    
    $to_sign = extract_message();
    $calculated_signature = calculate_signature($to_sign, $api_key);
    $recieved_signature = get_recieved_signature();
    $matched = verify_signature($recieved_signature, $calculated_signature);
    
    $json_obj = json_decode(file_get_contents('php://input'), TRUE);
    
    $fp = fopen('request.log', 'a');
    fwrite($fp, print_r(array(
      '$_SERVER' => $_SERVER, 
      "JSON" => $json_obj,
      "SIGNATURE INFO" =>  array(
        "To Sign" => str_replace("\n", "\\n", $to_sign),
        "Received" => $recieved_signature,
        "Calculated" => $calculated_signature,
        "Matched" => $matched ? "TRUE" : "FALSE"
      )
    ), TRUE));
    fclose($fp);
    

    Make a sample request

    <?php
    // https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.20.0/com/google/api/client/util/Base64#encodeBase64URLSafe(byte[])
    function urlsafe_b64encode($string) {
        $data = base64_encode($string);
        $data = str_replace(array('+','/','='), array('-','_',''), $data);
        return $data;
    }
    
    $sample_json = '{"company":"Example, Inc","pitchDetailsLink":"https://dev.clearslideng.com/manage/email/details?userVID=6YSZ9BBJWVM3H87PUAPE","senderEmail":"[email protected]","time":"Fri, 11/13/2015, 10:19 AM PST","recipients":["[email protected]"],"decks":["testing"]}';
    
    $api_endpoint = 'http://localhost';
    
    $verb = "POST";
    $sha1 = base64_encode(sha1($sample_json));
    $content_type = "application/json; charset=UTF-8";
    $request_time = date("D, d M Y H:i:s T");
    $path = "/emailpitchsent";
    
    $to_sign = "$verb\n$sha1\n$content_type\n$request_time\n$path";
    $api_key = "apikey";
    $signature = urlsafe_b64encode(hash_hmac('sha256', $to_sign, $api_key, true));
    
    $ch = curl_init("$api_endpoint$path");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // curl_setopt($ch, CURLOPT_VERBOSE, 1);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
     "CONTENT-TYPE: $content_type",
     "HMAC: $signature",
     "DATE: $request_time",
     "SHA1: $sha1"
    ));
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $sample_json);
    $result = curl_exec($ch);
    curl_close($ch);
    
    echo "To Sign: ". str_replace("\n", "\\n", $to_sign) ."\n\n";
    echo "Response: \n\n$result";
    

    Example Log

    Array
    (
        [$_SERVER] => Array
            (
                [USER] => vagrant
                [HOME] => /home/vagrant
                [FCGI_ROLE] => RESPONDER
                [QUERY_STRING] => event=emailpitchsent
                [REQUEST_METHOD] => POST
                [CONTENT_TYPE] => application/json; charset=UTF-8
                [CONTENT_LENGTH] => 270
                [SCRIPT_FILENAME] => /vagrant/public/index.php
                [SCRIPT_NAME] => /index.php
                [REQUEST_URI] => /index.php?event=emailpitchsent
                [DOCUMENT_URI] => /index.php
                [DOCUMENT_ROOT] => /vagrant/public
                [SERVER_PROTOCOL] => HTTP/1.1
                [GATEWAY_INTERFACE] => CGI/1.1
                [SERVER_SOFTWARE] => nginx/1.1.19
                [REMOTE_ADDR] => 10.0.0.10
                [REMOTE_PORT] => 61094
                [SERVER_ADDR] => 10.0.0.200
                [SERVER_PORT] => 80
                [SERVER_NAME] => 192.168.22.10.xip.io
                [HTTPS] => off
                [REDIRECT_STATUS] => 200
                [LARA_ENV] => local
                [HTTP_HMAC] => vnQM9g41jE--rJvvwymezRY5VU9pf52Vu9sGhe_Gy-4
                [HTTP_DATE] => Thu, 19 Nov 2015 06:24:49 PST
                [HTTP_SHA1] => X7hjTy8DUzIUNO05JiuXp1DT3Js=
                [HTTP_CONTENT_LENGTH] => 270
                [HTTP_CONTENT_TYPE] => application/json; charset=UTF-8
                [HTTP_HOST] => 10.0.0.200
                [HTTP_CONNECTION] => Keep-Alive
                [HTTP_USER_AGENT] => Apache-HttpClient/4.5.1 (Java/1.7.0_91)
                [PHP_SELF] => /index.php
                [REQUEST_TIME] => 1447943089
            )
    
        [JSON] => Array
            (
                [company] => ClearSlide
                [pitchDetailsLink] => https://dev.clearslideng.com/manage/email/details?userVID=YGCD4WNJT377FQ6FTUFH
                [senderEmail] => [email protected]
                [time] => Thu, 11/19/2015, 6:24 AM PST
                [recipients] => Array
                    (
                        [0] => [email protected]
                    )
    
                [decks] => Array
                    (
                        [0] => RockyBeach-720
                    )
    
            )
    
        [SIGNATURE INFO] => Array
            (
                [Path] => /index.php
                [To Sign] => POST\nX7hjTy8DUzIUNO05JiuXp1DT3Js=\napplication/json; charset=UTF-8\nThu, 19 Nov 2015 06:24:49 PST\n/index.php
                [Received] => vnQM9g41jE--rJvvwymezRY5VU9pf52Vu9sGhe_Gy-4
                [Calculated] => vnQM9g41jE--rJvvwymezRY5VU9pf52Vu9sGhe_Gy-4
                [Matched] => TRUE
            )
    
    )
    

    Example of Server Side toSign

    POST
    X7hjTy8DUzIUNO05JiuXp1DT3Js=
    application/json; charset=UTF-8
    Thu, 19 Nov 2015 06:24:49 PST
    /index.php