Запретить прямой доступ к файлу php


166

У меня есть php-файл, который я буду использовать исключительно для включения. Поэтому я хотел бы выдать ошибку вместо того, чтобы выполнить ее, когда к ней обращаются напрямую, введя URL вместо того, чтобы быть включенным.

В основном мне нужно сделать проверку следующим образом в файле php:

if ( $REQUEST_URL == $URL_OF_CURRENT_PAGE ) die ("Direct access not premitted");

Есть простой способ сделать это?


10
вместо die () вы должны проверить заголовок '(HTTP / 1.1 404 File Not Found ", 404); выход;'. Это (по крайней мере на Apache) заставит сервер вернуть нормальную страницу 404.
gnud

Вот два простых метода, которые я объяснил, чтобы отключить прямой доступ во включаемых файлах PHP - codespeedy.com/disable-direct-access-to-the-php-include-file
Фарук Ахамед Моллик,

Ответы:


174

Самый простой способ для общей ситуации «приложение PHP, работающее на сервере Apache, которым вы можете или не можете полностью управлять», - это поместить ваши включения в каталог и запретить доступ к этому каталогу в вашем файле .htaccess. Чтобы избавить людей от проблем с Google, если вы используете Apache, поместите его в файл с именем «.htaccess» в каталоге, который вам не нужен:

Deny from all

Если вы на самом деле имеете полный контроль над сервером (в наши дни это более распространено даже для небольших приложений, чем когда я впервые писал этот ответ), лучший способ - разместить файлы, которые вы хотите защитить, за пределами каталога, из которого обслуживается ваш веб-сервер. , Поэтому, если ваше приложение включено /srv/YourApp/, настройте сервер для обслуживания файлов /srv/YourApp/app/и вставьте включенные файлы /srv/YourApp/includes, чтобы буквально не было URL-адресов, которые могли бы получить к ним доступ.


1
Спасибо, так как у меня есть полный контроль над сервером, на котором я запускаю это приложение, это ответ, с которым я пошел.
Alterlife

24
если у вас есть полный контроль над сервером, лучше, если вы поместите конфигурацию в директиву каталога в файл конфигурации виртуального хоста. Apache читает его только один раз при запуске, .htaccess читается при каждом доступе и замедляет работу сервера
Eineki

22
Было бы неплохо иметь пример файла .htaccess как часть этого ответа.
Грэм Ли

7
<Files ~ "\.inc$"> Order Allow,Deny Deny from All </Files>
Дракорат

11
@James: Кроме того, не все считают, что Stack Overflow должен быть сайтом «plz send teh codez». Если он четко отвечает на вопрос, то это хороший ответ. Предоставление примера, где ничего не нужно, только поощряет копирование и вставку кода.
Чак

188

Добавьте это на страницу, которую хотите включить только

<?php
if(!defined('MyConst')) {
   die('Direct access not permitted');
}
?>

затем на страницах, которые включают его добавить

<?php
define('MyConst', TRUE);
?>

3
Мне действительно нужно научиться печатать быстрее. Это тот же способ, который я бы предложил, поскольку он более безопасен, чем метод, который использует переменную для проверки. Так как с некоторыми настройками PHP может быть возможно переопределить переменную.
Марк Дэвидсон

3
Вот как это делают несколько «основных» приложений. Я знаю, что Joomla делает это таким образом, и я думаю, что Wiki, Wordpress и другие тоже.
UnkwnTech

1
Возможно, сообщение слишком полезно для хакера (ни один реальный пользователь не найдет эти страницы), вы можете просто отправить заголовок перенаправления и остановить обработку php.
Банди

7
Просто отправьте заголовок 404 и выйдите - страница с ошибкой будет выглядеть так же, как обычные страницы 404 (по крайней мере, в Apache).
gnud

4
@ Smile.Hunter: речь идет о блокировке доступа к просмотру ваших файлов сценариев include / library напрямую, ответ работает. Если они создали somefile.phpна вашем сервере и добавили в него ваше определение, это все равно не дает им прямой доступ к файлу включения. Это позволит им «включать» файлы вашей библиотеки, но если они зайдут достаточно далеко, чтобы создавать файлы на вашем сервере и знать ваши сценарии define / include, у вас возникнут другие проблемы, которые, скорее всего, не позволят написать собственный файл с вашим определением. ,
Джеймс

114

У меня есть файл, который мне нужно действовать по-разному, когда он включен по сравнению с, когда к нему обращаются напрямую (в основном, print()против return()) Вот некоторый модифицированный код:

if(count(get_included_files()) ==1) exit("Direct access not permitted.");

Доступ к файлу всегда является включенным файлом, поэтому == 1.  


12
Это на самом деле адская идея, проверка количества включенных файлов. Интересно, что лучше: использование определений или использование этого метода? Это кажется более замкнутым.
Akoi Meexx

Впервые я видел, как кто-нибудь придумал это. Хотя я не знаю почему, потому что он кажется настолько самодостаточным, насколько это возможно, и он непосредственно измеряет то, что вы на самом деле хотите знать (включено или нет), а не измеряет что-то, предположительно связанное (например, некоторую константу или определенное местоположение запрещено .htaccess). Прекрасный.
Джимбо Джонни

Это действительно круто, потому что использование .htaccess для блокировки всех файлов .php может быть невозможно все время, так как в одном каталоге могут находиться некоторые файлы, которые необходимо вызывать напрямую или даже через javascripts. Спасибо за эту прекрасную идею!
Anuj

3
Это работает только в PHP5 и выше. Перед PHP5 вы хотите сравнить снова 0 вместо 1.
jmucchiello

Это действительно умное решение, которое позволяет вам контролировать прямой доступ, а не просто блокировать его - это то, что мне нравится. Я обычно включаю модульное тестирование в сами файлы, и таким образом я могу обернуть свое модульное тестирование в это утверждение if. Интересно, насколько это эффективно ..
whiteatom

34

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

Я обычно иду до конца и помещаю все свои PHP-файлы вне корневого каталога документа, кроме файла начальной загрузки - одиночного index.php в корневом каталоге документа, который начинает маршрутизацию всего сайта / приложения.


3
Это отличное решение, если вы можете это сделать. Мне только недавно пришлось начать работать с общими веб-хостами, и я обнаружил, что одним из многих раздражающих моментов является то, что все должно быть внутри докрута.
Бо Сименсен

3
В каждом хостинг-провайдере, с которым я работал, у меня всегда был доступ (ровно) на один уровень выше корня документа.
Эран Гальперин

3
На некоторых хостах (включая мой текущий) вы можете указать свой домен на любую папку, какую пожелаете.
Дина

1
Это также хорошая альтернатива .. использование preg_match -> if (preg_match ("~ globalfile \ .php ~ i", $ _SERVER ['PHP_SELF'])) {die ('<h3 style = "color: red"> Предупреждение устройства безопасности - прямой доступ запрещен! Ваш IP-адрес зарегистрирован! <H3> '); // Остановить дальнейшее исполнение}
MarcoZen

Этот URL devzone.zend.com/node/view/id/70 теперь 404. Ответ должен включать код, который был первоначально использован из этого несуществующего URL.
Funk Forty Niner

31

1: Проверка количества включенных файлов

if( count(get_included_files()) == ((version_compare(PHP_VERSION, '5.0.0', '>='))?1:0) )
{
    exit('Restricted Access');
}

Логика: PHP завершается, если минимальное число включений не выполнено. Обратите внимание, что до PHP5 базовая страница не считается включаемой.


2: определение и проверка глобальной константы

// In the base page (directly accessed):
define('_DEFVAR', 1);

// In the include files (where direct access isn't permitted):
defined('_DEFVAR') or exit('Restricted Access');

Логика: если константа не определена, то выполнение не начнется с базовой страницы, и PHP прекратит выполнение.

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

// Put the code in a separate file instead, say 'checkdefined.php':
defined('_DEFVAR') or exit('Restricted Access');

// Replace the same code in the include files with:
require_once('checkdefined.php');

Таким образом, дополнительный код может быть добавлен checkdefined.php для регистрации и аналитических целей, а также для генерации соответствующих ответов.

Кредит, где кредит должен: блестящая идея переносимости пришла из этого ответа .


3: Удаленная авторизация адреса

// Call the include from the base page(directly accessed):
$includeData = file_get_contents("http://127.0.0.1/component.php?auth=token");

// In the include files (where direct access isn't permitted):
$src = $_SERVER['REMOTE_ADDR']; // Get the source address
$auth = authoriseIP($src); // Authorisation algorithm
if( !$auth ) exit('Restricted Access');

Недостатком этого метода является изолированное выполнение, если только токен сеанса не предоставлен с внутренним запросом. Проверьте через адрес обратной связи в случае конфигурации с одним сервером или белый список адресов для многосерверной или серверной инфраструктуры с балансировкой нагрузки.


4: авторизация токена

Как и в предыдущем методе, можно использовать GET или POST для передачи токена авторизации во включаемый файл:

if($key!="serv97602"){header("Location: ".$dart);exit();}

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


5: Конфигурация веб-сервера

Большинство серверов позволяют вам назначать разрешения для отдельных файлов или каталогов. Вы можете поместить все свои включения в такие ограниченные каталоги и настроить сервер на их запрет.

Например, в APACHE конфигурация хранится в .htaccessфайле. Учебник здесь .

Однако обратите внимание, что специфичные для сервера конфигурации не рекомендуются мной, поскольку они плохо переносятся между различными веб-серверами. В таких случаях, как системы управления контентом, где алгоритм deny сложен или список запрещенных каталогов довольно велик, это может сделать сеансы реконфигурации довольно ужасными. В конце концов, лучше всего справиться с этим в коде.


6: Размещение включает в безопасном каталоге за пределами корня сайта

Наименее предпочтительный из-за ограничений доступа в серверных средах, но довольно мощный метод, если у вас есть доступ к файловой системе.

//Your secure dir path based on server file-system
$secure_dir=dirname($_SERVER['DOCUMENT_ROOT']).DIRECTORY_SEPARATOR."secure".DIRECTORY_SEPARATOR;
include($secure_dir."securepage.php");

Логика:

  • Пользователь не может запросить какой-либо файл за пределами htdocsпапки, поскольку ссылки будут выходить за рамки адресной системы сайта.
  • Php-сервер напрямую обращается к файловой системе и, следовательно, может обращаться к файлам на компьютере, как это делает обычная программа с необходимыми привилегиями.
  • Поместив включаемые файлы в этот каталог, вы можете гарантировать, что php-сервер получит к ним доступ, в то время как горячая ссылка запрещена для пользователя.
  • Даже если настройка доступа к файловой системе веб-сервера не была выполнена должным образом, этот метод предотвратит случайное обнародование этих файлов.

Пожалуйста, извините мои неортодоксальные соглашения о кодировании. Любые отзывы приветствуются.


мне нравится номер 2
Baim Wrong

26

Альтернативой (или дополнением) решению Чака было бы запретить доступ к файлам, соответствующим определенному шаблону, добавив что-то подобное в ваш файл .htaccess.

<FilesMatch "\.(inc)$">
    Order deny,allow
    Deny from all
</FilesMatch>

Я считаю, что было бы лучше использовать .inc.php, и это обычная практика
Lis

14

На самом деле мой совет - использовать все эти лучшие практики.

  • Поместите документы вне веб-корня ИЛИ в каталог, к которому веб-сервер не имеет доступа И
  • Используйте определение в ваших видимых документах, которые проверяют скрытые документы:
      if (!defined(INCL_FILE_FOO)) {
          header('HTTP/1.0 403 Forbidden');
          exit;
      }

Таким образом, если файлы становятся неуместными (ошибочная операция ftp), они все еще защищены.


8

Однажды у меня была эта проблема, решенная с помощью:

if (strpos($_SERVER['REQUEST_URI'], basename(__FILE__)) !== false) ...

но идеальным решением является размещение файла вне корневого каталога документов веб-сервера, как упоминалось в другом ответе.


7

Вам лучше создать приложение с одной точкой входа, т.е. все файлы должны быть доступны из index.php

Поместите это в index.php

define(A,true);

Эта проверка должна выполняться в каждом связанном файле (через require или include)

defined('A') or die(header('HTTP/1.0 403 Forbidden'));

7

Я хотел ограничить доступ к файлу PHP напрямую, но также иметь возможность вызывать его через jQuery $.ajax (XMLHttpRequest). Вот что сработало для меня.

if (empty($_SERVER["HTTP_X_REQUESTED_WITH"]) && $_SERVER["HTTP_X_REQUESTED_WITH"] != "XMLHttpRequest") {
    if (realpath($_SERVER["SCRIPT_FILENAME"]) == __FILE__) { // direct access denied
        header("Location: /403");
        exit;
    }
}

3

Самый простой способ - установить переменную в файле, который вызывает include, например:

$including = true;

Затем в файле, который включен, проверьте переменную

if (!$including) exit("direct access not permitted");

2
Это опасно, если register_globals включен.
jmucchiello

25
PHP опасен, если register_globals включен.
David Precious

3

Помимо способа .htaccess, я видел полезный шаблон в различных фреймворках, например в ruby ​​на рельсах. У них есть отдельный каталог pub / в корневом каталоге приложения, а каталоги библиотеки живут в каталогах на же уровня, что и pub /. Примерно так (не идеально, но вы поняли):

app/
 |
 +--pub/
 |
 +--lib/
 |
 +--conf/
 |
 +--models/
 |
 +--views/
 |
 +--controllers/

Вы настроили свой веб-сервер на использование pub / в качестве корня документа. Это обеспечивает лучшую защиту ваших сценариев: хотя они могут обращаться к корневому каталогу документа для загрузки необходимых компонентов, доступ к компонентам из Интернета невозможен. Другое преимущество, помимо безопасности, заключается в том, что все в одном месте.

Эта настройка лучше, чем просто создание проверок в каждом включенном файле, потому что сообщение «доступ запрещен» является подсказкой для злоумышленников, и это лучше, чем конфигурация .htaccess, потому что она не основана на белом списке: если вы испортили расширения файла он не будет виден в каталогах lib /, conf / etc.


Спустя долгое время я просто хочу прокомментировать, что модель, которую вы описываете выше, называется моделью MVC (Model - View - Controller). Пожалуйста, проверьте Google и добавьте дополнительную информацию к своему ответу. Также MVC поддерживают не только приложения Ruby on Rails и ASP.NET, но и PHP (см. Lavarel, CakePHP).

3

Какая Joomla! do определяет Константу в корневом файле и проверяет, определена ли она во включаемых файлах.

defined('_JEXEC') or die('Restricted access');

или еще

Можно хранить все файлы вне досягаемости http-запроса, размещая их вне каталога webroot, как рекомендует большинство фреймворков, таких как CodeIgniter.

или даже поместив файл .htaccess в папку include и правила записи, вы можете запретить прямой доступ.



3

Мой ответ несколько отличается в подходе, но включает в себя многие ответы, представленные здесь. Я бы порекомендовал многоплановый подход:

  1. Ограничения .htaccess и Apache наверняка
  2. defined('_SOMECONSTANT') or die('Hackers! Be gone!');

ОДНАКОdefined or die подход имеет ряд недостатков. Во-первых, это реальная боль в допущениях для тестирования и отладки. Во-вторых, если вы передумаете, это будет ужасающе, ошеломляюще скучным рефакторингом. "Найти и заменить!" ты говоришь. Да, но насколько вы уверены, что везде написано одно и то же, хммм? Теперь умножьте это на тысячи файлов ... oO

И тогда есть .htaccess. Что произойдет, если ваш код распространяется на сайты, где администратор не так скрупулезен? Если вы полагаетесь только на .htaccess для защиты своих файлов, вам также понадобятся: а) резервная копия, б) коробка с салфетками, чтобы высушить ваши слезы, в) огнетушитель для тушения пламени во всей ненависти от людей используя ваш код

Итак, я знаю, что вопрос требует «самого простого», но я думаю, что это требует более «защитного кодирования».

Я предлагаю следующее:

  1. Перед любым из ваших сценариев require('ifyoulieyougonnadie.php');не include() в качестве замены defined or die)
  2. В ifyoulieyougonnadie.php, сделать некоторые логики вещей - проверку для различных констант, вызывая сценарий, локальное тестирование и такой - а затем реализовать ваши die(), throw new Exception, 403, и т.д.

    Я создаю свой собственный фреймворк с двумя возможными точками входа - основной index.php (фреймворк Joomla) и ajaxrouter.php (мой фреймворк) - поэтому в зависимости от точки входа я проверяю разные вещи. Если запросifyoulieyougonnadie.php не приходит из одного из этих двух файлов, я знаю, что махинации предпринимаются!

    Но что, если я добавлю новую точку входа? Не беспокойся. Я просто меняюifyoulieyougonnadie.php и сортирую, плюс нет «найди и замени». Ура!

    Что если я решил переместить некоторые из моих сценариев для создания другой среды, которая не имеет одинаковых констант defined()? ... Ура! ^ _ ^

Я обнаружил, что эта стратегия делает разработку намного веселее и намного меньше:

/**
 * Hmmm... why is my netbeans debugger only showing a blank white page 
 * for this script (that is being tested outside the framework)?
 * Later... I just don't understand why my code is not working...
 * Much later... There are no error messages or anything! 
 * Why is it not working!?!
 * I HATE PHP!!!
 * 
 * Scroll back to the top of my 100s of lines of code...
 * U_U
 *
 * Sorry PHP. I didn't mean what I said. I was just upset.
 */

 // defined('_JEXEC') or die();

 class perfectlyWorkingCode {}

 perfectlyWorkingCode::nowDoingStuffBecauseIRememberedToCommentOutTheDie();

2

Если точнее, вы должны использовать это условие:

if (array_search(__FILE__, get_included_files()) === 0) {
    echo 'direct access';
}
else {
    echo 'included';
}

get_included_files () возвращает индексированный массив, содержащий имена всех включенных файлов (если файл выполняется как beign, то он был включен, и его имя находится в массиве). Таким образом, когда файл доступен напрямую, его имя является первым в массиве, все остальные файлы в массиве были включены.


1
<?php
if (eregi("YOUR_INCLUDED_PHP_FILE_NAME", $_SERVER['PHP_SELF'])) { 
 die("<h4>You don't have right permission to access this file directly.</h4>");
}
?>

Поместите код выше в верхней части вашего включенного файла PHP.

например:

<?php
if (eregi("some_functions.php", $_SERVER['PHP_SELF'])) {
    die("<h4>You don't have right permission to access this file directly.</h4>");
}

    // do something
?>

if (preg_match ("~ globalfile \ .php ~ i", $ _SERVER ['PHP_SELF'])) {die ('<h3 style = "color: red"> Предупреждение безопасности устройства - прямой доступ запрещен! Ваш IP-адрес зарегистрирован ! <h3> '); // Остановить дальнейшее выполнение} whre ~ является разделителем
MarcoZen


1

Я нашел это php-only и неизменное решение, которое работает как с http, так и с cli:

Определить функцию:

function forbidDirectAccess($file) {
    $self = getcwd()."/".trim($_SERVER["PHP_SELF"], "/");
    (substr_compare($file, $self, -strlen($self)) != 0) or die('Restricted access');
}

Вызовите функцию в файле, к которому вы хотите запретить прямой доступ:

forbidDirectAccess(__FILE__);

Большинство из приведенных выше решений этого вопроса не работают в режиме Cli.


где предполагается вводить URL в режиме CLI?
Ваш здравый смысл

Это просто для предотвращения запуска php script / inlude в режиме cli. Может быть полезным в проекте с несколькими разработчиками.
Ка.



1

Хранение ваших включаемых файлов вне каталога, доступного через Интернет, упоминалось несколько раз и, по возможности, является хорошей стратегией. Тем не менее, еще один вариант, который я еще не видел, был упомянут: убедитесь, что ваши включаемые файлы не содержат исполняемый код . Если ваши включаемые файлы просто определяют функции и классы и не имеют никакого другого кода, они просто создадут пустую страницу при прямом доступе.

Разумеется, разрешите прямой доступ к этому файлу из браузера: он ничего не сделает . Он определяет некоторые функции, но ни одна из них не вызывается, поэтому ни одна из них не запускается.

<?php

function a() {
    // function body
}

function b() {
    // function body
}

То же самое относится и к файлам, которые содержат только классы PHP и ничего больше.


По-прежнему рекомендуется хранить файлы вне веб-каталога, где это возможно.

  • Вы можете случайно отключить PHP, и в этом случае ваш сервер может отправлять содержимое файлов PHP в браузер вместо того, чтобы запускать PHP и отправлять результат. Это может привести к утечке кода (включая пароли базы данных, ключи API и т. Д.).
  • Файлы в веб-каталоге сидят на корточках по URL-адресам, которые вы можете использовать для своего приложения. Я работаю с CMS, у которой не может быть вызвана страница system, потому что это будет конфликтовать с путем, используемым для кода. Я нахожу это раздражающим.

0

Сделать что-то вроде:

<?php
if ($_SERVER['SCRIPT_FILENAME'] == '<path to php include file>') {
    header('HTTP/1.0 403 Forbidden');
    exit('Forbidden');
}
?>

Это не помешает его загрузке в браузер.
UnkwnTech

0

Вы можете использовать следующий метод ниже, хотя, у него есть недостаток, потому что он может быть подделан, за исключением случаев, когда вы можете добавить еще одну строку кода, чтобы убедиться, что запрос поступает только с вашего сервера либо с помощью Javascript. Вы можете поместить этот код в раздел «Тело» своего HTML-кода, чтобы там отображалась ошибка.

<?
if(!isset($_SERVER['HTTP_REQUEST'])) { include ('error_file.php'); }
else { ?>

Поместите другой код HTML здесь

<? } ?>

Завершите это так, чтобы вывод ошибки всегда отображался в разделе body, если вы хотите, чтобы это было так.


Я понимаю, что всем заголовкам сервера HTTP_ * нельзя доверять, поэтому лучше не использовать этот метод.
andreszs

0

Я предлагаю не использовать $_SERVERпо соображениям безопасности.
Вы можете использовать переменную, как $root=true;в первом файле, который включал другой.
и использовать isset($root)в начале второго файла, который будет включен.


0

Что вы также можете сделать, это защитить каталог паролем и хранить там все свои php-скрипты, разумеется, кроме файла index.php, поскольку во время включения пароль не потребуется, поскольку он потребуется только для доступа по http. он также предоставит вам возможность доступа к вашим сценариям, если вы захотите, поскольку у вас будет пароль для доступа к этому каталогу. вам потребуется настроить файл .htaccess для каталога и файл .htpasswd для аутентификации пользователя.

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

Надеюсь это поможет


0

Самый простой способ - хранить ваши включения вне веб-каталога. Таким образом, сервер имеет доступ к ним, но не имеет внешнего компьютера. Единственным недостатком является необходимость иметь доступ к этой части вашего сервера. Плюс в том, что он не требует настройки, настройки или дополнительного кода / нагрузки на сервер.


0

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

$currentFileInfo = pathinfo(__FILE__);
$requestInfo = pathinfo($_SERVER['REQUEST_URI']);
if($currentFileInfo['basename'] == $requestInfo['basename']){
    // direct access to file
}

0
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

сделает работу гладко


2
Скопируйте пасту из CodeIgnitor. Это круто, но на самом деле ничего не делает само по себе. Это BASEPATH const устанавливается в index.phpфайле, который лежит в нижней части древовидной структуры. CI переписывает URL-адреса, поэтому нет необходимости напрямую обращаться к сценариям.
Jimasun

я знаю, что в этом нет необходимости, но если кто-нибудь попытается это сделать
Варшаан,

0

Добавлено упомянутое ранее решение с проверкой версии PHP:

    $max_includes = version_compare(PHP_VERSION, '5', '<') ? 0 : 1;
    if (count(get_included_files()) <= $max_includes)
    {
        exit('Direct access is not allowed.');
    }

2
Я не очень понимаю, как это может помешать прямому доступу
Адам Линдсей,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.