Solved

PHP htaccess Watermark Hotlinked images

Posted on 2012-03-28
3
1,034 Views
Last Modified: 2012-03-29
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.
0
Comment
Question by:davideo7
  • 2
3 Comments
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
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
 

Author Comment

by:davideo7
Comment Utility
Ray_Paseur: If they got hotlinked.  How would I go about doing this with htaccess?
0
 
LVL 108

Accepted Solution

by:
Ray Paseur earned 500 total points
Comment Utility
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

Featured Post

Comprehensive Backup Solutions for Microsoft

Acronis protects the complete Microsoft technology stack: Windows Server, Windows PC, laptop and Surface data; Microsoft business applications; Microsoft Hyper-V; Azure VMs; Microsoft Windows Server 2016; Microsoft Exchange 2016 and SQL Server 2016.

Join & Write a Comment

Suggested Solutions

Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
This article discusses how to create an extensible mechanism for linked drop downs.
Learn how to get help with Linux/Unix bash shell commands. Use help to read help documents for built in bash shell commands.: Use man to interface with the online reference manuals for shell commands.: Use man to search man pages for unknown command…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now