Link to home
Start Free TrialLog in
Avatar of davidcowler
davidcowlerFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Can't access a file in a folder protected by .htpasswd/.htaccess

Hi experts

I'm stuck again! I have confidential user information in a folder that is protected by .htaccess and .htpasswd files which is working fine. Thing is, when I try to include images from this folder in a webpage built by the heredoc feature of PHP, a login box appears before the page does.

Is there a way of specifying a username/password to allow the image to be included on the page or is there a better way round it?

Also, is it safe to put .htaccess/.htpasswd only in the first level of folder protection required or should these files be in all subfolders too in order to protect those subfolders?

EG:  /topfolder/nextfolder
  to protect both levels, should the .ht files be in both folders or just /topfolder/ ?

Thanks
DC
Avatar of Ivanov_G
Ivanov_G
Flag of Bulgaria image

Have a look at this:
http://bg2.php.net/features.http-auth
it shows you how to authenticate in order to get the files.

It is good enough to have .htaccess/.htpasswd in the first level folder. It applies to all subfolders. Of cource if you need different settings you have to put separate files in the subdirectories.
Hello,

If you want to protect some information on your server, you should protect ONLY that information. It is not a good technique to put confidential information and public images into the same folder protected with .htaccess; because then you have to make workarounds for displaying the images and this is not ok.
You should put ONLY the confidential information (the info that really needs authentication to be displayed) into the .htaccess-protected folder, and the images and all things that are public, in other folders.
Avatar of davidcowler

ASKER

Ivanov_G, do you understand the page you directed me to? I'm not sure I do! It seems to want to access a web page whereas I want it to access an image. Maybe I've altered the path incorrectly?

if ($source = get401Page('/absolute/path/file.php?get=value')) {

This suggests it's looking for a PHP file with a GET variable. I don't understand what I need to change this to. If I'm looking to access pic1.jpg for example and the path is /mydomain/members/images then should the above be:

if ($source = get401Page('/mydomain/members/images/pic1.jpg?get=value')) {

???

Thanks for help so far.
DC
If you have .htpasswd in /mydomain/members/images/, it apply to all files and subdirectories. So in order to get the image, you have to authenticate with username/password. Look at the examples on the URL I gave you.
This isn't helping I'm afraid. Those examples seem to all be about giving then user a popup box to login with. I don't want that. I just want to be able to say, this is the image i want, it's in this folder, username and password for which is x and y, let me display it within my page. It's got to be a one-liner, surely? Otherwise I'm very quickly going to be losing my faith in PHP.

I've tried setting $PHP_AUTH_USER and $PHP_AUTH_PW and beyond that I don't know what to do with it. I get a blank image. If I right click to "show image" I get the login box. If I login the picture shows. The authentication MUST be invisible to the user. Using the two headers as shown in the example always gives the login dialog box. I never want to see it.  

Any ideas?

???? ;(
http://bg2.php.net/features.http-auth
Example 34-2. Digest HTTP Authentication example

Take a look at the example. It is exactly HTTP authentication example - what you need
I haven't got the time or the patience to fathom that code out. It's got a pop up box in it and it's checking a username and password. I KNOW what the username and password is, it doesn't need checking, I just need to tell the server that. That code doesn't even specify where the file is! No domain, nothing!

This stuff is just gonna have to go in an unprotected folder unless anyone can explain this to me or has any other bright ideas. I can upload images to the folder without a problem. Only hitting a brick wall when trying to retrieve them.
The Authentication is set in $_SERVER['PHP_AUTH_DIGEST'], so you are logged and don't get the popup box when you request a file.
I still haven't got the time to figure out that chunk of code. I tried the following, which did not work. (note i have removed the part that sends the headers and moved the function to the top of the script). Program stops at point 1a. I do not believe that this code runs as a standalone script. I mean, how do you specify a password?

I have tried to set the username before the PHP_AUTH_DIGEST thing but it seems to need info from that call before it will work, but the AUTH_DIGEST won't work without a username.

All I really want is a couple of lines of code that say "have this username and password and let me have the image files in return". It can't be done, can it?

<?php
$realm = 'Restricted area';

//user => password
$users = array('myusername' => 'mypassword');

// function to parse the http auth header
function http_digest_parse($txt)
{
   // protect against missing data
   $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
   $data = array();

   preg_match_all('@(\w+)=([\'"]?)([a-zA-Z0-9=./\_-]+)\2@', $txt, $matches, PREG_SET_ORDER);

   foreach ($matches as $m) {
       $data[$m[1]] = $m[3];
       unset($needed_parts[$m[1]]);
   }

   return $needed_parts ? false : $data;
}

$users[$data['username']] = 'myusername';  //<why does this not set the username?


// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
   !isset($users[$data['username']]))
   die('Wrong Credentials! 1a');


// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if ($data['response'] != $valid_response)
   die('Wrong Credentials! 2a');

// ok, valid username & password
echo 'You are logged in as: ' . $data['username'];

?>

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>New Page 1</title>
</head>

<body>

<p><img border="0" src="/members/photos/123.jpg" width="193" height="266"></p>

</body>

</html>


ASKER CERTIFIED SOLUTION
Avatar of floorman67
floorman67

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of floorman67
floorman67

for instance, in a .htaccess/.htpassword protected directory folder,  you can open up a file with php, like a mp3 file, and stream it to a flash player embedded in a web page, and furthermore you can for any file for a direct download routed through php then stream it through php to the client becasue the server is asking for it, not the client.

But you can not ask for it DIRECTLY through the client via html output.

see the difference ?

so no .. it cant be done.

Hi Floorman67

Thanks for your input. What I actually have is a folder of images which I don't want people to be able to access willy-nilly. For example, if you know the path and filename (which you can easily get by right clicking and choosing "properties") then you can put that straight into a browser and get the image. The php code builds a web page using the heredoc feature and tries to drop the paths to the images within it - but that's where it asks for authentication.

Now I know this can't be done I'll probably give the images "random" filenames so they can't be guessed if you know one (they are in sequences so you can guess the next filename).

Is there any clever way of disabling right click by the way? That would help too.

Thanks
DC
Another solution, I don't know if it will work, but just to mention it:
Create a file, let's say displayimage.php which will take a parameter: displayimage.php?img=7056

The id 7056 will tell you which image you have to open (you will define it by your own). Make your images readable by 127.0.0.1 ONLY.

In displayimage.php something like:

<?
  $id = $_GET['img'];
  $imagePath = 'SOMETHING_YOU_DETERMINE_HERE';
  header ("Content-type: image/jpeg");
  readfile($imagePath );
?>

I wrote it by memory. Not sure if it works, but You got the idea.

You can also check some $_SESSION variable in displayimage.php to make sure the file is not called directly like the example you gave above and then

<img src="displayimage.php?img=1234" ...
as per your request, here is an example of removing click and drag and right click capabilities from your web page within the <BODY> HTML tag.

<body oncontextmenu="return false" ondragstart="return false" onselectstart="return false">

and as to the other request and the information the other guy posted,

what you are talking about is two different things.

on the on hand, you are talking about a forced download routed through php, which is a SERVER SIDE REQUEST and CAN punch through any .htaccess/.htpassword protection becasue the permissions are absolute becasue the server has permissions for everything locally.

On the other hand, you are talking about a client request for information to be dispayed on a web page in a client browser via the browser and you can not automate the process from a server request becasue it is the client browser making the request TO the server (which requires authentication) for data.

There is an inherent security risk here for forced downloads and it will not automatically display the data but instead a download box.

even wrapping it in php with with a data tag ID# it can not be done, and the absolute path will show in properties and in the temporary internet cache.

The browser needs the absolute path and can not punch through .htaccess from a client request that isnt a forced download with a popup confirmation  routed through php.

The thing is that it requires some action on the user or any web page could use server side script to open anything on a client browser/machine and possibly run or display malicious content.

even IF you run it through php to punch through any .htaccess/.htpassword protection, there is still client side browser USER SECURITY to think about ... and they are protected form displaying any automated content without some kind of user action like clicking to accept the forced download or disabling images or objects embedded and forcing the user to refresh the page to get the content ... user action is needed.

Here is an example :

I set up this web page for you as an example: http://obscurity.ws/SECURE_DOWNLOAD_SCRIPT/

if you click this it will display some protected downloads.

it also attempts to automatically display an image using the protected download URL (as the gentleman above suggested using an ID# but I used an ID string).

as you can see the real file location is hidden and no one can get it.

as you also will see I tried to automatically place and image on the web page using the <IMG> tag and it at first shows you a disabled image until you refresh the page (manual user action required), then it is stored in the temporary internet cache, so this wouldnt be a valid production solution because when the user first enters your site they will be greeted with a disabled image.

In conclusion, while routing files through PHP is an excellent solution for some forms of content and resource protection, it does not serve as an adequate protection for automatic image placement on web pages for production sites becasue of the manual input required to see them in the first place.

HOWEVER there are certain things you can do with some fancy javascript to overlay a transparent image so when they right click the image they save the transparent one, but this solution is  a waste if you are using an anti right click I posted, and they can always get it from the cache.

Hotlink protections is probably your best bet.
ok i just retested it a few times, clearing my cache between uses, and sometimes i got the red x disabled image until i refreshed the page and sometimes I didnt ... very strange.

Play wiith it a while if you want and see if its an adequate solution for you.

Might be my server configuration.
Still got to try this... have been away for a while. Adjudicators - please don't close this one yet. Thanks :)
Ok, I don't need to "hide" all the images if I give them random filenames, so that kinda solves that. I also realised that putting an index.php in a folder will prevent a client browser from displaying the file contents of that folder and the index.php script can simply redirect the user to the home page or a "you can't do that you spotty oik" page.

I am awarding the points to floorman67 as he ended the wild goose chase by pointing out that the authentication I was looking for isn't possible for security reasons.

Thanks for all contributions :)