Page 1 of 1
How to limit ability to download a file in a store environm
Posted: Tue Jul 19, 2005 10:51 pm
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
Posted: Wed Jul 20, 2005 10:22 am
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.
Posted: Wed Jul 20, 2005 10:26 am
by craiger862
Thanks for your reply. Can you extrapolate on the actual steps to do this?
Posted: Wed Jul 20, 2005 10:28 am
by nielsene
Which steps are confusing to you?
Posted: Wed Jul 20, 2005 10:32 am
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
Posted: Wed Jul 20, 2005 11:03 am
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
Posted: Wed Jul 20, 2005 11:20 am
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!]