Pull to refresh

Защита от сохрания изображений

Reading time3 min
Views13K
image
Здравствуйте, уважаемые харбрачитатели.
Некоторое время назад я столкнулся с очень, на мой взгляд, интересной задачей. Суть задачи заключалась в том, что необходимо максимально усложнить процесс сохранения картинок со страниц сайта. При этом картинки могут быть разных размеров.

Задача ясна, приступим к её решению. В общем случае процесс сохранения картинки достаточно прост: кликаем по ней правой кнопкой мышки и в открывшемся меню выбираем «Сохранить изображение…» (в разных браузерах надпись может отличаться). Перехват нажатия правой кнопки мышки частично решит эту задачу, но обход данного ограничения до безобразия прост – отключаем 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 не передаётся.

Осталось два способа вытащить картинку: подделать запрос или сделать скриншот. К сожалению, я так и не нашёл решения которое могло бы противостоять этим способам. Но от большинства неопытных пользователей защита вполне рабочая.

Надеюсь, данная информация будет хоть кому-нибудь полезна.
Спасибо за внимание.

Исходники примера
Tags:
Hubs:
Total votes 61: ↑15 and ↓46-31
Comments46

Articles