?
Solved

PHP: how to generate protected download link

Posted on 2010-01-12
15
Medium Priority
?
666 Views
Last Modified: 2013-12-12
Hi X-perts,

I need the following functionality:

1) a customer enters username and password, which are verified against db tables. It is a simple thing; everything is clear here

2) if username and password are valid, the script returns a download link. The actual d/l directory cannot be accessed directly, but only via that script after login validation.

How can I do the 2nd part?

Please, suggest a few solutions.

Thanks
0
Comment
Question by:andy7789
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
15 Comments
 
LVL 15

Accepted Solution

by:
ludofulop earned 668 total points
ID: 26291857
put the files outside the web root, than generate link in the following form:
http://server/download.php?hash=23423536

then, your download.php should select filename from database, according to hash, and use headers and readfile() to output the file.
0
 
LVL 14

Expert Comment

by:Kalpan
ID: 26291882
You can do this as below logic..

1. Maintain the session once the user login is successful...on the index.php

if($user)
 // add this to session
 $_SESSION['user'] = $user;

2. Check if the current session is available in each page with your browser...

if($_SESSION['user']){
   echo $download_link;
}

hope this is helpful.....let me know if this the correct or i could put more logic for this...

Thanks

Kalpan

0
 

Author Comment

by:andy7789
ID: 26291975
Thank you, guys. I believe the 1st solution should work better, because I need the header and the d/l to be returned automatically instead of download URL, i.e.

1) my VB.NET application calls the URL www.myurl.com/download.php via HttpWebRequest and passes the username and password

2) download.php generates a response as the actual file headers.

probably, it is a messy explanation. My problem is that it is automatic download via the VB.NET desktop application. it would be easy to make it working as a normal web only access system.

Any comments on this?

Thanks!
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 7

Expert Comment

by:marklogan
ID: 26291980
As ludofulop said you need the read the contents of your file and return it through a script.

Be careful with large files though, you can get script timeout issues.
<?php

$contentLen=@filesize("yourfile.zip");

header("Content-type: application/zip");
header("Content-type: octet-stream");
header('Content-Disposition: attachment; filename="yourfile.zip"');

if($content_len!=FALSE){
 header("Content-length: $contentLen");
}

readfile("yourfile.zip");

?>

Open in new window

0
 
LVL 7

Expert Comment

by:marklogan
ID: 26291983
Incorrect variable ^
<?php

$contentLen=@filesize("yourfile.zip");

header("Content-type: application/zip");
header("Content-type: octet-stream");
header('Content-Disposition: attachment; filename="yourfile.zip"');

if($contentLen!=FALSE){
 header("Content-length: $contentLen");
}

readfile("yourfile.zip");

?>

Open in new window

0
 
LVL 7

Expert Comment

by:marklogan
ID: 26292003
The actually file location is then hidden from the browser headers.
11:48:00.007[18ms][total 18ms] Status: 200[OK]
GET http://127.0.0.1/test.php Load Flags[LOAD_DOCUMENT_URI  LOAD_INITIAL_DOCUMENT_URI  ] Content Size[177] Mime Type[application/x-unknown-content-type]
   Request Headers:
      Host[127.0.0.1]
      User-Agent[Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)]
      Accept[text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
      Accept-Language[en-gb,en;q=0.5]
      Accept-Encoding[gzip,deflate]
      Accept-Charset[ISO-8859-1,utf-8;q=0.7,*;q=0.7]
      Keep-Alive[300]
      Connection[keep-alive]
      Cookie[PHPSESSID=60dd006144d08b6b064dbc5405c9557f]
   Response Headers:
      Date[Tue, 12 Jan 2010 11:48:36 GMT]
      Server[Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.8b PHP/5.2.6]
      X-Powered-By[PHP/5.2.6]
      Expires[Thu, 19 Nov 1981 08:52:00 GMT]
      Cache-Control[no-store, no-cache, must-revalidate, post-check=0, pre-check=0]
      Pragma[no-cache]
      Content-Disposition[attachment; filename="yourfile.zip"]
      Content-Length[177]
      Keep-Alive[timeout=5, max=100]
      Connection[Keep-Alive]
      Content-Type[octet-stream]

Open in new window

0
 

Author Comment

by:andy7789
ID: 26292135
Thank you - it is getting better :)

>Be careful with large files though, you can get script timeout issues.

What would be a right size to avoid timeout problems? 20MB?

I understand that it is a PHP section, but I have to marry PHP and VB.NET. What happens if I call that file test.php from a VB.NET desktop application - see the code.

The below code works if I use a simple downloadable file test.txt

Dim wr As HttpWebRequest = CType(WebRequestFactory.Create("http://www.testthis.com/test.txt"), HttpWebRequest)

Sorry for asking a wrong zone question here.....
Imports System.IO
Imports System.Net
Imports System.Text
Class WebRetrieve
Public Shared Sub Main()
Dim wr As HttpWebRequest = CType(WebRequestFactory.Create("http://www.testthis.com/test.php"), HttpWebRequest)
Dim ws As HttpWebResponse = CType(wr.GetResponse(), HttpWebResponse)
Dim str As Stream = ws.GetResponseStream()
Dim inBuf(100000) As Byte
Dim bytesToRead As Integer = CInt(inBuf.Length)
Dim bytesRead As Integer = 0
While bytesToRead > 0
Dim n As Integer = str.Read(inBuf, bytesRead, bytesToRead)
If n = 0 Then
Exit While
End If
bytesRead += n
bytesToRead -= n
End While
Dim fstr As New FileStream("weather.jpg", FileMode.OpenOrCreate, FileAccess.Write)
fstr.Write(inBuf, 0, bytesRead)
str.Close()
fstr.Close()
End Sub 'Main
End Class 'WebRetrieve

Open in new window

0
 
LVL 7

Assisted Solution

by:marklogan
marklogan earned 664 total points
ID: 26292213
There is a PHP function

http://php.net/manual/en/function.set-time-limit.php 

that you can set the timeout period for the single script.

Also in your php.ini file there is a variable (max_execution_time) you can change that sets the timeout period for all scripts.



To integrate the PHP into your VB you will need to do something like

Dim wr As HttpWebRequest = CType(WebRequestFactory.Create("http://www.testthis.com/download.php?file=testfile.zip"), HttpWebRequest)


Note: Make sure you don't have spaces in your file names. This can mess up the header in PHP and the file isn't returned.
<?php

if(isset($_GET['file']){

 $contentLen=@filesize($_GET['file']);

 header("Content-type: application/zip");
 header("Content-type: octet-stream");
 header('Content-Disposition: attachment; filename="".$_GET['file'].""');

 if($contentLen!=FALSE){
  header("Content-length: $contentLen");
 }

 readfile($_GET['file']);

}

?>

Open in new window

0
 
LVL 7

Expert Comment

by:marklogan
ID: 26292226
The PHP script would return the file piece by piece and your VB puts the file back together.


Quick question: If you are hiding the download location in the VB code why does it need to be hidden by the PHP script? Or are you going to use the PHP script externally as well, say in a browser?
0
 

Author Comment

by:andy7789
ID: 26292263
The file is to be downloaded via VB code as a desktop application. However, the location has to be hidden to avoid reverse engineering and downloading the file directly via web browser without correct username/password
0
 
LVL 7

Expert Comment

by:marklogan
ID: 26292292
So they insert the username and password to access the VB program.

If the user is to watch network traffic and requests on their PC they might notice the VB application requesting

http://www.testthis.com/download.php?file=testfile.zip

If they then try this in the browser they will download the file. Is it feasible for you to check the username and password again in the PHP code?
0
 

Author Comment

by:andy7789
ID: 26296628
yes - this is how it should be done., i.e. the PHP code will check the username and password. The VB application should only pass the access details to the server (PHP)
0
 
LVL 111

Assisted Solution

by:Ray Paseur
Ray Paseur earned 668 total points
ID: 26296775
2) if username and password are valid, the script returns a download link. The actual d/l directory cannot be accessed directly, but only via that script after login validation.

How can I do the 2nd part?

Here is how I would handle it - all PHP.  

1. The login validation starts the session.  Either in that script or in the download script you would choose what file is to be downloaded.  The name of that file can be set in the $_SESSION array.

2. The download script looks in the $_SESSION array to get the file name.  If it is missing, obviously the client has not logged in so the file is not made available for download.  You can do your own set of validation rules here - it should be pretty obvious.

3. You take the file name out of the session and feed it to your version of this script.  AFAIK the function in here will force a download of any kind of file on any modern browser.

I like @ludofulop's suggestion to keep the files in a directory that is outside of the WWW root - less chance of an accidental data loss, and without the login credentials, your unauthorized visitors will not be able to get to the files.

Best regards, ~Ray
<?php // RAY_force_download.php
error_reporting(E_ALL);


// A FILE TO DOWNLOAD - THIS LINK COULD COME IN THE URL VIA $_GET OR COULD BE FOUND IN THE SESSION ARRAY AFTER LOGIN
$url = "http://a0.twimg.com/a/1252097501/images/twitter_logo_header.png";

// USE CASE
force_download($url);


// FUNCTION TO FORCE A DOWNLOAD
function force_download($filename)
{
   $basename = basename($filename);
   $filedata = file_get_contents($filename);

   if ($filedata)
   {
   // THESE HEADERS ARE USED ON ALL BROWSERS
      header("Content-Type: application-x/force-download");
      header("Content-Disposition: attachment; filename=\"$basename\"");
      header("Content-length: ".(string)(strlen($filedata)));
      header("Expires: ".gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y")))." GMT");
      header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");

   // THIS HEADER MUST BE OMITTED FOR IE 6+
      if (FALSE === strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE '))
      {
         header("Cache-Control: no-cache, must-revalidate");
      }

   // THIS IS THE LAST HEADER
      header("Pragma: no-cache");

   // FLUSH THE HEADERS TO THE BROWSER
      flush();

   // CAPTURE THE FILE IN THE OUTPUT BUFFERS - WILL BE FLUSHED AT SCRIPT END
      ob_start();
      echo $filedata;
   }
}

Open in new window

0
 

Author Comment

by:andy7789
ID: 26298887
Thank you guys, very helpful!
0
 

Author Comment

by:andy7789
ID: 26299803
I am playing with Ray's code and it works perfectly on small-to-medium files up to 12-13MB. Please, have a look at

http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_25049980.html

what could be the problem here?
0

Featured Post

Video: Liquid Web Managed WordPress Comparisons

If you run run a WordPress, you understand the potential headaches you may face when updating your plugins and themes. Do you choose to update on the fly and risk taking down your site; or do you set up a staging, keep it in sync with your live site and use that to test updates?

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Build an array called $myWeek which will hold the array elements Today, Yesterday and then builds up the rest of the week by the name of the day going back 1 week.   (CODE) (CODE) Then you just need to pass your date to the function. If i…
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
Suggested Courses

800 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question