Does this script parse return parameters?

If someone sees something like
www.whatever.com/purchase.php?price=999
It could be tempting to even an otherwise honest person to change it to
www.whatever.com/purchase.php?price=399
and save themselves $6

Passing the price via the url makes your website highly susceptible to fraud.

That command doesn’t even exist in the latest version of PHP and it wouldn’t prevent any junk value from being able to be inserted into the database even in old versions of PHP where it did exist.

The best way to prevent injection is to make it completely impossible by using prepare statements. Of course you still need to validate or sanitize ALL inputs to prevent junk insertion.

Thanks for your replies.
I’d like to determine what the return url (back from Amazon) shows, but it returns so fast I can’t tell.
Can you provide a suggestion on how to determine what’s in that url?

Could you var_dump($_GET) before any other code, then immediately exit?

With regard to the potential for a user changing the parameters by tampering with the URL, this is the reason for the “signature” field. But that script doesn’t seem to check it, or (as mentioned earlier) the “resultCode” field which may contain “Success” or “Failure”. The php code just goes ahead and processes the return as if all was fine.

There’s some sample code here ( https://github.com/amzn/pay-with-amazon-express-demo/tree/master/php ) in the “Success.php” that shows how to check the return parameters.

1 Like

Thanks for your replies.I installed https://github.com/amzn/pay-with-amazon-express-demo/tree/master/php successfully(using Result.php as the return url). Now I’d like to combine/integrate the code below (which now successfully adds credits to a Users’ account after purchase) with the git hub code, but I don’t know which file from there would be the one to use. Do you know if it would be Result.php or Success.php? Any help will be appreciated. I look forward to your reply. Much thanks again.

<?php
/// - Database Information
 $dbhost = 'localhost';
$dbuser = '......';
$dbpass = '......';
$dbname = '......';

$conn = mysql_connect($dbhost, $dbuser, $dbpass);

mysql_select_db($dbname);

 /////////////////////////////////////////////////////////////////////////////////////////

 function getUsername($id) {

$sql1 = "SELECT * FROM member_profile WHERE user_id = $id";
$query1 = mysql_query($sql1) or DIE(mysql_error());
$result = mysql_fetch_array($query1);

return $result['user_name'];
}
header('Location: ../index.php');
include_once ('../classes/config.php');
include ('../classes/functions.php');
include_once ('../classes/sessions.php'); //gives us access to the user's cookies for validation

$user = $user_id;
$price = 0;
 if (is_numeric($_GET['amount'])) $price = $_GET['amount'];
$username = getUsername($user_id);

$backp = $price;
switch ($price) {
    case .10:
        $credits = 20;
        break;
       case .20:
       $credits = 40;
        break;
      case .30:
        $credits = 60;
        break;
     default:
        $credits = 0;
 }

$sql2 = "INSERT INTO purchases (id, type, user_id, vid_id, date, name, uploader, uploaderID,     title, amount, videoid, descr, promo) VALUES ('', 'purchase', '$user', '0', CURDATE(), '$username', 'none', 'none', 'none', '$backp', 'none', 'Purchased via Amazon', 'none')";
 $query2 = mysql_query($sql2);

$sql1 = "SELECT * FROM credits WHERE user_id = $user";
$query1 = @mysql_query($sql1);

// =========================================================
// Error reporting for the above query is turned off, so we
// don't know if the credits record was even found.
// The following line fixes that issue by inserting a blank
// record if the row count is zero.
   // =========================================================
    if (mysql_num_rows($query1) == 0)
     {
     $sql1_I = "INSERT INTO credits (user_id) VALUES ($user)";
     $query1_I = mysql_query($sql1_I) or die(mysql_error());
     }
    // =========================================================
   //        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // This code may be unneccessary considering an entry is made upon initial user registration.
    //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    $old = @mysql_fetch_array($query1);
    $balance = $old['total_credits'] + $credits;
    $purchases = $old['total_purchases'] + 1;
    $sql = "UPDATE credits SET user_id=$user, total_credits=$balance, pending_credits=0,  last_purchase=CURDATE(), total_purchases=$purchases WHERE user_id=$user";
    $query = mysql_query($sql);



     $template = "../themes/$user_theme/templates/main_1.htm";
     $inner_template1 = "../themes/$user_theme/templates/inner_amazon_success.htm";     //middle of page
    $TBS = new clsTinyButStrong;
    $TBS->NoErr = true; // no more error message displayed.

    $TBS->LoadTemplate("$template");
    $TBS->MergeBlock('mp', $members_full);

    $TBS->Render = TBS_OUTPUT;
    $TBS->Show();

    ?>

I think from memory you’d want to integrate your code (though using PDO instead of the old-style mysql calls) into success.php, as you only want to update your database if the purchase has gone through correctly. I can’t remember whether the code in result.php passes through the response that you got from Amazon, so that might need changing to ensure the response data is still in place. Also I don’t know where your $user_id is coming from, and whether that needs passing through somehow.

1 Like

Thanks again for your message/help. Much appreciated.
I just got a reply from Amazon that stated “you should add your credit logic after you have confirmed successful payment. Whether this means adding the code directly to Success.php(code below) or including the AddCredits.php(code above) file is your choice”. So, is it possible to add something like

include ('.../AddCredits.php'); 

to the code below to “add your credit logic after you have confirmed successful payment”, or would it only work if the AddCredits.php code parts - that actually add the credits, be integrated into Success.php?

<html>
 <body>
     <br />
     <p>Your Transaction was successful. Following are the Parameters returned</p>
     <br />
     </body>
     <?php
     echo ("<pre>");
     print_r($_GET);
     echo ("</pre>");

    /* begin signature validation */
    require_once 'Express.config.php';

    $signatureReturned = $_GET['signature'];
    $parameters = $_GET;
    unset($parameters['signature']);
    $parameters['sellerOrderId'] = rawurlencode($parameters['sellerOrderId']);
    uksort($parameters, 'strcmp');

   $parseUrl = parse_url($returnURL);
    $stringToSign = "GET\n" . $parseUrl['host'] . "\n" . $parseUrl['path'] . "\n";

   foreach ($parameters as $key => $value) {
    $queryParameters[] = $key . '=' . str_replace('%7E', '~', rawurlencode($value));
    }
    $stringToSign .= implode('&', $queryParameters);

    $signatureCalculated = base64_encode(hash_hmac("sha256", $stringToSign, $secretKey, true));
    $signatureCalculated = str_replace('%7E', '~', rawurlencode($signatureCalculated));

    if ($signatureReturned == $signatureCalculated) {
    echo "Signature was successfully validated.";
    } else {
    echo "Signature does not match.";
    }
   ?>
 <html>

You could include the code, or add it directly into the success.php file - there’s no real difference. By the look of it you need to add your code within the last if () clause where the signature has been found to be correct.

if ($signatureReturned == $signatureCalculated) {
    // echo "Signature was successfully validated.";
    // Add your processing code here, now you know the signature is correct.
    } else {
    echo "Signature does not match.";
    }

Thanks again for that. Very helpful.
I added the code, and this code appears to work successfully (thanks again).
Can someone please take a peek at it and tell me it is more secure, and if you believe that
the browser bar won’t be able to be edited, as mentioned earlier, to add any additional credits - less suseptible to fraud - and does it help with preventing “junk insertion” or “SQL injection”?
I look forward to your feedback. Much thanks again.

<html>
<body>
    <br />
    <p>Your Transaction was successful. Following are the Parameters returned</p>
    <br />
</body>
<?php
echo ("<pre>");
//print_r($_GET);
echo ("</pre>");

/* begin signature validation */
require_once 'Express.config.php';

$signatureReturned = $_GET['signature'];
$parameters = $_GET;
unset($parameters['signature']);

if(isset($parameters['sellerOrderId'])) {
    $parameters['sellerOrderId'] = rawurlencode($parameters['sellerOrderId']);
}
uksort($parameters, 'strcmp');

$parseUrl = parse_url($returnURL);
$stringToSign = "GET\n" . $parseUrl['host'] . "\n" . $parseUrl['path'] . "\n";

foreach ($parameters as $key => $value) {
    $queryParameters[] = $key . '=' . str_replace('%7E', '~', rawurlencode($value));
}
$stringToSign .= implode('&', $queryParameters);

$signatureCalculated = base64_encode(hash_hmac("sha256", $stringToSign, $secretKey, true));
$signatureCalculated = str_replace('%7E', '~', rawurlencode($signatureCalculated));

if ($signatureReturned == $signatureCalculated) {
    echo "Signature was successfully validated.";

   /// - Database Information
$dbhost = 'localhost';
 $dbuser = '.........';
$dbpass = '.........';
$dbname = '.........';

$conn = mysql_connect($dbhost, $dbuser, $dbpass);

mysql_select_db($dbname);

/////////////////////////////////////////////////////////////////////////////////////////

function getUsername($id) {

$sql1 = "SELECT * FROM member_profile WHERE user_id = $id";
$query1 = mysql_query($sql1) or DIE(mysql_error());
$result = mysql_fetch_array($query1);

return $result['user_name'];
}
//header('Location: ../index.php');
include_once ('../../classes/config.php');
include ('../../classes/functions.php');
include_once ('../../classes/sessions.php'); //gives us access to the user's cookies for  validation

$user = $user_id;
$price = 0;
if (is_numeric($_GET['amount'])) $price = $_GET['amount'];
$username = getUsername($user_id);

$backp = $price;
switch ($price) {
case .10:
    $credits = 20;
    break;
case .20:
    $credits = 40;
    break;
case .30:
    $credits = 60;
    break;
default:
    $credits = 0;
}

$sql2 = "INSERT INTO purchases (id, type, user_id, vid_id, date, name, uploader, uploaderID, title, amount, videoid, descr, promo) VALUES ('', 'purchase', '$user', '0', CURDATE(), '$username', 'none', 'none', 'none', '$backp', 'none', 'Purchased via Amazon', 'none')";
 $query2 = mysql_query($sql2);

$sql1 = "SELECT * FROM credits WHERE user_id = $user";
$query1 = @mysql_query($sql1);

// =========================================================
// Error reporting for the above query is turned off, so we
// don't know if the credits record was even found.
// The following line fixes that issue by inserting a blank
// record if the row count is zero.
// =========================================================
if (mysql_num_rows($query1) == 0)
{
$sql1_I = "INSERT INTO credits (user_id) VALUES ($user)";
$query1_I = mysql_query($sql1_I) or die(mysql_error());
}
// =========================================================


//     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// That may be unecessary code considering an entry is made upon initial user registration.
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

 $old = @mysql_fetch_array($query1);
 $balance = $old['total_credits'] + $credits;
 $purchases = $old['total_purchases'] + 1;
 $sql = "UPDATE credits SET user_id=$user, total_credits=$balance, pending_credits=0,  last_purchase=CURDATE(), total_purchases=$purchases WHERE user_id=$user";
 $query = mysql_query($sql);

 $template = "../../themes/$user_theme/templates/main_1.htm";
 $inner_template1 = "../../themes/$user_theme/templates/inner_amazon_success.htm";       //middle of page
 $TBS = new clsTinyButStrong;
 $TBS->NoErr = true; // no more error message displayed.

 $TBS->LoadTemplate("$template");
 $TBS->MergeBlock('mp', $members_full);

 $TBS->Render = TBS_OUTPUT;
 $TBS->Show();

 } else {
    echo "Signature does not match.";
 }
 ?>
 <html>

I can’t comment on the security aspects of the code - by checking the signature based on the Amazon code, that should prevent the chance of someone altering the URL, or calling your return URL with their own parameters, unless they can somehow figure out the signature, but I have no real-world experience on this unfortunately.

The key problem, mentioned above, is that your code uses the old-style mysql functions to access the database. These have been removed from the current version of PHP, so whenever your host upgrades their servers to PHP 7, the code will stop working. Hence, if this is new code, you should take the time to change it to PDO.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.