PHP htaccess Watermark Hotlinked images

I have a PHP script which places a watermark on images on my site.  How could I make it so that it only watermarks my images if they're placed on another site?

Here's my .htaccess code:
RewriteEngine on
    RewriteRule ^(.*\.(jp?g))$ /videogames/watermark.php?image=$1&watermark=watermark.png [NC]

Open in new window


Thanks.
davideo7Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Ray PaseurCommented:
How could I make it so that it only watermarks my images if they're placed on another site?
How would you envision that your images would get placed on another site?

What I would suggest (without having thought this through in great detail or even tested the idea) would be to use PHP scripts to render your images.  The scripts could check the $_SERVER["HTTP_HOST"] and $_SERVER["HTTP_REFERER"].  It the values indicate your site, render the image unwatermarked.  Else watermark.

If you put the image files outside of your web root they will not be browsable (no URL) and that may help enhance security.
0
davideo7Author Commented:
Ray_Paseur: If they got hotlinked.  How would I go about doing this with htaccess?
0
Ray PaseurCommented:
Let me show you what I have been able to do with this question.  It does not involve .htaccess but uses a PHP script to render the images.

Here is what happens if you try to access the image directly.
http://www.laprbass.com/RAY_watermark_hotlinked_image.php?q=r2.jpg

Here is a page on an unauthorized server.
http://www.iconoun.com/RAY_watermark_test_hotlink.php

Here is a page on the authorized server.
http://www.laprbass.com/RAY_watermark_test_hotlink.php

Here is the test script from the "foreign" server.  Note that it does not know the secret handshake!
<?php // FOREIGN/RAY_watermark_test_hotlink.php
error_reporting(E_ALL);

session_start();

// FOREIGN SITES DO NOT KNOW TO DO THIS
// $_SESSION["image_link"] = "OK";

echo '<image src="http://www.laprbass.com/RAY_watermark_hotlinked_image.php?q=r2.jpg" />';

Open in new window

And here is the script on the authorized server, where the secret handshake ($_SESSION["image_link") is filled in correctly.
<?php // RAY_watermark_test_hotlink.php
error_reporting(E_ALL);

session_start();
$_SESSION["image_link"] = "OK";

echo '<image src="http://www.laprbass.com/RAY_watermark_hotlinked_image.php?q=r2.jpg" />';

Open in new window

Now this part is important:  YOU MUST CLOSE ALL INSTANCES OF THE BROWSER WINDOW AND REOPEN THE WINDOW for each separate test of this process.  In practice this will not be much of an issue because it is unlikely that a foreign site hotlinking to your images will have access to session data that loads the secret handshake.  But if you're testing these links on the same machine with the same browser you may find that there is session-cross-pollination (for want of a better term).  All instance of the browser (windows or tabs) use the same cookie jar.

Here is the script that renders the images.
<?php // RAY_watermark_hotlinked_image.php

// WRITES A WATERMARK OVER THE IMAGE IF
// THE FILE IS HOTLINKED BY A FOREIGN SITE

// THERE WILL BE A SIGNAL IN THE SESSION IF IT OK TO PRODUCE UN-WATERMARKED IMAGES
session_start();

// HARDWIRED: THE DOMAIN AND IMAGE DIRECTORY (OUTSIDE OF THE WEB ROOT)
$my_domain = 'laprbass.com';
$image_dir = '../root_images' . DIRECTORY_SEPARATOR;

// GET THE IMAGE NAME
$image_url = (isset($_GET['q'])) ? $_GET['q'] : NULL;
if (!$image_url) die();

// GET THE IMAGE EXTENSION AND HEADER TYPE FOR JPEG OR PNG
$image_ext = end(explode('.', $image_url));
$image_ext = strtolower($image_ext);
$image_hed = 'jpeg';
if ($image_ext == 'png') $image_hed = 'png';

// TEST IF IT IS OK TO PRODUCE UN-WATERMARKED IMAGES
if ($_SESSION["image_link"] == 'OK')
{
    header("Content-Type: image/$image_hed");
    echo file_get_contents($image_dir . $image_url);
    exit;
}

// GET THE IMAGE RESOURCE
$image_res = FALSE;
if ($image_hed == 'png')  $image_res = ImageCreateFromPNG($image_dir . $image_url);
if ($image_hed == 'jpeg') $image_res = ImageCreateFromJPEG($image_dir . $image_url);
if (!$image_res) die();

// COMPUTE THE IMAGE TEXT SIZE IN POINTS
$image_w   = ImagesX($image_res);
$image_h   = ImagesY($image_res);
$image_pct = $image_w / 100.0;

$image_size = round($image_pct * 2);

// SET THE WATERMARK TEXT
date_default_timezone_set('America/Chicago');
$image_wmk = '© ' . date('Y') . ' Ray Paseur';

// LOCATE THE FONT (VERDANA Z = BOLD ITALIC)
$font = 'fonts/verdanaz.ttf';

// PREPARE HORIZONTAL TEXT WITH ZERO ANGLE
$angle = 0;

// GET THE BOUNDING BOX SIZE FOR THE TEXT (DOES NOT INCLUDE DESCENDERS)
$poz = imageTTFBBox
( $image_size            // SIZE IN POINTS
, $angle                 // ONLY WORKS RIGHT IF ANGLE = 0
, $font                  // PATH TO TTF FILE
, $image_wmk             // TEXT
)
;

// GET THE WIDTH AND HEIGHT OF THE WATERMARK IMAGE WITH ROOM FOR DESCENDERS
$wmi_w = $poz[2] - $poz[0] + $image_size;
$wmi_h = $poz[1] - $poz[7] + $image_size;

// CREATE A TRUE COLOR WATERMARK IMAGE
$wmi = imageCreateTrueColor($wmi_w, $wmi_h);

// MAKE THE BACKGROUND, TEXT AND SHADOW COLORS
$wmi_bgc = imageColorAllocateAlpha($wmi,   0,    0,   0,  64);
$wmi_txt = imageColorAllocateAlpha($wmi, 224,  224, 224,  64);
$wmi_sh1 = imageColorAllocateAlpha($wmi, 128,  128, 128,  80);
$wmi_sh2 = imageColorAllocateAlpha($wmi,  96,   96,  96,  96);
$wmi_sh3 = imageColorAllocateAlpha($wmi,  80,   80,  80, 112);

imageFill($wmi, 0, 0, $wmi_bgc);

// SET SOME PADDING VALUES
$off_x = 6;
$off_y = (int)( $image_size / 2 ) + ( $off_x / 2);

// SHADOW THE NAME ON THE WATERMARK IMAGE
imageTTFText
( $wmi                   // IMAGE RESOURCE
, $image_size            // SIZE
, $angle                 // ANGLE
, $off_x + 3             // HORIZONTAL LEFTMOST BASEPOINT
, $wmi_h - $off_y + 3    // VERTICAL BASEPOINT
, $wmi_sh3               // COLOR INDEX
, $font                  // PATH TO TTF FILE
, $image_wmk             // TEXT
)
;

// SHADOW THE NAME ON THE WATERMARK IMAGE
imageTTFText
( $wmi                   // IMAGE RESOURCE
, $image_size            // SIZE
, $angle                 // ANGLE
, $off_x + 2             // HORIZONTAL LEFTMOST BASEPOINT
, $wmi_h - $off_y + 2    // VERTICAL BASEPOINT
, $wmi_sh2               // COLOR INDEX
, $font                  // PATH TO TTF FILE
, $image_wmk             // TEXT
)
;

// SHADOW THE NAME ON THE WATERMARK IMAGE
imageTTFText
( $wmi                   // IMAGE RESOURCE
, $image_size            // SIZE
, $angle                 // ANGLE
, $off_x + 1             // HORIZONTAL LEFTMOST BASEPOINT
, $wmi_h - $off_y + 1    // VERTICAL BASEPOINT
, $wmi_sh1               // COLOR INDEX
, $font                  // PATH TO TTF FILE
, $image_wmk             // TEXT
)
;

// WRITE THE NAME ON THE WATERMARK IMAGE
imageTTFText
( $wmi                   // IMAGE RESOURCE
, $image_size            // SIZE
, $angle                 // ANGLE
, $off_x + 0             // HORIZONTAL LEFTMOST BASEPOINT
, $wmi_h - $off_y + 0    // VERTICAL BASEPOINT
, $wmi_txt               // COLOR INDEX
, $font                  // PATH TO TTF FILE
, $image_wmk             // TEXT
)
;

// PUT THE WATERMARK IN THE MIDDLE OF THE IMAGE
imageCopy
( $image_res                     // DESTINATION
, $wmi                           // SOURCE (WATERMARK)
, ($image_w / 2) - ($wmi_w / 2)  // DESTINATION X-AXIS IN PIXELS
, ($image_h / 2) - ($wmi_h / 2)  // DESTINATION Y-AXIS IN PIXELS
, 0                              // SOURCE X-AXIS IN PIXELS
, 0                              // SOURCE Y-AXIS IN PIXELS
, $wmi_w                         // SOURCE WIDTH
, $wmi_h                         // SOURCE HEIGHT
)
;

// RENDER THE IMAGES AND RELEASE THE MEMORY
header('Content-Type: image/png');
imagePNG($image_res);
imageDestroy($wmi);
imageDestroy($image_res);

Open in new window

So give it a try and see if it makes sense to you.  Remember to close the browser after every test of the authorized pages.

Now here is the bad news.  Let's say that you put this in place, and a bad actor visits your web site, scrapes the HTML and tries to use your image.  Zap - you've put a watermark into his site.  He does not have a clue about what the secret might be that tells the rendering script to omit the watermark.  But he knows he cannot hotlink.  So he goes back to your site, finds the image he want to steal, right-clicks and uses "save as" to make his own copy.  I am sure you have found JavaScript that will disable right-clicks.  No problem - he can just bypass the JavaScript.  Or he can use a screen capture and get the image that way.  Executive summary: If you publish it on the WWW you have released it into the wild.  So mark your EXIF data with your copyright, register your copyrights with the government, have a good lawyer on retainer, use Tineye and Google Image Seach frequently.
http://chrismartino.com/blog/2012/01/how-to-find-stolen-photos/

HTH, ~Ray
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Apache Web Server

From novice to tech pro — start learning today.