Do not allow hot-linking of images unless logged in - php

I just ran into something today that I am not sure how it is being done.
I know a few things:
The site is done in php
There is an image gallery, a url would be something like
http://www.example.com/girls/Cyn/sets/getImage/1170753147/7.jpg
I can see that url, as long as I am logged into the site. It does not appear to be referrer based, as I took the url, made a new window in a browser, and was able to load it while still logged in. In doing so, I had no referrer.
The second I log out, I am redirected to a please register/login page.
This is a heavy hit site.
How is this done? I can tell they are running apache on Cent. When I login, I am given a cookie, that has a hash of something, which I am sure they are using to lookup an id to make sure I am allowed to be logged in.
However, the above is a direct request for a resource that is just a jpg. There has to be some communication then with Apache, and their database to see the state of that request. How would merely loading a url, send off a cookie value to apache that could then pass it off to a database?
I am about to embark on a paid membership site, and will need to protect images in the same way. This was not http auth, this was form based login, and I am at a loss as to how this was done. Any ideas?

All requests go through the web server. If a site sets a cookie, then all your requests to that site will include the cookie contents until that cookie expires or is removed. It doesn't matter what you're requesting it only matters where you are requesting it from.
If you have firebug open up the 'Net' tab when you're on this site and check all the requests you have made. You'll see in the request headers a 'Cookie' line. This will be on every resource requested: the images, the stylesheets, everything.
If Apache is the web server then it could use mod_rewrite to direct your request or it could pass it to PHP or Perl or something else that can check the cookie and output the image if valid or redirect if not.
Here is a php example (image output taken from php.net):
if(valid_auth($_COOKIE['auth'])) {
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
} else {
header('Location: /login');
exit;
}

It's probably a web application that uses a session cookie for authentication and redirects if the session has not been authenticated.
Pretty much any web framework has plugins for this sort of thing. There might even be apache modules to do it, but I haven't seen one.

You must create a "getter" for the images. The images must be stored in a folder outside of the public accessible directories.
/public_html
/js
jquery.js
index.php
getimage.php
/private_images/
myimage.jpg
Note that private_images directory is not accessible when you: http://www.mysite.com/private_images
Now, to create the "getter" script.
/* This is getimage.php */
if(!isset($_SESSION['is_logged_in'])) {
header('Location: /login');
exit;
}
/*
Get the image_name from the URL
You will be using something like: http://mysite.com?image_name=flowers.jpg
This is the way to get the image.
*/
$path = "/var/www/html/private_images"
$name = $path.'/'.$_GET['image_name'];
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/jpg");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
If you missed the comment above, you can do this to retrieve the image:
http://mysite.com?image_name=flowers.jpg

However, the above is a direct request for a resource that is just a jpg. There has to be some communication then with Apache, and their database to see the state of that request. How would merely loading a url, send off a cookie value to apache that could then pass it off to a database?
Every single http request is sent to a web server. The web server will then decide how to handle the request, based on a set of rules. By default, Apache has a simple handler that just sends the requested file back to the user. There is however no reason why you couldn't configure Apache to handle all requests with a php-script. On a high traffic site, you would probably solve this differently, since it's a bit expensive to fire up php for each and every image to show, but in theory you could just make a mod_rewrite rule that pipes all requests matching a particular pattern (Such as ^girls/Cyn/sets/getImage/.*) to a php-script. This script would then read the actual file from somewhere outside the web root and print it out to the user.

Related

PHP - Issue with Retrieving Image and Redirect

I have a PHP file that performs a query to an external API using a query parameter to retrieve the image from the database. This particular API doesn't return the image, but rather generates a new URL that can be used for a short period of time (associated cookie involved).
So my PHP file might be found at:
myserver.com/getFile.php?id=B9590963-145B-4E6A-8230-C80749D689WE
which performs the API call which generates a URL like this:
myserver.com/Streaming_SSL/MainDB/38799B1C4E38F1F9BCC99D5A4A2E0A514EEA26558C287C4C941FA8BA4FB7885B.png?RCType=SecuredRCFileProcessor&Redirect
which I store in a PHP variable - $fileURL. I then use:
header('Location: '. $fileURL);
to redirect the browser to the URL that shows the image. This is all working well, but has created an issue with a service that I integrate with. This service caches the 2nd (redirected URL) which causes problems, as it essentially only works the first time it is generated due to the use of the session cookie. I need to come up with a solution that will allow them to cache a URL that will continue to work that shows the image.
I'm wondering is there a way that I can download the image and show that somehow, without having to redirect the browser to a new location here and thus allowing the original URL to continue working if it is cached?
To get image or file do this:
$getImg = file_get_contents('myserver.com/getFile.php?id=B9590963-145B-4E6A-8230-C80749D689WE')
Name the file
$fileName = 'name.png'
Save the image on your server (in directory) to view it later or show somewhere else with url.
$saveFile = file_put_contents($fileName , $getImg);
Load the file like you wanted after you have saved it.
header('Location: '. $fileName);
Hope it helps.

Automatically get URL of a site displaying my image?

I've been trying to get the URL (including GET parameters) of a site that is displaying my image. This is because I want to extract one parameter of the URL.
A friend told me that she knew someone that could achieve this, but I don't know if he was doing it with an image. Also I don't think I can do it with a link because when going to external sites it will appear a warning page saying that you're being redirected outside, so if I put a link to my page and someone clicks, I will get the referrer URL of redirection warning page. I can't assure if my friend was telling the truth about this, but it's very likely that it was true.
All I could get with the image was the IP and other things of the HTTP header, but the referrer part is empty and I thought that the referrer contained the full URL I'm talking about.
This is what I have tried.
First the img tag in the other site in BBCode:
[img]http://______.com/get_image.php?i=myimage[/img]
And in my site this script in PHP, although any language that does the work would be good for me:
<?php
// Get name of image to be displayed (non-sanitized here for simplicity)
$filename = $_GET["i"];
// Here I want to get the site where image is being viewed
if (!empty($_SERVER['HTTP_REFERER'])) {
$visitor_url = $_SERVER['HTTP_REFERER'];
} else {
$visitor_url = "none";
}
// And write the referrer to a file just to test if it works
$fp = fopen('referer.txt', 'w');
fwrite($fp, $visitor_url);
fclose($fp);
// Eventually display the image
header('Content-Type: image/png');
readfile($filename . '.png');
?>
So my questions are:
Is it possible to get full URL of a site that is displaying my image?
If not, is there any other method to get the full URL?
Thank you in advance.
Note: I don't have any permision in the other site where I'm posting the image, I'm just an user there. Please tell me if I'm missing something or I have to ask this in another way, I'm new to StackOverflow.
Try REMOTE_HOST instead of HTTP_REFERER:
// Here I want to get the site where image is being viewed
if (!empty($_SERVER['REMOTE_HOST'])) {
$visitor_url = $_SERVER['REMOTE_HOST'];
} else {
$visitor_url = "none";
}
The web server where you are serving the image will need to be configured properly. If using Apache, this is with HostNameLookups On.
See http://php.net/manual/en/reserved.variables.server.php
Normally browsers are sending full referer with all URL components including query parameters - $_GET params. If they don't then there is no other way to achieve that URL while passing throught an image content.
Sometimes sending referer may be blocked, for eg. in some batch URL processing using some crawler like program/script or on some proxies.
In PHP receiving referer is done by $_SERVER['HTTP_REFERER'] because it's normally just http header from request and it's the only $_SERVER array key with referer info.
You added the .htaccess tag so I think you're using the Apache web server. If you'd like to prevent the issue entirely, you can disable hotlinking entirely by going one layer lower. Instead of managing in PHP, you can configure the web server to not serve content to domains other than the one you are hosting.
Check out the guide for more details.
I fixed this problem by switching my site (where image is hosted) to HTTPS. The code in my question was doing its job correctly.
It looks that HTTP_REFERER was blank because of it coming from an HTTPS site and my site being HTTP it would always send it blank. I was aware that it could be a problem, but didn't make much sense for me because HTTP_REFERER was also blank when coming from another HTTP site (which I think it's not normal) so I thought the error was in another place.
Usually HTTP_REFERER is sent when it comes from and goes to:
from HTTP to HTTP
from HTTPS to HTTPS
from HTTP to HTTPS
But it's not sent when it comes from and goes to:
from HTTPS to HTTP
And in my case, I don't know why, it wasn't being sent from HTTP to HTTP which was confusing me.

check every request coming to apacheweb server

I have a weird requirement asked , to check time difference between each request and response
I have a PHP file (request.php) serving a image as it's response, so i have to check time difference between GET request.php and GET image request. for each and every request.
For example:
the time request.php is made lets say 12:40 AM
it will response a image, when image is inserted in a web page i get a image request say 12.42 AM.
so my requirement is to calculate time difference between request.php and image every time they hit my server.
I suggested to read Apache access-log but client want me to note request time for every request made and store some where and calculate average between each request on end of the day.
My idea is to read Apache-log and is there a way we can write a php script which will work for every incoming request and check it's timing, probably a custom access-log file.
Any Help!!
You should use a handler to be able to hook into each and every request.
This can be done quite easily, but you should then also serve the files from your php.
Update
The following code snippets assume the following:
you have a typical "LAMP stack" installed on the hosting server
your Apache config "allows directory overrides" (with .htaccess files)
your Apache setup has mod_rewrite installed and enabled
Apache+PHP has read permission of all the files created in your docroot
your PHP version is at least 5.5 (or better)
In your server's docroot folder, create these files:
.htaccess
handler.php
.htaccess
Open the .htaccess file in your favorite text editor, type in the following, and save:
RewriteEngine On
RewriteCond %{REQUEST_URI} !handler.php$
RewriteRule (.*) handler.php
handler.php
Type the following in your .handler.php file, and save:
<?
$over = $_SERVER['SERVER_PROTOCOL'];
$path = explode('?',$_SERVER['REQUEST_URI']);
$path = (($path == '/') ? '/home.html');
$extn = array_shift((explode('.',$path)));
$list = # array
[
'html' => 'text/html',
'css' => 'text/css',
'png' => 'image/png',
'js' => 'application/javascript',
];
$type = (isset($mime[$extn]) ? $mime[$extn] : 'text/plain');
if (file_exists(".${path}"))
{
if (is_dir(".${path}") || ($path == '/.htaccess'))
{
header("${over} 403 Forbidden");
echo "path: `$path` is forbidden";
exit;
}
header("${over} 200 OK");
header("Content-Type: ${type}");
header("Content-Length: ".filesize($path));
readfile($path);
exit;
}
header("${over} 404 Not Found");
echo "path: `$path` is undefined";
exit;
?>
With each request, get the current microtime.
then, serve the file, (get the mime type of the file, write appropriate header: "Content-type", and simply: readfile('path/to/file.mp3');
*where 'path/to/file.mp3' would obviously be:
$_SERVER['REQUEST_URI'] or some re-routing - however you want it.
then, get the microtime again, now, subtract the former microtime from the latter, and you have the time it took to serve the file.
So, with that done, now you can log each request in the database, or where-ever, per request, specifying the field names accordingly.
I'm not sure how how much detail is required, please comment accordingly.
2:From your question, I gather that the requirement is to actually get the time that the image finally "shows up" on the visitor's browser?
If that is the case, then, well this may get technical pretty quick, depending how accurate the info should be, and also if you don't mind getting the said images via XMLHTTpRequest - via JavaScript. If that is the case, you can do the whole server-side thing as a pre-requisite, aditionally, set a custom response header, i.e:
header('x-imageID', $mysql_insert_id());
Then readfile('blah.jpg');
on the client-side, with the XMLHTTpRequest, set in the onload event another request to the server, with the image-id with e.g xhr.getResponseHeader('x-imageID') and the current time in milliseconds: new Date().getTime().
obviously you will need to set some info to distinguish the requests so that the server will only log for image requests, and update the records with the "update time" requests.
Hope it helps ;)

Reserve file for ajax request only

I have a massive of scripts that my core application
include('JS/gramp.php');
include('JS/est.php');
include('JS/curest.php');
include('JS/memomarker.php');
include('JS/local----------.php');
include('JS/poirel.php');
include('JS/maplayers.php');
include('JS/trafficinc.php');
include('JS/plannedtraffic.php');
include('JS/transportissues.php');
include('JS/cams_traff.php');
include('JS/places2.php');
Now these are all being moved to a on the fly loading, to reduce the size of the application on load
if(button_case_curtime==true){
$(".jsstm").load("<?php echo $core_dir; ?>JS/curresttime.php?la=<?php echo $caseset['iplat']; ?>&lo=<?php echo $caseset['iplong']; ?>&h=<?php echo $days_h; ?>");
rendermap = true;
}
issue! the application requires these files to be secure, the data involved requires that no one can access.
The ONLY file that will ever request these files will be index.php
Any input or idears would be fantastic!
There is no way to provide a file to the browser without also providing it to a user.
You could configure your server to only supply the files given an extra HTTP header (which you could add with JS), but nothing would stop people from sending that header manually or just digging the source out of the browser's debugging tools.
Any user you give the files to will have access to the files. If you want to limit which users have access to them, then you have to use auth/authz (which you'll want to apply to the index.php file as well so that unauthorised users don't just get JS errors or silent failure states).
No. What you are trying to do is not possible. Ajax requests are not special. They are just HTTP requests. End points created for Ajax should be secured with authentication/authorization just like any other HTTP request end point.
This is a trivial solution that will solve your problem half-way. Request them via a POST request, like so:
$.post('JS/maplayers.php', {'ajax':true}, function(){});
Notice the POST variable 'ajax'. In the file maplayers.php, add to the beginning the following code:
if((!isset($_POST['ajax']))) {
die('Invalid request, only ajax requests are permitted');
}

Cancel an HTTP POST request server side

I am trying to write a script for uploading large files (>500MB). I would like to do some authentication before the upload is processed, eg:
$id = $_GET['key'];
$size = $_GET['size'];
$time = $_GET['time'];
$signature = $_GET['signature'];
$secret = 'asdfgh123456';
if(sha1($id.$size.$time.$secret) != $signature){
echo 'invalid signature';
exit;
}
process upload...
unfortunately php only runs this code after the file has been uploaded to a temp directory, taking up valuable server resources. Is there a way to do this before the upload happens? I have tried similar things with perl/cgi but the same thing happens.
Wow, already 5 answers telling how it can't be done. mod_perl to the rescue, here you can reject a request before the whole request body is uploaded.
Apache is taking care of the upload before the PHP script is even invoked so you won't be able to get at it.
You can either split up the process into two pages (authentication, file upload page) or, if you need to do it all in one page, use an AJAX-esque solution to upload the file after authentication parameters are checked.
As far as I know, you cannot do that in PHP. PHP script is launched in response to a request, but a request is not "sent" until the file is uploaded, since the file being uploaded is a part of the request.
This is definitely not possible inside the PHP script you're uploading to.
The most simple possibility is indeed to provide authentication one step before the upload takes place.
If that is not an option, one slightly outlandish possibility comes to mind - using a RewriteMap and mapping it to an external program (it should be possible to make that program a PHP script).
Using RewriteMap it is possible to rewrite an URL based on the output of a command line program. If you use this directive to call a (separate) PHP script - you won't be able to use the user's session, though! - you would have access to the GET parameters before the request is processed.
If the processing fails (= the credentials are invalid), you could redirect the request to a static resource which would at least prevent PHP from starting up. (I assume the uploaded will be hogging some resources anyway, but probably less than if it were redirected to PHP.)
No guarantees whether this'll work! I have no own experience with RewriteMap.
This is due to the fact that each HTTP request is a single contains all the of form/POST data, including the file upload data.
As such, I don't believe it's possible to handle a file upload request in this fashion irrespective of which scripting language you use.
I don't think you can do this. The best you can do is probably to run an AJAX function onSubmit to do your validation first, then if it returns valid then execute the POST to upload the file. You could set a $_SESSION in your AJAX script if the authentication is valid, then check for that session var in the upload script to allow the upload.

Categories