[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 340
  • Last Modified:

OSCommerce - If a customer backs out of payment screen (paypal), they can still access the downloadable products link and dont need to pay.

Customers dont need to pay they can access the download links within the order history or by manipulating their URL to checkout_success.php.

Does anyone know where/how I fix this problem? I have searched oscommerce.com community pages but can only find other people posting the same problem, I cannot find the fix.

The version I have installed, I believe, is oscommerce-2.2ms2-051113

Thanks in advance.
0
pdoxford
Asked:
pdoxford
  • 2
  • 2
1 Solution
 
gamebitsCommented:
You should do that thru Paypal's IPN system (Instant Payment Notification) there will be communication between your server and Paypal's server to validate the transaction, after that it's just a matter of having an if(isset... added to your checkout_sucess.php.

Gamebits
0
 
pdoxfordAuthor Commented:
Thanks for the advice. I am already using IPN. I have since modified checkout_success to check TABLE_ORDERS_STATUS which appears to fix the security hole (i think?). However, can you ellaborate on your previous post...exactly what do i need to check with isset?
Many Thanks
0
 
gamebitsCommented:
Oh, was just a general comment in the sense that if you don't want people to access your page directly you can do that by checking if they are loged in or not or any other means.

Like you could have a hidden field in a form with the value set to sesame;

<input type="hidden" name="cave" value="sesame">

Then in your checkout_success.php you would have.

$cave = $_POST['cave'];

if(isset($cave == 'sesame')){}else{echo "You do not have permission to access this page"; }

So if somebody is trying to access the page directly the variable would not be set and they would get the message instead of accessing the page.

But honestly by checking the Status you should be all set.

Gamebits
0
 
pdoxfordAuthor Commented:
Thanks for your quick response. There must be a fix for this, it appears a serious bug with oscommerce.
0
 
floorman67Commented:
then mask the true downlaod location and have php authenticate the users ability to downlaod by if they paid or not.

here are two scripts taht do it, but will need modifications to work for authenticated downlaods

the first one is a base class drop in for security and client authentication.

the second (filepipe.php) masks a files true location so no one can directly access it by direct URL, and MUST be ran thru cci security class.

now as stated, filepipe.php will need some customization for your needs, but by running the requests thru php it isnt a far stretch and these will certaily get you started.

::: ccisecurity.php :::
<?php

##################
#
# Configuration Section - Set these variables first (Or use default if you want)
#
##################

$usehtaccessbans = 1;                  # 1 = modify .htaccess to ban IPs, 0 = don't ban IPs.
$filterGETvars = 1;                        # 1 = sterilize HTML tags in GET variables, 0 = don't
$filterCOOKIEvars = 1;                  # 1 = sterilize HTML tags in COOKIE variables, 0 = don't
$filterPOSTvars = 0;                  # 1 = sterilize HTML tags in POST variables, 0 = don't
$extraPOSTprotection = 0;            # 1 = use the extra POST protection, 0 = don't
$extraGETprotection = 0;            # 1 = use the extra GET protection, 0 = don't (not recommended!)
$checkmultiPOST = 1;                  # 1 = only allow maxmultiPOST number of successive POSTs, 0 = don't care
$maxmultiPOST = 5;                        # Maximum number of POST operations in a row, if checkmultipost is on.
$zipcompress = 0;                        # 1 = Compress pages using GZIP library (lower bandwidth, higher CPU), 0 = don't
$compresslevel = 9;                        # Compression level for zipcompressing, from 1 (low) to 9 (maximum)
$cpuloadmonitor = 0;                  # 1 = block access if over a certain system load, 0 = don't
$cpumaxload = 10.0;                        # Maximum 5 minute system load average before blocking access
$ccisessionpath = "";                  # if not blank, sets a directory path to store session files.

##### Encryption/Encoding Variables

$javababble = 0;                        # 1 = Use Encoding/Encrypting (Must be on for any), 0 = Don't
$javaencrypt = 0;                        # Do actual encrypting of HTML, not just escaping (warning: may slow display)
$preservehead = 0;                        # 1 = Only encode/encrypt between BODY tags, 0 = encode/encrypt whole document

##################
#
# Check for in-script overrides
#
##################

if (isset($zipoverride)) {
  if (!isset($_REQUEST["zipoverride"])) {
    $zipcompress = $zipoverride;
    unset($zipoverride);
  }
}

if (isset($babbleoverride)) {
  if (!isset($_REQUEST["babbleoverride"])) {
    $javababble = $babbleoverride;
    unset($babbleoverride);
  }
}

##################
#
# Function: CCIJavaBabble
#
# Usage: Takes some HTML, url-encodes it (jumbles it) then returns the javascript needed to display it properly.
#
##################

function CCIJavaBabble($myoutput) {
  global $mycrypto, $myalpha2, $javaencrypt, $preservehead;
  $s = $myoutput;
  $s = ereg_replace("\n","",$s);

  if ($preservehead) {  
    eregi("(^.+<body[^>]*>)",$s,$chunks);
    $outputstring = $chunks[1];
    eregi_replace($headpart,"",$s);

    eregi("(</body[^>]*>.*)",$s,$chunks);
    $outputend = $chunks[1];
    eregi_replace($footpart,"",$s);
  } else {
    $outputstring = "";
    $outputend = "";
  }
 
  if ($javaencrypt) {
    $s = strtr($s,$myalpha2,$mycrypto);
    $s = rawurlencode($s);
    $outputstring .= "<script>var cc=unescape('$s'); ";
    $outputstring .= "var index = document.cookie.indexOf('" . md5($_SERVER["REMOTE_ADDR"] . $_SERVER["SERVER_ADDR"]) . "='); " .
      "var aa = '$myalpha2'; " .
      "if (index > -1) { " .
      "  index = document.cookie.indexOf('=', index) + 1; " .
      "  var endstr = document.cookie.indexOf(';', index); " .
      "  if (endstr == -1) endstr = document.cookie.length; " .
      "  var bb = unescape(document.cookie.substring(index, endstr)); " .
      "} " .
      "cc = cc.replace(/[$myalpha2]/g,function(str) { return aa.substr(bb.indexOf(str),1) }); document.write(cc);";
  } else {
    $outputstring .= "<script>document.write(unescape('" . rawurlencode($s) . "'));";
  }
  $outputstring .= "</script><noscript>You must enable Javascript in order to view this webpage.</noscript>" . $outputend;
       
  return $outputstring;
}

##################
#
# Function: CCIClearSession
#
# Format: CCIClearSession()
# Returns: Nothing
#
# Usage: Clears all the data out of the session record other than data used for this script
#
##################

function CCIClearSession() {
  $getvariables = array_keys($_SESSION);
  $count = 0;
  while($count < count($getvariables)) {
    if (substr($getvariables[$count],0,7) != "ccisec-") {
      session_unregister($getvariables[$count]);
      if (ini_get('register_globals')) unset($$getvariables[$count]);
    }
    $count++;
  }
}

##################
#
# Function: CCIBanIP
#
# Format: CCIBanIP(IPAddress)
# Returns: Nothing
#
# Usage: Will open and add a deny line to the .htaccess file in the same directory to deny all
#        accessing by a given IP address.
#
##################

function CCIBanIP($banip) {
  $filelocation = ".htaccess";
  $limitend = "# End of CCI Security Section\n";
  $newline = "deny from $banip\n";
  if (file_exists($filelocation)) {
    $mybans = file($filelocation);
    $lastline = "";
    if (in_array($newline,$mybans)) exit();
    if (in_array($limitend,$mybans)) {      
      $i = count($mybans)-1;
      while ($mybans[$i] != $limitend) {
        $lastline = array_pop($mybans) . $lastline;
        $i--;
      }
      $lastline = array_pop($mybans) . $lastline;
      $lastline = array_pop($mybans) . $lastline;
      $lastline = array_pop($mybans) . $lastline;
      array_push($mybans,$newline,$lastline);
    } else {
      array_push($mybans,"\n\n# CCI Security Script\n","<Limit GET POST>\n","order allow,deny\n",$newline,"allow from all\n","</Limit>\n",$limitend);
    }
  } else {
    $mybans = array("# CCI Security Script\n","<Limit GET POST>\n","order allow,deny\n",$newline,"allow from all\n","</Limit>\n",$limitend);
  }  
  $myfile = fopen($filelocation,"w");
  fwrite($myfile,implode($mybans,""));
  fclose($myfile);
   
}

##################
#
# Function: CCIFloodCheck
#
# Format: CCIFloodCheck("identifier",interval,threshold)
# Returns: 1 if requested without minimum interval, a threshold number of times.  0 if not.
#
# Usage: For functions that require flood control pass a unique identifier, the minimum number of
#        seconds that should be waited between repeats of the function, and a number of times that
#        function can be called too quickly before it sets off the flood trapping.
#
##################

function CCIFloodCheck($identifier,$interval,$threshold=1) {
  $myresult = 0;
  if (isset($_SESSION["ccisec-" . $identifier])) {
    if ($_SESSION["ccisec-" . $identifier] > (time()-$interval)) {
      if ($threshold<2) {
        $myresult = 1;
      } else {
        if (!isset($_SESSION["ccisec-" . $identifier . "-counter"])) {
          $_SESSION["ccisec-" . $identifier . "-counter"] = 1;
        } else {
          $_SESSION["ccisec-" . $identifier . "-counter"]++;
          if ($_SESSION["ccisec-" . $identifier . "-counter"] >= $threshold) {
            $myresult = 1;
          }
        }
      }
    }
    $_SESSION["ccisec-" . $identifier] = time();
  }
  return $myresult;
}

################################################################################

srand(time());
if (eregi("ccisecurity\.php",$_SERVER["SCRIPT_NAME"])) exit();

if ($ccisessionpath != "") session_save_path($ccisessionpath);
session_name(md5($_SERVER["REMOTE_ADDR"] . $_SERVER["HTTP_HOST"] . "CCI"));

ini_set("session.use_only_cookies","1");
ini_set("session.use_trans_sid","0");

if (($zipcompress) && (eregi("gzip",$_SERVER["HTTP_ACCEPT_ENCODING"]))) {
  ini_set("zlib.output_compression","On");
  ini_set("zlib.output_compression_level",$compresslevel);
  ob_start("ob_gzhandler");
}
if ($javababble) {
  if ($javaencrypt) {
    $myalpha = array_merge(range("a","z"),range("A","Z"),range("0","9"));
    $myalpha2 = implode("",$myalpha);
    shuffle($myalpha);
    $mycrypto = implode("",$myalpha);
    setcookie(md5($_SERVER["REMOTE_ADDR"] . $_SERVER["SERVER_ADDR"]),$mycrypto);
    unset($myalpha);
  }
  ob_start("cciJavaBabble");
}

if (substr_count($_SERVER["SERVER_NAME"],".")>1) {
  $cookiedomain = eregi_replace("^[^\.]+\.",".",$_SERVER["SERVER_NAME"]);
} else $cookiedomain = "." . $_SERVER["SERVER_NAME"];

$ip = $_SERVER["REMOTE_ADDR"];
$mykeyname = md5($_SERVER["REMOTE_ADDR"] . $_SERVER["HTTP_HOST"] . $_SERVER["DOCUMENT_ROOT"] . "CCI");
$myposthashname = md5($_SERVER["REMOTE_ADDR"] . $_SERVER["HTTP_HOST"] . $_SERVER["PATH"] . "CCI");

$myhash = md5($_SERVER["REMOTE_ADDR"] . $_SERVER["HTTP_USER_AGENT"] .
                                    $_SERVER["HTTP_HOST"] . $_SERVER["DOCUMENT_ROOT"] .
                                    $_SERVER["SERVER_SOFTWARE"] . $_SERVER["PATH"] . "X");
                                                                  
$mysession = md5($_SERVER["REMOTE_ADDR"] . $_SERVER["HTTP_HOST"]);                                    
session_id($mysession);
session_start();


# Sneaky cookie-storing flooding programs tend to trip this - a cookie not meant to be returned.

if ((isset($_SESSION["ccisec-tripwire"])) && (isset($_COOKIE[$_SESSION["ccisec-tripwire"]]))) {
  CCIBanIP($ip);
  exit();
}
$tripwire = md5(uniqid(time()));
setcookie($tripwire,md5(uniqid(time())),time()-999999,"/",$cookiedomain);
$_SESSION["ccisec-tripwire"]=$tripwire;

# End of the tripwire routine


if (!isset($_SESSION["ccisec-errors"])) $_SESSION["ccisec-errors"] = 0;
if ($_SESSION["ccisec-errors"]>=10) {
  CCIBanIP($ip);
  exit();
}

if ($_SESSION["ccisec-myhash"] != $myhash) {            
  $_SESSION["ccisec-myhash"] = $myhash;
  $_SESSION["ccisec-errors"]++;
  session_write_close();
  Header("Location: http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
  exit();
}

if ((!isset($_COOKIE[$mykeyname])) || ($_COOKIE[$mykeyname] != $myhash)) {
 
  if (!isset($_SESSION["ccisec-nocookie"])) {
    $_SESSION["ccisec-nocookie"] = 1;
  } else {
    $_SESSION["ccisec-nocookie"]++;
  }
 
  if (($usehtaccessbans) && ($_SESSION["ccisec-nocookie"]>10)) CCIBanIP($ip);
   
  setcookie($mykeyname,$myhash,0,"/",$cookiedomain);
       
  if ($_SESSION["ccisec-nocookie"]>2) {
    echo "<b><h1>Access Denied</h1><br><br>You must enable cookies in order to access this website.  Please do so before returning, as continued attempts to access without cookies may result in a banning of this ip ($ip).</b>";
    session_write_close();
      exit();
  }
  if ($extraGETprotection) {
    $_SESSION["ccisec-hash"] = md5(uniqid(time()));
    setcookie($myposthashname,$_SESSION["ccisec-hash"],0,"/",$cookiedomain);  
  }
  CCIClearSession();  
  session_write_close();
  Header("Location: http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
  exit();
} else $_SESSION["ccisec-nocookie"] = 0;

if (($usehtaccessbans) && ($_SESSION["ccisec-fastaccesses"]>40)) CCIBanIP($ip);

if ($_SESSION["ccisec-fastaccesses"]>10) {
  if ((time()-60) < $_SESSION["ccisec-lastaccess"]) {
    echo "<b><h1>Access Denied</h1><br><br>There have been too many rapid requests from this IP address ($ip).  You must now wait a full 60 seconds before accessing this site again.</b>";
    $_SESSION["ccisec-fastaccesses"]++;
    $_SESSION["ccisec-lastaccess"]=time();
    exit();
  }
}

if (!isset($_SESSION["ccisec-lastaccess"])) {
  $_SESSION["ccisec-lastaccess"]=time();
} else {
  if ((time()-2) < $_SESSION["ccisec-lastaccess"]) {
    if (!isset($_SESSION["ccisec-fastaccesses"])) $_SESSION["ccisec-fastaccesses"] = 0;
    $_SESSION["ccisec-fastaccesses"]++;
  } else {
    $_SESSION["ccisec-fastaccesses"] = 0;
  }
  $_SESSION["ccisec-lastaccess"]=time();
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {

  if ($checkmultiPOST) {
    if (($_SESSION["ccisec-lastoperation"] == "POST") && ($_SESSION["ccisec-opcount"] >= $maxmultiPOST)) {
      echo "<b><h1>Access Denied</h1><br><br>You may not make multiple POST operations in sequence - please return to the website and try again.</b>";
      $_SESSION["ccisec-errors"]++;
      exit();
    }
  }    

  if ($extraPOSTprotection) {
    if ((!isset($_COOKIE[$myposthashname])) || ($_COOKIE[$myposthashname] != $_SESSION["ccisec-hash"])) {
      echo "<b><h1>Access Denied</h1><br><br>Your browser did not send the correct security data needed to complete a POST operation.  Make sure that you have cookies enabled and then try again, or contact the administration if you feel you are receiving this message in error.</b>";
      $_SESSION["ccisec-errors"]++;
      exit();
    }
  }
} else if (($extraGETprotection) && ($_SERVER["REQUEST_METHOD"] == "GET")) {
  if ((!isset($_COOKIE[$myposthashname])) || ($_COOKIE[$myposthashname] != $_SESSION["ccisec-hash"])) {
    echo "<b><h1>Access Denied</h1><br><br>Your browser did not send the correct security data needed to complete a GET operation.  Make sure that you have cookies enabled and then try again, or contact the administration if you feel you are receiving this message in error.</b>";
    $_SESSION["ccisec-errors"]++;
    exit();
  }
} else if ($_SERVER["REQUEST_METHOD"] != "GET") {
  exit();
}

if (($extraPOSTprotection) || ($extraGETprotection)) {
  srand(time());
  $_SESSION["ccisec-hash"] = md5(uniqid(time()));
  setcookie($myposthashname,$_SESSION["ccisec-hash"],0,"/",$cookiedomain);
}

if ($_SESSION["ccisec-lastoperation"] == $_SERVER["REQUEST_METHOD"]) {
  if (!isset($_SESSION["ccisec-opcount"])) {
    $_SESSION["ccisec-opcount"] = 1;
  } else {
    $_SESSION["ccisec-opcount"]++;
  }
} else $_SESSION["ccisec-lastoperation"] = $_SERVER["REQUEST_METHOD"];

# Make special characters safe in any GET based cgi variables.

if ($filterGETvars) {
  $getvariables = array_keys($_GET);
  $count = 0;
  while($count < count($getvariables)) {
    $_GET[$getvariables[$count]] = htmlspecialchars($_GET[$getvariables[$count]]);
    if (ini_get('register_globals')) $$getvariables[$count] = $_GET[$getvariables[$count]];
    $count++;
  }
}

if ($filterPOSTvars) {
  $getvariables = array_keys($_POST);
  $count = 0;
  while($count < count($getvariables)) {
    $_POST[$getvariables[$count]] = htmlspecialchars($_POST[$getvariables[$count]]);
    if (ini_get('register_globals')) $$getvariables[$count] = $_POST[$getvariables[$count]];
    $count++;
  }
}

if ($filterCOOKIEvars) {
  $getvariables = array_keys($_COOKIE);
  $count = 0;
  while($count < count($getvariables)) {
    $_COOKIE[$getvariables[$count]] = htmlspecialchars($_COOKIE[$getvariables[$count]]);
    if (ini_get('register_globals')) $$getvariables[$count] = $_COOKIE[$getvariables[$count]];
    $count++;
  }
}

if ($cpuloadmonitor) {
  $myshelldata = shell_exec("uptime");
  $myshelldata = eregi_replace(".*average.*: ","",$myshelldata);
  $myshelldata = eregi_replace(", .*","",$myshelldata);
  if ($myshelldata >= $cpumaxload) {
    echo "<b><h1>Access Denied</h1><br><br>The server is currently too busy to serve your request.  We apologize for the inconvenience.</b>";  
    exit();
  }
  unset($myshelldata);
}

unset($count);
unset($getvariables);
unset($ip);
unset($cookiedomain);
unset($mykeyname);
unset($myposthashname);
unset($myhash);
unset($mysession);

$_SESSION["ccisec-errors"] = 0;
if (connection_aborted()) exit();

?>



::: filepipe.php :::
<?php

#####
#
#  Warning: This script requires ccisecurity.php
#
#      link usage for download is:       http://your_website.com/filepipe.php?fn=filename.zip
#
#####

# You must set these variables correctly for this script to work:

$filepath = "/absolute_system_path_to_hidden_download_folder/";            # full path to directory holding files (must end in /)
$mininterval = 5;                              # minimum number of seconds between downloads
$allowdirect = 1;                              # 0 = Disallow direct linking, 1 = allow it (manual navigation)
$allowoutside = 0;                              # 0 = Disallow linking from other sites, 1 = allow it

#################################################################

# Check for and prevent deep linking to our files, using the Referer header.

if ((!isset($_SERVER["HTTP_REFERER"])) && (!$allowdirect)) {
  Header("Location: http://" . $_SERVER["HTTP_HOST"]);
  exit();
}

if (!$allowoutside) {
  if ((isset($_SERVER["HTTP_REFERER"])) && (!eregi("http://" . $_SERVER["HTTP_HOST"] . ".+$",$_SERVER["HTTP_REFERER"]))) {
    Header("Location: http://" . $_SERVER["HTTP_HOST"]);
    exit();
  }
}

# Call ccisecurity.php for basic flood control functions

$zipoverride = 0;
$babbleoverride = 0;
require_once("ccisecurity.php");


# If no filename is specified, send them to the main page.

if (!isset($_GET["fn"])) {
  Header("Location: http://" . $_SERVER["HTTP_HOST"]);
  exit();
}  

# Prevent rapid downloading based on user set minimum time interval:

if (CCIFloodCheck("filepipe-check",$mininterval)) {

  # Keep track of the number of times they attempt to download too quickly.

  if (!isset($_SESSION["filepipe-toofast"])) {
    $_SESSION["filepipe-toofast"] = 1;
  } else {
    $_SESSION["filepipe-toofast"]++;
  }
 
  # If they push their luck and try to download too fast more than 20 times, ban them using .htaccess
 
  if ($_SESSION["filepipe-toofast"]>20) {
    CCIBanIP($_SERVER["REMOTE_ADDR"]);
    exit();
  }
 
  echo "<head>";
  echo "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"" . ($mininterval + 1) . "\"; URL=\"http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] . "\">";
  echo "<title>Download Delay</title></head><body>";
  echo "<b><h1>Download Delayed</h1><br><br>The administrator requires a minimum of $mininterval seconds between downloads by a given user.  Your download will begin in $mininterval seconds (starting now) - please wait...</b>";
  echo "</body>";  
  session_write_close();
  exit();
}

# Filter out any backward directory change elements (from people who think they're tricky) :

$filename = $_GET["fn"];

$filename = ereg_replace("\.+/", "", $filename);
$filename = ereg_replace("\.\.","",$filename);
$filename = ereg_replace("^[\/]+", "", $filename);
$filename = ereg_replace("^[A-Za-z][:\|][\/]?", "", $filename);

# Make sure the file actually exists in our specified file folder:

if ((!file_exists($filepath . $filename)) || (!is_file($filepath . $filename))) {
  if (!isset($_SESSION["filepipe-notfound"])) {
    $_SESSION["filepipe-notfound"] = 1;
  } else {
    $_SESSION["filepipe-notfound"]++;
  }
  if ($_SESSION["filepipe-notfound"]>10) {
    CCIBanIP($_SERVER["REMOTE_ADDR"]);
    exit();
  }  
 
  echo "<b><h1>Access Denied</h1><br><br>You attempted to download a file does not exist.  To prevent brute-force scouting of files, continued attempts to download files that do not exist may result in the banning of your IP.  Please check the URL and try again.</b>";
  exit();
}

$justfilename = ereg_replace(".+/","",$filename);


if(strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE')){
  header('Cache-Control: public');
}

# Got this from some site - seems IE5.5 needs to be coddled like a fucking baby

if(preg_match("/MSIE 5.5/", $_SERVER["HTTP_USER_AGENT"])) {
  header("Content-Disposition: filename=$justfilename");  
} else {
  header("Content-Disposition: attachment; filename=$justfilename");
}

# Am using application/unknown at the suggestion of Kazz (Chris).  Seems to work well.

header("Content-Type: application/unknown");
Header("Content-Length: " . filesize($filepath . $filename));
if($fp = fopen($filepath . $filename, "rb")) {
  fpassthru($fp);
}

?>

0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now