A Beginners Guide to PHP Download Scripts

A Beginners Guide to PHP Download Scripts

Note: The author does not address handling security threats that are associated with the file download. This code is not intended for real-world use, without further security hardening. Please read the comments below for more details. Also, download this file for additional sanitation and security code.


In this tutorial, I’m going to show you how to write a PHP script that allows downloads.

To allow downloads from a server, you need to write a script that can communicate with it effectively.

PHP is a server-side scripting language and is well-designed for this task, with many versatile tools. I’m going to show you how to power the download using the HTTP header function.

Let’s take a look at the HTTP header function. This function is used to send a raw HTTP header to a client:

header( string, replace, http_response_code );

Let’s examine the three parts of that function:

  • string: This is a required parameter and specifies the header string to send.
  • replace: This is an optional parameter and its indicate whether or not to replace the previous header or add a second header. The default is TRUE which will replace the header, whereas FALSE allows multiple headers of the same type.
  • http_response_code: This is an optional parameter and forces the HTTP response code to the chosen value.

Here’s the function inside a complete download script:

// check if the download button is clicked if ( isset($_POST['downloadButton'] )) { // check if the filename is set $filename = ( isset($_POST["filename"]) ? $_POST["filename"] : null ); // check if file is in the directory or on the server if ( file_exists( $filename )) { // download the file from the server header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=filename here"); header("Content-Length: " . filesize( $filename) ); header("Cache-Control: must-revalidate"); readfile( $filename ); exit; } }

You can click here to download a working copy of the code in this tutorial.

Let’s break down the examples of the HTTP header function in that code:

Content-Type

This declares the file as a binary and setting its type. The HTTP Header string parameter is set to Content-Type: application/octet-stream, or to the specified file type if needed. This enables the browsers to treat the file as a binary. Note that application/octet-stream can be used to dynamically refer to all file types.

header("Content-Type: application/octet-stream");

Content-Disposition

The HTTP Header string parameter is set to Content-Disposition: attachment. This forces the browser to display a download dialog box, thereby making the download possible.

header("Content-Disposition: attachment");

By using the HTTP header Content-Disposition: attachement, you can also supply or add a recommended filename to be displayed by the download dialog box. This is done using a concatenated filename attribute.

header("Content-Disposition: attachment; filename=file name here");

When no filename is specified, the current script filename is used. Also, avoid separating the attribute filename with a blank space like so, file name=file name here, because blank spaces will break the script.

Content-Length

The HTTP Header string parameter is set to Content-Length: filesize=file size here. This is used to display the file size information in the download dialog box. To easily get the file size, you will use the PHP filesize( ) function and pass the filename to its parameter.

header("Content-Length: filesize=" . filesize("file name here") );

Here is the filesize() function which returns the size of the specified file.

filesize( string filename );

Cache-Control

The HTTP Header string parameter is set to Cache-Control: no-cache, or Cache-Control: must-revalidate. This is because most information about the file is cached, so it’s important to control the cache.

header("Cache-Control: no-cache");

The readfile function

To retrieve the actual file contents form the server, you can use the PHP readfile() function. This file function comes in handy because you don’t necessary need to write any conditional loop statements to loop over all of the file’s data.

This function reads a file and writes it to the output buffer. It returns the numbers of bytes read on success, or FALSE and an error on failure.

readfile( filename, include_path, context );
  • filename: This is a required parameter. It specifies the file to read.
  • include_path: This is an optional parameter. Set this parameter to ‘1’ if you want to search for the file in the include_path (in php.ini) as well.
  • context: This is an optional parameter. Specifies the context of the file handle. Context is a set of options that can modify the behavior of a stream.

Security checks and limitations

To prevent the user from downloading any files from the server by altering the script, the filename should not be passed as a query string in the URL using the $_GET method. Always use the $_POST method to send the filename along with the form whenever it is submitted.

You can use the HTTP header Content-Type : file type to limit the files users can download from the server. For example, if you have an image file with .png extension on the server, and you want users to be able to download it, instead of using Content-Type: application/octet-stream, you can set Content-Type header to the specified file type like so – Content-Type: image/png. This will limit the types of file users can download, and also, prevent them from downloading sensitive content from the server.

Why is this important? If you send a URL query like so localhost/download.php?filename=download.txt, the user can try to manipulate the URL query and change it from ?filename=download.txt to this – ?filename=anything. Or the user may try a URL query injection, which might break your script.

Author

0 0 votes
Article Rating
Subscribe
Notify of
18 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Phil Taylor, The Joomla Expert
Phil Taylor, The Joomla Expert
7 years ago

DO NOT USE THIS CODE EVER – It is insecure and the author has absolutely no idea about security at all!

John Bkk
John Bkk
7 years ago

Perhaps you could tell us why ? As a newbie to PHP such a generalisation is a waste of time unless we know why the code is insecure ?

Phil Taylor, The Joomla Expert
Phil Taylor, The Joomla Expert
7 years ago
Reply to  John Bkk

Why? Because its clear the author doesn’t understand the basics of security and/or PHP developing at all. This blog post is not fit to be published!! If you want to download a file – DONT do this. Its a great example on how NOT TO DO THIS, or how to write insecure code!

John Bkk
John Bkk
7 years ago

Sorry, i do not get it. Why is it insecure or will that mean giving away something that may be of use to someone with not very nice motives ?

Michael Babker
Michael Babker
7 years ago
Reply to  John Bkk

For starters, unfiltered user input. Simply changing where the params are passed in from GET (the URL query string) to POST isn’t enough to consider the data source safe or secure. The filename parameter isn’t in any way scope limited, I can pass any string to that value and if the user the PHP process is running as has read access to the file and it exists in the filesystem, I can get it. Configuration files are a great place to get “sensitive” info.

Toma
Toma
7 years ago
Reply to  John Bkk

Quote: “Content-Type: image/png This will [snip] prevent them from downloading sensitive content from the server.” -> This is so untrue!

Mateusz Podraza
Mateusz Podraza
7 years ago
Reply to  John Bkk

*coughs* localhost/download.php?filename=../../../../../../../../../../../etc/passwd

Phil Taylor, The Joomla Expert
Phil Taylor, The Joomla Expert
7 years ago
Reply to  John zenith

This article still contains factually incorrect statements, and wrongly gives the assumption of the use of headers to restrict content types. It is clear you don’t understand real world basic security in PHP. This article should be deleted. Its not serrving the purpose it was written for.

Steve smith
Steve smith
7 years ago

wow u just came and strike the author

Phil Taylor, The Joomla Expert
Phil Taylor, The Joomla Expert
7 years ago
Reply to  Steve smith

Those that teach should know what they are talking about and should not teach people how to code insecurely! Facts are facts.

Griffin
Griffin
7 years ago

instead of going after the Author why don’t you show us what to do apparently the Author is better than you,and btw i believe programming is all about community and improving each other’s code

Steven ŖöĦǟn Malip
Steven ŖöĦǟn Malip
7 years ago

Please I really need help on how to set it up… Please email me @เรื่อศรีจันทร์ @เรื่อศรีจันทร์gmail.com">Stevenrohan9@เรื่อศรีจันทร์gmail.com. how to setup force download script for media files.

Igor Oliveira
Igor Oliveira
6 years ago

Hi. Can you check this article? Optimize File Downloading in PHP, by CodexM, published in devshed domain.
I tested it on my computer, and I think it is safe, since it gets the file on server side and streams it. And folder with files can be public access denied.
Any help will be welcome! Thanks.

David
David
6 years ago

This code is extremely insecure. You could create a hashed value and store it in your db and also store a dummy name and the real name of the file. The dummy name should be I simple name which would be shown to user during download and the real name which would be used in locating the file should be a randomly generated value. The hash value should the be used in the get method to query db for file. Also verify important use prepared Statements and validate inputs

Anthony
Anthony
4 years ago

So much negative feedback on this code block all about security, but no one has actually stated what is wrong with it?! Seems to me that a lot of people like to talk…

Obviously this article isn’t about ‘security’, it’s about how to download a file!

You should have a good understanding of security in PHP and in web generally and adapt this code for your application…. it’s not written to be directly copy and pasted.

This article has some good useful information; but use your own knowledge of PHP to adapt it and make sure your Apps are secure.

For an example, say this was used to download software that’s been purchased via an online store.

You could use an order ID, an order verification token (generated when you create an order) and a media ID to validate the download… then download the file with the URL & filename in the DB after validation.

Clearly this isn’t written to be copy & pasted… use it with your own knowledge and adapt it for your Apps purposes.

Thank you to the author for going to the effort of putting the information out there.

There is no need to be so rude to the author…!

Chibututu
Chibututu
3 years ago
Reply to  Anthony

Dear Anthony, I think you made the most valid point as regards the attacks on “security” to this post. The author stated in the beginning part of the post that it wasn’t for real time use. Hence, exempted himself from any claims on security expertise. I think a beginner php developer shouldn’t just take one post as all, but should find other posts related to the topic they want, to see if that’s how things are done. Also, an experienced developer should know what to do with any code as long as there are a few points in it. Thanks for highlighting your point once again, well done!

Chibututu
Chibututu
3 years ago

I have seen some attacks pointed to the post as regards security. But the author stated clearly in the beginning part of the post that it wasn’t for any real world use. Hence, exempted his post from claims on security expertise. I think whoever’s reading should just focus on the basic functions that aid download in php and not “download-security” per say. The author has done nothing wrong, since he already said that this code wasn’t for real world use.
For beginners, follow up stackoverflow.com, Janet Valade pdf, tutorialrepublic.com, codewithawa.com, mmtutus on youtube and w3schools.com for a broader explanation on the topic and even other topics in php. I highly recommend mmtuts on YouTube for php tutorial. Feel free to ask me any questions on php, if you’re a beginner @เรื่อศรีจันทร์ @เรื่อศรีจันทร์gmail.com">chibututu2000@เรื่อศรีจันทร์gmail.com. Also, @เรื่อศรีจันทร์ Anthony, respects to your comment. Well done!

18
0
Would love your thoughts, please comment.x
()
x