Подтвердить что ты не робот

Доступ к журналу в PHP

Я хочу регистрировать доступ к любым файлам в папке /files, поэтому я могу обработать его с помощью PHP для генерации некоторых статистических данных.

Я не хочу писать собственный обработчик PHP, вызванный через RewriteRule, потому что я не хочу иметь дело с кодами состояния, типами MIME и заголовками кеширования, а также проблемами с блокировкой файлов.

У меня нет доступа к конфигурации сервера, поэтому я не могу использовать CustomLog (у меня есть доступ к .htacess).

Я не могу использовать X-Sendfile, потому что он не включен.

У меня нет доступа к access.log.


Ищите официальный ответ.

4b9b3361

Ответ 1

Я пробовал много вещей, и, похоже, нет простого решения.

В моем решении используется трюк заголовка Location, предложенный @yes123, но я изменил его в соответствии с моими предпочтениями.

Связи с файлами сохраняются неповрежденными, поэтому они все еще: /files/path/to/my/file.abc У меня есть RewriteRule:

RewriteRule ^files/(.*) path/to/tracker.php?path=/$1

Затем в файле я выпускаю заголовок Location, добавляя ?track=no к URL-адресу и исключение из предыдущего RewriteRule:

RewriteCond %{QUERY_STRING} !(&|^)track=no(&|$)

Я добавил еще одну оптимизацию. Я включил E-теги, поэтому, если клиент отправляет заголовок E-Tag, посмотрите, соответствует ли он файлу и возвращает 304 Not Modified вместо Location.

$fs = stat($document_root . $path);
$apache_etag = calculate_apache_etag($fs);
if ((isset($_SERVER["HTTP_IF_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_MATCH"], $apache_etag))
    || (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_NONE_MATCH"], $apache_etag))
) {
    header("ETag: " . $apache_etag, true, 304);
    exit;
}

function etag_within_range($etag1, $etag2) {
    list($size1, $mtime1) = explode("-", $etag1);
    list($size2, $mtime2) = explode("-", $etag2);
    $mtime1 = floor(hexdec($mtime1) / 1000000);
    $mtime2 = floor(hexdec($mtime2) / 1000000);
    return $mtime1 === $mtime2 && $size1 === $size2;
}

И реализация для calculate_apache_etag можно найти здесь: Как вы создаете etag, который соответствует Apache?

etag_withing_range решает проблему сравнения с более высокой точностью mtime в Apache.


Заметки о решениях, которые не работают

virtual

Тест script:

var_dump(apache_response_headers());
virtual("/path/to/image.jpg");
var_dump(apache_response_headers());

Выходы:

array(1) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" }
[[binary junk]]
array(5) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" ["Keep-Alive"]=> string(18) "timeout=5, max=100" ["Connection"]=> string(10) "Keep-Alive" ["Transfer-Encoding"]=> string(7) "chunked" ["Content-Type"]=> string(9) "text/html" }

Content-Type: text/html reaaaaalllyly?: (

Возможно, функция PHP5.3 header_remove может решить эту проблему? Я не пробовал.

Ответ 2

Это довольно несколько ограничений, которые вы разместили там.

Вы можете сделать это с помощью специального обработчика, установленного через PHP include в верхней части каждого применимого (или с __FILE__ синтаксического анализа, не применимого) script. У вас должен быть script, который запускается, когда каждый файл попадает, и вы исключили изменения в конфигурацию сервера (включая, я полагаю, .htaccess, когда вы сказали RewriteRule не было достаточно хорошо), так что это означает, что будет делать это с помощью гейткипера на основе script. Вы не можете иметь решение, которое соответствует вашим ограничениям, и пользователи могут перейти к файлам, не удаляя сначала PHP (или другой серверный динамический язык). Кэширование может быть сохранено путем перенаправления пользователя на фактические файлы вместо запуска статического содержимого через PHP.

Вы можете хранить информацию журнала в базе данных или файл в месте, доступном для записи сервером (обратите внимание, что если вы используете файлы - режим добавления сложный).

EDIT: quickshiftin указывает два способа, с помощью которых вы можете вызвать PHP, не добавляя include вызовы вручную.

Ответ 3

Создайте auto_prepend_file и определите функцию для записи w/e, которую вы хотите. Вам понадобится доступ к .htaccess, чтобы установить их (и для веб-хоста потребуется что-то вроде AllowOverride all в vhost) или с PHP 5.3 вы можете использовать для функции INI для каждого каталога.

.htaccess

php_value auto_prepend_file/path/to/file.php

per-directory php.ini(PHP 5.3 CGI/Fast CGI SAPI)

user_ini.auto_prepend_file =/path/to/file.php

Тогда для вашего файла /path/to/file.php(что-то более элегантное, я уверен;))

  

file_put_contents(
    LOG_FILE,
    implode(PHP_EOL . PHP_EOL, array(
                'SERVER: ' . PHP_EOL . print_r($_SERVER, true),
                'REQUEST: ' . PHP_EOL . print_r($_REQUEST, true)
            )),
    FILE_APPEND
);

Красота такого подхода заключается в том, что вы, скорее всего, сможете избежать этого, и вам нужно будет только указать/включить код ведения журнала в одном месте.

EDIT:

После ретроспекции я вижу, что вы хотите, чтобы это работало для произвольных типов файлов... Да, это было бы довольно грубо. Лучшее, что я могу придумать, - это обозначить эти файлы как .php или определить пользовательские типы mime в .htaccess. Идея заключалась в том, чтобы запускать файлы через интерпретатор PHP, тем самым выполняя auto_prepend_file, и поскольку в файле нет тегов PHP, содержимое отправляется непосредственно клиенту. Возможно, даже крошечный фрагмент PHP поверх каждого файла содержимого, задающего заголовок ContentType. Я даже не уверен, что это сработает, но это возможно.

Ответ 4

Это довольно просто сделать, учитывая, что вам не нужно ограничивать доступ.

постройте страницу logger.php, которая принимает входной файл, запрошенный как:

logger.php?file=abc.exe

В logger.php вам просто нужно зарегистрировать этот доступ, а затем rediret в файл:

file_put_contents('log', $_GET['file'] . ' requested',FILE_APPEND);
header('Location: files/'.$_GET['file']);

Просто проверьте $_GET['file'] на наличие вредоносных файлов

Конечно, вы должны заменить ссылки на своем сайте, от:

<a href="files/abc.exe">

к

<a href="logger.php?file=abc.exe">

Ответ 5

Похоже, что целью здесь является обход всех систем, которые по сути существуют в Apache и PHP. Если эти ограничения действительно присутствуют на вашем экземпляре сервера, вам гораздо лучше попросить изменить ваши привилегии, чем разработать обходное решение, которое администратор системы может или не может быть вам доволен внедрением.

Ответ 6

Не может быть именно то, что вы хотите, но почему бы вам совсем не использовать другое решение?

Вы можете использовать Google Analytics VirtualPageviews для отслеживания загрузки файлов через Javascript.

См. здесь для получения дополнительной информации: http://support.google.com/googleanalytics/bin/answer.py?hl=en&answer=55529

Вы даже можете создать свой собственный JS для отслеживания загрузки файлов через браузер, не беспокоясь о GA.

Обновление

Как я уже сказал, вы можете легко создать свой собственный JS, чтобы отслеживать их, не беспокоясь о GA. Вот глупый пример в jQuery, который бы работал (не тестировал его - просто написал его в верхней части моей головы):

Пример кода:

JS Сторона:

$(document).ready(function() {
  $("a").click(function() {
    if( $(this).attr('href').match(/\/files\/(.*)/) ) {
      $.ajax({
        url: '/tracking/the/file/downloads.php'
        data: {
          'ok': 'let\'s',
          'add': 'some information',
          'about': 'the user that initiated',
          'the': 'request',
          'file': $(this).attr('href')
        }
      });
    }

    return true;
  });
});

Ответ 7

Работает только в случае mod_php. Существует некоторая производительность - apache_lookup_uri() выполняет дополнительный внутренний запрос apache.

Как указывалось другим вам нужно .htaccess вроде

RewriteEngine On
RewriteRule ^/handler.php$ - [L]
RewriteRule ^/([a-zA-Z0-9\.]+)$ /handler.php?filename=$1 [L]

В файле handler.php используйте функцию virtual() для выполнения подзапроса apache. Пример здесь: http://www.php.net/manual/en/function.virtual.php#88722

Обновлено и проверено (но довольно минимальное) решение:

<?php
//add some request logging here
$file = $_GET["filename"];

$file_info = apache_lookup_uri($file);
header('content-type: ' . $file_info -> content_type);
// add other headers?
virtual($file);
exit(0);
?>

Ответ 8

Хорошо, вот идея. Потерпите меня на этом, это может показаться на первый взгляд неподходящим, но прочитайте бит в конце. Надеюсь, он работает с тем, что у вас есть. В папке, содержащей ваши файлы, вы помещаете .htaccess, который переписывает все запросы обработчику PHP script в том же каталоге, что-то вроде этого (untested):

RewriteEngine On
RewriteRule ^/handler.php$ - [L]
RewriteRule ^/([a-zA-Z0-9\.]+)$ /handler.php?filename=$1 [L]

В PHP script вы выполняете любые записи, используя file_put_contents(). Затем вы создаете handler.php с помощью этого кода:

<?php
if (!file_exists) {
    header("Status: 404 Not Found");
    //if you have a 404 error page, you can use an include here to show it
    exit(0);
}

header("Content-disposition: attachment; filename={$_GET["filename"]}");
header("Content-type: ".get_mime_type($_GET["filename"]));
readfile($filename);

function get_mime_type($filename, $mimePath = '/etc') {
    $fileext = substr(strrchr($filename, '.'), 1);
    if (empty($fileext)) return (false);
    $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileext\s)/i";
    $lines = file("$mimePath/mime.types");
    foreach($lines as $line) {
        if (substr($line, 0, 1) == '#') continue; // skip comments
        $line = rtrim($line) . " ";
        if (!preg_match($regex, $line, $matches)) continue; // no match to the extension
        return ($matches[1]);
    }
    return (false); // no match at all
}
?>

В принципе, вы создаете слой между запросом файла и фактической службой файла. Этот PHP-уровень регистрирует доступ к файлу, а затем обслуживает файл. Вы сказали, что не хотите возиться со статусом и типами MIME, но красота в том, что все, о чем позаботились. Если файл не существует, он просто генерирует стандартный 404, и вы можете включить пользовательскую страницу ошибок 404. Да, заголовок состояния здесь изменяется, но ничего сложного. Что касается типов MIME, они обнаруживаются для вас в соответствии с теми же правилами типа MIME, которые использует Apache. Направьте функцию get_mime_type в файл mime.types на вашем сервере. Если вы не знаете, где это, просто скачайте копию из здесь. Я признаю, что это решение, вероятно, более техническое, чем вы искали, но с ограничениями у вас есть это хорошее решение. Лучшая часть - это полностью прозрачная для конечного пользователя, а также тех, кто загружает материал.

Ответ 9

Единственный ненавязчивый мониторинг, который вы могли бы сделать без фильтрации файлов через PHP, проверял бы все файлы и каждый раз отмечал бы время доступа к файлам каждый раз любой PHP файл запрашивается (вы просто добавляете функцию в свои php файлы или используете переписывание). Это принесет немного накладных расходов, но это единственная ненавязчивая статистика, которую вы можете получить.

Очевидно, таким образом вы не можете получить точное количество обращений, но больше, как частоты, поэтому это своего рода (жизнеспособная) статистика. Чтобы получить что-то вроде номеров хитов (это было открыто 1000 тыс. Раз 25 марта в 2 часа ночи), вам нужно иметь доступ к журналам или транслировать все через PHP или cgi script - что-то просто нужно делать с подсчетом вручную.

Ответ 10

Предполагая, что вы используете PHP в качестве скомпилированного модуля Apache, функция virtual() может сделать это. См.: http://www.php.net/manual/en/function.virtual.php

<?php

$fn = $_GET['fn'];

log_file_access($fn); // You define how you want this to happen    
virtual($fn);

Затем вы ссылаетесь на файлы через:

http://example.com/file.php?fn=files/lolcat.jpg