SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 54
  1. #1
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Prevent direct access to php file

    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.,

    Code:
    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....

    Is this possible? Any takers?

  2. #2
    SitePoint Evangelist
    Join Date
    Jun 2006
    Location
    Wigan, Lancashire. UK
    Posts
    523
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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
    ---
    Development Projects:
    PHPExcel
    PHPPowerPoint

  3. #3
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Why are you using get.php at all? Could caller.php not obtain the data directly?
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  4. #4
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @Mark
    If there was a way without a cookie, that would be great. But if I follow you, you're saying:
    1. set a cookie from within caller.php,
    2. md5/crypt it with some key,
    3. pass hash to get.php,
    4. decrypt hash in get.php with key,
    5. compare timestamp in browser's cookie to timestamp from hash to ensure the same, else die
    6. compare current time() to timestamp received AND clear cookie,
    6. if > x seconds, deny access...
    7. 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.

    Code:
    <?php
    $fileonly=$_GET['param'];
    $fileonly=str_ireplace("..","",$fileonly);
    $fileonly=str_ireplace("/","",$fileonly);
    $fileonly=str_ireplace(" ","_",$fileonly);
    ?>
    <embed
    src="mediaplayer.swf"
    width="640"
    height="480"
    allowscriptaccess="always"
    allowfullscreen="true"
    autostart="true"
    flashvars="file=get.php?param=<?php echo $fileonly; ?>&showstop=true&autostart=true&bufferlength=3"
    />
    get.php has the following in it:

    PHP Code:
    <?php
    $fileonly
    =$_GET['param'];
    $file="../music/".$fileonly;

    header('Cache-Control: no-cache');
    header('Cache-Control: no-store');
    header('Pragma: no-cache');
    header('Content-Type: audio/x-mp3');
    header('Content-Length: ' filesize($file));

    $fh fopen($file,"rb");
    while (!
    feof($fh)) { print(fread($fhfilesize($file))); }
    fclose($fh);
    ?>

  5. #5
    SitePoint Evangelist
    Join Date
    May 2006
    Location
    Austin
    Posts
    401
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    You can easily store these files below your root directory which would make them completely inaccessable to the public.

    Then use the script you have in the bottom to dynamically deliver the file.

    Basically, your should look something like this:

    /home/mysite/public_html/

    Store files in:

    /home/mysite/files/
    Merchant Equipment Store - Merchant Services, POS, Equipment, and supplies.
    Merchant Account Blog | Ecommerce Blog

  6. #6
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @jestep

    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.

  7. #7
    SitePoint Enthusiast premiumscripts's Avatar
    Join Date
    Aug 2009
    Location
    PremiumScripts.com
    Posts
    55
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just do this:

    In caller.php

    PHP Code:
    definet('IN_CALLER'1); 
    In get.php

    PHP Code:
    if (!defined('IN_CALLER')) { die(); } 
    However, the most secure is just to put everything outside of the webroot.

    Edit: or are you invoking get.php via ajax? (on caller.php page)? If so, this won't work..

  8. #8
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  9. #9
    SitePoint Enthusiast premiumscripts's Avatar
    Join Date
    Aug 2009
    Location
    PremiumScripts.com
    Posts
    55
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If by call you mean require/include, then yes, as long as you pass it the correct path ofcourse.

  10. #10
    SitePoint Wizard
    Join Date
    Mar 2008
    Posts
    1,149
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Is this what you mean? (No cookies needed.)

    caller.php
    PHP Code:
    <?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)) ?>&amp;showstop=true&amp;autostart=true&amp;bufferlength=3"
    />
    get.php
    PHP Code:
    <?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
    //

  11. #11
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @premiumscripts

    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.

    @sk89q

    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.

  12. #12
    SitePoint Wizard
    Join Date
    Mar 2008
    Posts
    1,149
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  13. #13
    Programming Since 1978 silver trophybronze trophy felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, NSW, Australia
    Posts
    16,812
    Mentioned
    25 Post(s)
    Tagged
    1 Thread(s)
    To prevent a PHP file being called directly and only allow its use if included in another PHP file just add the following code to the top of it.

    Code:
    $file = basename(__FILE__);
    if(eregi($file,$_SERVER['REQUEST_URI']))
       {die('This file cannot be accessed directly!');}
    Stephen J Chapman

    javascriptexample.net, Book Reviews, follow me on Twitter
    HTML Help, CSS Help, JavaScript Help, PHP/mySQL Help, blog
    <input name="html5" type="text" required pattern="^$">

  14. #14
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @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...

  15. #15
    Programming Since 1978 silver trophybronze trophy felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, NSW, Australia
    Posts
    16,812
    Mentioned
    25 Post(s)
    Tagged
    1 Thread(s)
    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.
    Stephen J Chapman

    javascriptexample.net, Book Reviews, follow me on Twitter
    HTML Help, CSS Help, JavaScript Help, PHP/mySQL Help, blog
    <input name="html5" type="text" required pattern="^$">

  16. #16
    SitePoint Wizard
    Join Date
    Mar 2008
    Posts
    1,149
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by bakhlawa View Post
    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...
    Well, anyone knowledgable can easily download the MP3 file if you do it this way. Intercepting files served via HTTP is ridiculously easy.

  17. #17
    SitePoint Enthusiast
    Join Date
    Aug 2008
    Posts
    31
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the responses. This is what I've been able to do so far, but stuck...

    1. Multiple songs (for playlist) sent to caller.php
    2. Upon load, caller.php sets cookie in browser (w/ a known hash value)
    3. caller.php calls get.php?param=song1.mp3
    4. get.php checks to see if cookie set w/ known hash value
    5. 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

  18. #18
    Trash Boat mkoenig's Avatar
    Join Date
    Aug 2007
    Posts
    1,232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP-Nuke (original cms system)

    used to use something like

    PHP Code:
    <?
    if (!eregi("modules.php"$_SERVER['PHP_SELF'])) {
        die (
    "You can't access this file directly...");
    }
    ?>
    where modules.php was the name of the file calling the other files that they didn't want displayed directly.

  19. #19
    SitePoint Member
    Join Date
    Aug 2009
    Posts
    1
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by bakhlawa View Post
    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.,

    Code:
    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....

    If by call you mean require/include, then yes, as long as you pass it the correct path ofcourse.

  20. #20
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,196
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    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.

  21. #21
    SitePoint Wizard
    Join Date
    Apr 2007
    Posts
    1,399
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by bakhlawa View Post
    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.,

    Code:
    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....

    Is this possible? Any takers?
    Edit: Seems you don't like to use referrer.

  22. #22
    SitePoint Enthusiast
    Join Date
    Nov 2007
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well, I didn't really read through all the posts, but I'll throw in my two cents. It sounds like what would work for you is to check the referring domain. For instance, if they try to directly type in http://www.domain_name.com/get.php?id=123, there's no referring domain - because it's typed in directly. In this way, you will display the video only if the user arrives at the page from a link on your site. Does this help?
    My website: www.sitehatchery.com

    Recent Article: Dynamic CSS

  23. #23
    SitePoint Enthusiast
    Join Date
    Nov 2007
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I don't want to use referrer checks if possible.
    Why?
    My website: www.sitehatchery.com

    Recent Article: Dynamic CSS

  24. #24
    SitePoint Wizard TheRedDevil's Avatar
    Join Date
    Sep 2004
    Location
    Norway
    Posts
    1,196
    Mentioned
    4 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sitehatchery View Post
    Why?
    Because no one that know how it works rely on it. It can just not be trusted.

  25. #25
    SitePoint Enthusiast
    Join Date
    Nov 2007
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Let's say I'm on http://some_site.com and then navigate to http://some_site.com/test.php. I can test to see if the referring domain was "http://some_site.com" like this:

    Code:
    $refering=parse_url($_SERVER['HTTP_REFERER']);
    if($refering['host']==$_SERVER['HTTP_HOST']){
       echo "all is well";
    } else {
       echo "all is not well";
    }
    If they direct type the URL, it will echo "all is not well". If they arrive at that page from an external source which is not your site, it will echo "all is not well". But if you arrive at that page from an internal link, it will echo "all is well". So what's unreliable about that if that's the exact behavior you want? Am I missing something?
    My website: www.sitehatchery.com

    Recent Article: Dynamic CSS


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •