Здравствуйте, уважаемые харбрачитатели.
Некоторое время назад я столкнулся с очень, на мой взгляд, интересной задачей. Суть задачи заключалась в том, что необходимо максимально усложнить процесс сохранения картинок со страниц сайта. При этом картинки могут быть разных размеров.
Задача ясна, приступим к её решению. В общем случае процесс сохранения картинки достаточно прост: кликаем по ней правой кнопкой мышки и в открывшемся меню выбираем «Сохранить изображение…» (в разных браузерах надпись может отличаться). Перехват нажатия правой кнопки мышки частично решит эту задачу, но обход данного ограничения до безобразия прост – отключаем JavaScript и спокойно вызываем контекстное меню. В этом случае достаточно картинку сделать фоновым изображением:
<div id="img" style="width: 220px; height: 130px; background-image: url(http://example.ru/img/Habrahabr_logo.png);"></div>
Но в данном примере фиксированный размер блока, а по условиям задачи размер может быть любой. Здесь на помощь приходит PHP и функция getimagesize().
$size = getimagesize(ROOT."/img/Habrahabr_logo.png ");
$html = "<div id=\"img\" style=\"width: {$size[0]}px; height: {$size[1]}px; background-image: url(http://example.ru/img/Habrahabr_logo.png);\"></div>";
echo $html;
Задача практически решена, но в коде страницы есть прямая ссылка на это изображение, по которой его можно спокойно скачать: можно просто скопировать адрес изображения и вставить его в адресную строку браузера и сохранить. Но не всё так плохо, как кажется на первый взгляд. Достаточно написать простенький скрипт и через него отдавать изображение браузеру, при этом проверяя заголовок referrer, и если он отсутствует или отличен от example.ru, то выдать ошибку. Здесь стоит отметить, что для повышения производительности и безопасности при решении данной задачи информация об изображения хранилась в базе данных. У каждого изображения был уникальный идентификатор, который можно было передать скрипту (image.php):
<?php
define("ROOT", dirname(__FILE__));
include_once ROOT."/images_db.php";
if (isset($_SERVER['HTTP_REFERER']) && eregi("example.ru", $_SERVER['HTTP_REFERER']))
{
$img_path = ROOT."/img/".$images[intval($_GET['id'])]['file_name'];
switch ($images[intval($_GET['id'])]['file_ext'])
{
case "png":
$image = imagecreatefrompng($img_path);
header("Content-Type: image/png");
imagepng($image);
break;
case "jpg":
$image = imagecreatefromjpeg($img_path);
header("Content-Type: image/jpeg");
imagejpeg($image);
break;
case "gif":
$image = imagecreatefromgif($img_path);
header("Content-Type: image/gif");
imagegif($image);
break;
}
}
else
{
$img_path = ROOT."/img/no_image.jpg";
$image = imagecreatefromjpeg($img_path);
header("Content-Type: image/jpeg");
imagejpeg($image);
break;
}
@imagedestroy($image);
?>
В файле images_db.php хранится массив с описанием картинок, это сделано исключительно для примера:
<?php
$images = array("123" => array("file_name" => "Habrahabr_logo.png",
"file_ext" => "png",
"file_id" => "123"),
"124" => array("file_name" => "php-logo1.jpg",
"file_ext" => "jpg",
"file_id" => "124"),
"125" => array("file_name" => "google-logo.gif",
"file_ext" => "gif",
"file_id" => "125"));
?>
Заменим в коде прямую ссылку на изображение и файл index.php будет выглядеть так:
<?php
define("ROOT", dirname(__FILE__));
include_once ROOT."/images_db.php";
$html = "";
foreach($images as $im)
{
$size = getimagesize(ROOT."/img/".$im['file_name']);
$html .= "<div id=\"img{$im['file_id']}\" title=\"File name: {$im['file_name']}, File width: {$size[0]}px, File height: {$size[1]}px\" style=\"width: {$size[0]}px; height: {$size[1]}px; background-image: url(http://example.ru/image.php?id={$im['file_id']});\"></div>\r\n";
}
echo $html;
?>
Теперь при прямом обращении к адресу example.ru/image.php?id=123 мы получим картинку no_image.jpg, так как заголовок referrer не передаётся.
Осталось два способа вытащить картинку: подделать запрос или сделать скриншот. К сожалению, я так и не нашёл решения которое могло бы противостоять этим способам. Но от большинства неопытных пользователей защита вполне рабочая.
Надеюсь, данная информация будет хоть кому-нибудь полезна.
Спасибо за внимание.
Исходники примера