Please include the list when replying.

On Wed, Jul 6, 2011 at 10:20 PM, Kirk Bailey <>wrote:

> Um, assuming dishonest intent on the customers part, why would the token
> NOT be shared?

I meant shared within your system between customers. Whether you lock the
URL or not, if customers want to share the asset(s) they're downloading,
they will. Nothing you can really do about that.

> On 7/6/2011 4:34 PM, Stuart Dallas wrote:If you read back you'll note I
> said "generate a unique token linked to their account." At no point did I
> say the tokens would be shared between customers.
> Perchance, you have wroking code addressing this sort of issue? Could you
> post it here for all to examine please?

Not without breaching various copyright laws, but I can go through the
general process :). The following assumes you don't have a user DB, or don't
want the tokens to be connected to the users.

You have a data source (DB, whatever) that can store unique tokens with
other data. For the sake of example let's go with a standard DB. You create
a table that contains fields...
 - id (unsigned int auto_increment)
 - token (char(40) with a unique key)
 - expires_at (unsigned int)
 - product_id (unsigned int)

Customer buys something. Once payment is confirmed the site does this...

1) Insert a row into the table...
  - expires_at = time() + 86400 for 24 hours, time() + 900 for 15 minutes,
  - product_id = the ID of the product they purchased (modify this to
include what you need to deliver the item[s] they've purchased)
2) Retrieve the last inserted ID.
3) Hash (sha1) or encrypt (mcrypt_encrypt) the ID with a secret salt [i.e.
$token = sha1('this is the '.$id.' secret salt'); or similar]. If you use
mcrypt you may need to modify the result to be usable in a URL - read the
4) Update the row with that token. Catch duplicate key errors (unlikely but
possible), go back to 3 and try again with a different salt (adding a random
character will do).

How you generate the token is essentially irrelevant, so long as you can't
deduce another token from the one you have. I've found the above to be
sufficient, and it very very rarely generates a duplicate.

When a download URL is hit it runs a script that does the following...

1) Make sure a token has been provided in the URL.
2) Select the row corresponding to that token from the table.
3) If no row was found display "access denied" or "download expired", along
with your support email address.
4) If ($row['expires_at'] < time()), delete the row (optional) and
display "access denied" or "download expired", along with your support email
5) If not, use the product_id (or whatever) to deliver the download by
writing out the correct headers and then use readfile() to send it. Note
that if the downloads are large you may want to do this a different way so
you can catch and deal with client disconnects.

That's basically it. To keep the table clean you can have a cron job that
does a simple "delete from table where expires_at < unix_timestamp();" query
every 24 hours, or depending on how long the query takes you could simplify
it by running that same query during a suitable percentage of the download
URL hits.

You may also see benefits by connecting the table above to the user and
order tables (or whatever equivalents your system may have).

Hope that makes it a bit clearer.


Stuart Dallas
3ft9 Ltd

Reply via email to