Налаштування захисту зображень від копіювання 1С-Бітрікс
Конкуренти копіюють фотографії товарів із водяним знаком компанії — це відбувається постійно. Повністю заблокувати завантаження зображень неможливо, але можна підняти вартість копіювання до рівня, коли простіше зробити власні фотографії.
Що реально працює, а що ні
Не працює:
- Заборона правої кнопки миші через JavaScript (
oncontextmenu="return false") — вимикається в браузері за 3 секунди - CSS
pointer-events: none— зображення все одно доступне через DevTools - Заборона збереження через JS-обробник
dragstart— копіюється через скриншот або мережеву вкладку
Працює:
- Водяний знак, нанесений на зображення на сервері — складно прибрати, якщо знак напівпрозорий і розташований у центрі
- Віддача зображень через PHP із перевіркою реферера — уповільнює хотлінкінг
- Заміна URL зображень на ресурс з авторизацією
Водяний знак через GD
Найбільш практичний підхід — нанесення водяного знаку під час завантаження зображення у 1С-Бітрікс. Обробник події OnAfterFileSave:
// /local/php_interface/init.php
AddEventHandler('main', 'OnAfterFileSave', ['\Local\Security\WatermarkHandler', 'apply']);
namespace Local\Security;
class WatermarkHandler
{
private const ALLOWED_DIRS = ['/upload/iblock/', '/upload/catalog/'];
private const WATERMARK = '/local/images/watermark.png';
public static function apply(array $file): void
{
$path = $file['PATH'] ?? '';
// Тільки зображення каталогу
$inAllowed = false;
foreach (self::ALLOWED_DIRS as $dir) {
if (str_starts_with($path, $_SERVER['DOCUMENT_ROOT'] . $dir)) {
$inAllowed = true;
break;
}
}
if (!$inAllowed) return;
if (!in_array(strtolower($file['CONTENT_TYPE'] ?? ''), ['image/jpeg', 'image/png', 'image/webp'])) return;
if (!file_exists($path) || !file_exists($_SERVER['DOCUMENT_ROOT'] . self::WATERMARK)) return;
self::applyWatermark($path, $_SERVER['DOCUMENT_ROOT'] . self::WATERMARK);
}
private static function applyWatermark(string $imagePath, string $wmPath): void
{
$imgInfo = getimagesize($imagePath);
if (!$imgInfo) return;
// Завантажуємо вихідне зображення
$image = match ($imgInfo[2]) {
IMAGETYPE_JPEG => imagecreatefromjpeg($imagePath),
IMAGETYPE_PNG => imagecreatefrompng($imagePath),
default => null,
};
if (!$image) return;
$wm = imagecreatefrompng($wmPath);
$imgW = imagesx($image);
$imgH = imagesy($image);
$wmW = imagesx($wm);
$wmH = imagesy($wm);
// Масштабуємо знак до 30% ширини зображення, якщо він більший
if ($wmW > $imgW * 0.3) {
$ratio = ($imgW * 0.3) / $wmW;
$newWmW = (int)($wmW * $ratio);
$newWmH = (int)($wmH * $ratio);
$resizedWm = imagecreatetruecolor($newWmW, $newWmH);
imagealphablending($resizedWm, false);
imagesavealpha($resizedWm, true);
imagecopyresampled($resizedWm, $wm, 0, 0, 0, 0, $newWmW, $newWmH, $wmW, $wmH);
imagedestroy($wm);
$wm = $resizedWm;
$wmW = $newWmW;
$wmH = $newWmH;
}
// Позиція: центр зображення
$dstX = (int)(($imgW - $wmW) / 2);
$dstY = (int)(($imgH - $wmH) / 2);
imagecopy($image, $wm, $dstX, $dstY, 0, 0, $wmW, $wmH);
// Зберігаємо назад
match ($imgInfo[2]) {
IMAGETYPE_JPEG => imagejpeg($image, $imagePath, 90),
IMAGETYPE_PNG => imagepng($image, $imagePath, 7),
};
imagedestroy($image);
imagedestroy($wm);
}
}
Захист від хотлінкінгу через nginx
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked ~\.yourdomain\.com;
if ($invalid_referer) {
return 403;
}
}
Або замінюємо зображення при хотлінкінгу:
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked ~\.yourdomain\.com;
if ($invalid_referer) {
rewrite ^ /local/images/hotlink-protected.jpg last;
}
}
Віддача зображень через PHP (для приватних каталогів)
Якщо зображення мають бути доступні лише авторизованим користувачам:
// /local/ajax/secure-image.php
$fileId = (int)($_GET['id'] ?? 0);
$token = $_GET['token'] ?? '';
if (!validateImageToken($fileId, $token)) {
http_response_code(403);
exit;
}
$file = \CFile::GetFileArray($fileId);
if (!$file) { http_response_code(404); exit; }
$path = $_SERVER['DOCUMENT_ROOT'] . $file['SRC'];
if (!file_exists($path)) { http_response_code(404); exit; }
header('Content-Type: ' . $file['CONTENT_TYPE']);
header('Cache-Control: private, max-age=3600');
readfile($path);
Токен — HMAC від ID файлу та солі: hash_hmac('sha256', $fileId, SECRET_KEY).
Практичні рекомендації
Водяний знак працює в поєднанні з грамотним дизайном: напівпрозорий логотип у центрі складніше прибрати, ніж кутовий. Для цінних товарів (ювелірні вироби, дизайнерські меблі) — наносити на 100% зображень. Для масового каталогу з тисячами SKU — тільки на головне зображення товару. Зображення брендів від постачальників водяним знаком не чіпаємо — контракти забороняють.







