How to limit ability to download a file in a store environm

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
craiger862
Forum Newbie
Posts: 3
Joined: Tue Jul 19, 2005 10:10 pm

How to limit ability to download a file in a store environm

Post by craiger862 »

'm looking guidance as to the general architecture for a specific purpose. I would like to have members of my site/store to only be allowed access to download a music track if it has been purchased. Things I would think I would want to check for before sending the file are: Is user valid and logged in? Does some unique session or ID exist that identifies the file she seeks with a processed order? Has too much time / too many downloads passed so that they are no longer able to download?

Obviously, this is much more complicated than presenting a user with the full URL to a file once Verisign has OK'd the credit card. In this scenario, anyone who could get ahold of the link could grab the file or worse, if the link is like http://site.com/files/blah.mp3 they could just go up to http://site.com/files/ and have their pick of the catalog.

What typically is done to control something like this? I think it's done in OS Commerce in some sane way to prevent unauthorized access. I just don't know what logic it takes to move from validating the requirements to delivering a binary file like an MP3.

Thanks
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Normally, what you would do:

1. You have a page called "download.php?file=foo" that you link to for downloads. This page includes all the needed permission checks for the current user and the file "foo".

2. After the tests have been passed you send the needed HTML headers to indicate the page/file being returned is of the given type of foo. (such as image/jpeg for a jpeg, etc)

3. You then basically include/passthru the the contents of foo into the returned page and exit. Ideally you are storing the catalog of "foo"s outside the webroot.
craiger862
Forum Newbie
Posts: 3
Joined: Tue Jul 19, 2005 10:10 pm

Post by craiger862 »

Thanks for your reply. Can you extrapolate on the actual steps to do this?
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Which steps are confusing to you?
craiger862
Forum Newbie
Posts: 3
Joined: Tue Jul 19, 2005 10:10 pm

Post by craiger862 »

Specifically what the headers would look like and how the pass-through part works. Do you have a case study or code example of how to accomplish this?

Thanks
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Here's an ancient thread on the topic, I know there is another one, which I think was better. I'll keep looking.
viewtopic.php?t=11246
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

And here's some sample code from one of my projects, but I only had to deal with PDF's. so the headers were simple.

Code: Select all

<?php 
################################################################
# This file is part of SlidingDoors                            #
# Copyright 2001-2002. Eric D. Nielsen, All rights reserverd   #
# SlidingDoors is availible for license under the GPL, see     #
# the COPYING file in the root directory of the install for    #
# the full terms of the GPL.                                   #
################################################################
include "include_others.inc";
session_start();

// Extract needed information to use in security check
$contents = $_GET["inv_code"];
$pieces = explode("_",$contents);
$primeID=$pieces[0];
$firstname=$pieces[1];
$MAC=$pieces[2];

// Test data against legal values (this is only moderate security
// designed to stop "curious" visitors, not real attackers)
$testMAC=MD5($primeID."_".$firstname."_".$CIB_SERVER_SECRET_MAC);
if ($MAC!=$testMAC)
{
     die ('Tampering with Get String detected - Class 1');
}
if (!preg_match("/[0-9]+/",$primeID))
{
     die ('Tampering with Get String detected - Class 2');
}
$query ="SELECT firstname FROM people WHERE peopleid=$primeID;";
$result=$db->query($query);
if ($result->numrows()!=1)
{
     die ('Tampering with Get String detected - Class 3');
}
list ($first) = $result->getRowAt(0);
if ($firstname!=$first)
{
     die ('Tampering with Get String detected - Class 4');
}
$testMAC=MD5($primeID."_".$first."_".$CIB_SERVER_SECRET_MAC);
if ($MAC!=$testMAC)
{
     die ('Tampering with Get String detected - Class 5');
}

// All tests passed, find the file to show the visitor
// and setup the download
$filename = "{$CIB_VAR_PATH}/temp/$unixname-$primeID.pdf";
$filesize=filesize($filename);
$simplename = "MAC-Invoice.pdf";
$user_agent = strtolower ($_SERVER["HTTP_USER_AGENT"]);
header( "Content-type: application/pdf" );
if ((is_integer (strpos($user_agent, "msie"))) && (is_integer (strpos($user_agen
t, "win")))) {
  header( "Content-Disposition: filename=".$simplename);
} else {
  header( "Content-Disposition: attachment; filename=".$simplename);
}
header( "Content-Description: File Transfert");
@readfile($filename);
exit;
?>
[EDIT: wow, knowing what I know now, I'm surprised that it worked :) There are typos in the stuff I cut and pasted from another site ("File Transfert"????) hehe. And those is_integer tests instead of !==FALSE.... sigh, I'll definitly be cleaning that up!]
Post Reply