Вопрос: Возможно ли защитить сайт от скачивания файлов с других ресурсов?

Лучший ответ: Если мне не изменяет память, то это делается через .htaccess (в данном случае, пример с картинками):Под капотOptions +FollowSymlinks
#Запрещаем загрузку файлов с внешних сайтов
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www.)?site.ru/ [NC]
RewriteRule .*.(gif|jpg|png)$ site.ru/halyavi_net.png [NC]
Здесь site.ru - Ваш сайт.
Можно ещё указать дружественные сайты, добавив записи:RewriteCond %{HTTP_REFERER} !^http(s)?://(www.)?yandex.ru [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www.)?google. [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://(www.)?друг.ru [NC]
Прошу заметить, что можно указывать файлы любого формата:RewriteRule .*\.(jpe?g|bmp|gif|png|css|mov|swf|dcr|exe|rar|avi|vob|zip|pdf|txt|doc|flv|mp3|mp4)$ files/images/nohotlink.jpg [NC,L]

Худший ответ: Так как "парсеры" других ресурсов читают html, можно сделать добавление ссылки через javascript ( имею в веду на страницу ссылка будет вставляться в определенный элемент).
Так же создать простую систему токенов.
Например у вас есть папка где лежат Ваши файлы "site.com/myfile/", все запросы к данной папки будут перенаправляться на файл обработки, где и будет проверяться "токен"..
Ссылка будет выглядеть так:
site.com/myfile/prog.exe?token=1234567890
GET параметр "token" сравниваем с тем что будет в сессии пользователя, если все хорошо от открываем файл, если нету то и скачать нельзя...
Как правило парсеры не хранят сессии и не смогут скачать файл...
-------- Решение:
Давайте с начало определимся с видом ссылки на файл.
Можно сделать так:
/myfile/img.jpg?b31598b111cf1c175e956830b764e3f1e6d0a364
или вот так:
/myfile/img.jpg?token=b31598b111cf1c175e956830b764e3f1e6d0a364
Мне более симпатичен первый вариант.
1. Необходимо в конфигурационном файле сервера сделать что бы все запросы по определенной папки открывали файл проверки.
Например download.php
По настройки для вашего веб сервера можно найти примеры водяного знака для изображений.
Принцип точно такой же.
Настройка .htaccess<FilesMatch "\.(gif|jpg|jpeg|png)$">
   RewriteEngine On
   RewriteCond %{REQUEST_FILENAME} -f
   RewriteRule ^(.*)$ /download.php?file=$1 [T=application/x-httpd-php,L,QSA]
</FilesMatch>
2. Необходимо создать сессию и ключ.session_name('session');
session_start();

define('token', "?" . hash('sha1', session_id() , false));
# define('token', "?token=" . hash('sha1', session_id() , false));
Придется добавить в ваши шаблоны константу "token" в конец ссылки.
3. Проверка ключа download.php<?php

session_name('session');
session_start();

# if( !empty($_GET['token']) && $_GET['token'] == hash('sha1', session_id() , false) )

if (isset($_GET[hash('sha1', session_id(), false)])) {
    if (ob_get_level()) {
        ob_end_clean();
    }

    # $file = $_SERVER['DOCUMENT_ROOT'] . '/' . preg_replace('/%2F/', '/', urlencode(trim(preg_replace('%\?.*%i', null, $_SERVER['REQUEST_URI']), '/')));
    $file = $_GET['file'];

    if (!is_file($file)) exit("Файл не найден!");

    // заставляем браузер показать окно сохранения файла
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=' . basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));

    // читаем файл и отправляем его пользователю
    readfile($file);
    exit;

} else exit("Неверный ключ авторизации");
Более короткий вариант:<?php

session_name('session');
session_start();

if (!isset($_GET[hash('sha1', session_id(), false)])) exit("Неверный ключ авторизации");

if (ob_get_level()) ob_end_clean();

if (!(!empty($file = $_GET['file']) && is_file($file))) exit("Файл не найден!");

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));

exit (readfile($file));