I have caller.php that calls a second file get.php with some parameters (note get.php is not a runtime include, but called only when a user clicks a button), i.e.,
file=get.php?doc=../../abc.mp3
Both caller.php and get.php are in publicly accessible folders. abc.mp3 resides in a level higher than public and not open.
I want get.php to work ONLY when called from caller.php. If get.php called directly from the browser it should result in an error message.
I don’t want to use referrer checks if possible. Also, not looking for foolproof method, but something that is reasonably secure or will require a few steps each time to break.
I have considered passing a $secretkey from caller.php to get.php but anything I pass can be seen in the view source or headers? Also, session variables don’t work well I think as I don’t want user to go to caller.php first and then right after do a direct call to get.php because session key is set as that will trick get.php into working…
You could use a secret key that changes whenever a call is made based on a relatively arbitrary value.
e.g. set a cookie to a unique value (timestamp) before making the call, and pass a hash of the cookie value in the url, then clear the cookie once the response has been received. On the server side, the PHP script tests the cookie value and matches it against the hash in the URL, and rejects if the two don’t match
@Mark
If there was a way without a cookie, that would be great. But if I follow you, you’re saying:
set a cookie from within caller.php,
md5/crypt it with some key,
pass hash to get.php,
decrypt hash in get.php with key,
compare timestamp in browser’s cookie to timestamp from hash to ensure the same, else die
compare current time() to timestamp received AND clear cookie,
if > x seconds, deny access…
If get.php called directly without hash or with old hash, it would deny.
Did I get this right?
@Silver
I am actually embedding a flash player in a page using code similar to that below. get.php basically fopens and freads the requested mp3 file and sends to the user. If I do this inside caller.php (in the “file=” part below) the fopen and fread gets executed while the page loads, taking really long to load and breaking the player…not sure why, but it works fine if “file=” points to another php file (get.php) that streams the mp3.
Are you referring to get.php that is called by caller.php?
I tried doing this, but caller.php (which is in public_html) couldn’t call get.php which was residing above the publicly accessible directory. Is there something that needs to be enabled/configured on the server that would allow caller.php to run get.php which is in a non-public folder?
I have access to the server so can make changes if required, hopefully not too daunting.
No, this isn’t Ajax. Just a regular call to another php file.
Can I call get.php if it’s oustide the webroot? That would be ideal as no one (except my caller.php script of course) would be able to call it directly.
<?php
$file_only = basename($_GET['param']);
$file_only = str_replace(" ", "_", $file_only);
// These don't really matter since you're just reading from one folder and you don't check extensions or anything, but I like to do it in case
$file_only = str_replace("\\0", "", $file_only); // Poison null byte
$file_only = str_replace(":", "", $file_only); // NTFS ADS
// Key
$shared_key = "something";
$timestamp = time();
$hash = hash_func_hmac($file_only, "$timestamp-$shared_key");
$url = printf("get.php?param=%s&t=%d&key=%s", urlencode($file_only), $timestamp, $hash);
?>
<embed
src="mediaplayer.swf"
width="640"
height="480"
allowscriptaccess="always"
allowfullscreen="true"
autostart="true"
flashvars="file=<?php echo htmlspecialchars(urlencode($url)) ?>&showstop=true&autostart=true&bufferlength=3"
/>
get.php
<?php
$file_only = $_GET['param'];
$shared_key = "something";
$in_hash = $_GET['key'];
$in_timestamp = intval($_GET['t']);
$in_hash = $_GET['key'];
$expected_hash = hash_func_hmac($file_only, "$in_timestamp-$shared_key");
if ($expected_hash != $in_hash || $in_timestamp < time() - 60 * 15) { // You could do the timestamp check before computing the hash
header("HTTP/1.1 404 Not Found");
exit;
}
// I would still do a security check again
// And then load the file
//
No, I didn’t mean include get.php. I meant invoking (calling) get.php from caller.php when the user requests the file…see flash embed code that calls get.php to retrieve mp3 file.
Interesting, except that after calling caller.php, the user could directly call get.php (with the same parameters that were passed) and because the hash would still be valid (within 15 minutes), it would send back the file. So, users can call get.php directly to obtain the file…I want to block any direct calls to get.php.
Uh, define “direct call.” How are you going to play a sound file in Flash if your browser never sends a call to get.php? Do you mean that you only want Flash Player to be able to play it? That means that you will have to encrypt the sound file and not use Flash (or use Flash Media Server), because at the server, there is no sure way to tell whether Flash Player was the program issuing the request or not.
@felgall
I’m not including the file, but it gets called by the flashplayer, so this check won’t do (as the call from get.php is also a separate call as far as the server is concerned)
@sk89q
I understand what you’re saying, I guess for php and the server there is no difference between the flash player in caller.php calling get.php and an evil visitor just typing the get.php directly into the browser.
I’m still lost…I just don’t want others to call get.php and my server just starts sending back the mp3 file that I’m trying to protect, else why even bother…
So what you need is a session established by the page that is calling the code and to check in the get.php that the same session still exists. Then if it is called from somewhere other than your page then the session will not exist.
Thanks for the responses. This is what I’ve been able to do so far, but stuck…
Multiple songs (for playlist) sent to caller.php
Upon load, caller.php sets cookie in browser (w/ a known hash value)
caller.php calls get.php?param=song1.mp3
get.php checks to see if cookie set w/ known hash value
if cookie set, stream mp3 file and delete cookie, else die
I know this isn’t very secure, but at least dissuades the casual browser.
The problem I’m having is when the flash player in caller.php completes playing song1.mp3 and proceeds to song2.mp3. At this stage, in the flash embed code, it’s just making another call to get.php?param=song2.mp3 without setting a cookie again (because it’s not loading the file caller.php again which would’ve set the cookie). As a result, get.php can’t find the cookie (because it got deleted after streaming song1.mp3) and it rejects the call.
Any ideas on how to set the cookie again when the flash player makes the second or third or fourth calls…? I feel I’m almost there, but not quite!
Note: Same problem faced even if I use session variables instead of cookies
What you want to do bakhlawa is impossible. You can not “protect” anything you provide access for on the internet from the fact that people can download it.
By default, the second you put it on the internet you have already lost.
You can only make it harder for them to manage to save the content.
However in this case, there is so many ways to “download” the content that I would not even care about adding protection to it. Instead I would just make certain that the download solution could not be abused to fetch any other files.
As you mentioned you have issues regarding the cookie, to solve that you need to rewrite the flash application, telling it to connect to the file for each song.
Though you do NOT know what connect to caller.php, it can be the flash script, but it dont need to be. Hence, the current protection is not even worth the time it took to implement it.