Задать вопрос
Ответы пользователя по тегу PHP
  • Установить cookie с другим domain?

    @granty
    В чём там проблема? Открывай ифрейм 1х1px с src= этого сервера. Скрипт сервера в ответ пусть высылает куку со своим доменом и установит в ней флаг httpOnly.
    Ответ написан
    Комментировать
  • Насколько безопасно использование сессий в php?

    @granty
    Сессия - это Кука с именем PHPSESSID (по умолчанию, его можно изменить) и значением вида 8jae35cosacp2f5qv6g2uqe6i7. Все данные сессии (массив $_SESSION) хранятся на стороне сервера в файле с именем 8jae35cosacp2f5qv6g2uqe6i7 в формате JSON (можно хранить и в БД).
    То есть, вам надо не дописывать 1, а угадывать это имя, что нереально.

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

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

    - установить заголовок HSTS, это заставит браузер работать только по HTTPS, те Кука будет зашифрована при передаче по сети.

    - в самой сессии можно хранить IP адрес(привязка сессии к IP). Тогда даже с правильной Кукой не залогиниться, поскольку не совпадет IP (который хранится в $_SESSION на стороне сервера). Неудобно если провайдер меняет IP при каждом переподключении.

    - можно в сессии хранить User Agent (все равно Кука - она только для этого браузера). Но при автоматическим апдейте браузера придётся авторизоваться заново, и у кого хватило ума перехватить Куку - перехватит и имя User Agent-а.

    - можно в сессии хранить fingerprint браузера (или даже всего компа), только надо придумать как безопасно передавать его на сервер, чтобы сохранять в сессии (и для сравнения при авторизации). Его же тоже могут перехватить.

    - при каждой авторизации по Куке, и через каждые ## секунд можно(и нужно) делать session_regenerate_id (меняется 8jae35cosacp2f5qv6g2uqe6i7 на другое), там по ссылке есть пример как это сделать прямо внутри сессии. То есть, угнанная Кука быстро перестаёт работать.

    - можно делать "сессионную" Куку (не указывать её время жизни). Такая Кука живёт до закрытия браузера, но после закрытия браузера придётся заново вводить логин/пароль

    Можно добавить своей безопасности - например отправлять email пользователю при каждой авторизации по Куке, если не было активности Пользователя более 12 часов.

    PS: Если пользоваться сессиями правильно - они достаточно безопасны, практически вся авторизация в интернетах построена на них.
    Ответ написан
    Комментировать
  • Как перенаправить пользователя на страницу 404 с помощью header?

    @granty
    В вашем файле post.php на входе вы имеете два параметра: $_GET['id'] и $_GET['language']
    По $_GET['id'] вы лезете в БД и формируете статью для показа. Вам надо в верхней части post.php дополнительно сформировать ЧПУ-шную часть URL этой статьи (для этого и используется ваша seo_replace()), и сравнить его с $_GET['language']. Если они совпадают - отдаёте статью, если нет - делаете одно из 2-х:
    a) Redirect 301 на правильный $URL, вы его уже сгенерили для сверки с $_GET['language']:
    header($_SERVER["SERVER_PROTOCOL"].' 301 Moved Permanently');
    header("Location: $URL");
    exit;

    или

    б) Отдаёте 404 страницу:
    header($_SERVER["SERVER_PROTOCOL"].' 404 Not Found'); exit;


    Что лучше сделать - решайте сами, ибо такое псевдо-ЧПУ это всегда гемор.
    - Недруги могут ставить на ваш сайт ссылки вида /post/4/erotika_bez_sms и по id=4 страница будет показываться. При редиректе такие ссылки приклеятся на статью, а по 404 - нет.
    - при простановке внешних ссылок может обрезаться часть ссылки, но по Redirect 301 вы её подберёте, если сохранилась часть /post/4/.

    в догонку

    У вас в функции seo_replace() ошибка - используется непроинициированная переменная $finalna_nazwa (там должна стоять $final_name).
    Кстати, странное название $finalna_nazwa. Пани разумеет по-польску?
    Ответ написан
    Комментировать
  • Проверка уникальности записи, как это работает?

    @granty
    Я хочу чтобы при совпадении определённой записи в БД, он выводил ошибку. Для этого, я для столбца поставил Индекс.
    Поле email в БД должно быть UNIQUE INDEX, тогда при вставке дубля MySQL будет кидать ошибку, которую можно обрабатывать.
    Или можно использовать конструкцию INSERT IGNORE ... ON DUPLICATE KEY, например, чтобы вернуть ID записи с этим email.
    Ответ написан
    Комментировать
  • Как осуществить транслит в обе стороны?

    @granty
    при наличии в строке ь ъ(мягкого и твердого знака) заменяется в транслите ' и '', что мне не подходит, тк я строку передаю в url и уже с этого url выдергиваю строку и обратно на русский язык перевожу
    Почему? Согласно RFC3986 все недопустимые в URL символы должны кодироваться, и в PHP для этого есть функции rawurlencode() и rawurldecode().
    Просто обрабатываете url ими, они правильно закодируют ваши ' и потом обратно их декодируют (останется только экранировать их, когда будете пихать в SQL-запрос).
    Как, по вашему, получаются url вида https://ru.wikipedia.org/wiki/Набор_символов?

    Зато ваши URL будут сделаны в точности по ГОСТ 7.79-2000 "ПРАВИЛА ТРАНСЛИТЕРАЦИИ КИРИЛЛОВСКОГО ПИСЬМА ЛАТИНСКИМ АЛФАВИТОМ"
    (КИРИЛЛОВСКОГО, блин, черти нерусские!)

    зачем вам ЧПУ?
    Но что-то мне подсказывает, что ЧПУ на сайте вам нужно совсем для другого...
    Только вы не поставили тег про "это другое" в своём вопросе. Поэтому, даже решив проблему с кодированием Ь и Ъ, вы получите неправильное ЧПУ с точки зрения этого "чего-то другого" - того, о чём вы даже и не спросили.
    Ответ написан
    Комментировать
  • Как правильно распознавать авторизованного пользователя?

    @granty
    Сессия в PHP - это практически те же ваши "куки + БД", только вместо БД используется файл сессии. Он будет свой для каждого пользователя.
    Генерируется уникальный хэш (ID сессии), он садится пользователю в куки, а на сервере создаётся файл с именем == ID сессии. PHP все это делает сам.

    В файле сессии (на сервере) хранятся все переменные, которые вы захотите. Когда поднимается сессия, PHP автоматически инициирует массив $_SESSION
    с вашими переменными (Имя пользователя, IP пользователя, дата последнего логина, права пользователя и тп).

    Под работу с сессиями в PHP есть встроенные функции, они не все работают очевидным образом, но разобраться можно.

    PS: Если не поддерживаются куки, то ID сессии может передаваться как параметр в URL.

    есть ли возможность в случае с сессиями сохранить пользователя к примеру на месяц?
    Да. Ставите у сессионной куки время жизни 1 месяц, и через месяц браузер пользователя автоматически её уничтожит. После этого надо будет авторизоваться заново.
    Но на сервере есть параметр настроек PHP session.gc_maxlifetime, который задает время жизни PHP-сессии на сервере. После истечения session.gc_maxlifetime PHP garbage collection удаляет файл сессии на сервере. То есть, его надо ставить > месяца.

    какой вариант предпочтительнее
    вопрос холливарный. Если есть надо мониторить пользователей (кто, когда и зашёл и что сделал) - по БД это удобнее, чем рыться руками по тысяче файлов.
    В плане безопасности - одинаково, тк работает один и тот же механизм. Угон куков -> подмена сессии.

    PS: На хабре есть неплохая статья Подводные камни использования сессий в PHP.
    Ответ написан
    1 комментарий
  • Как создать приложение в котором открывается конкретный сайт?

    @granty
    Пишете манифест к каждому сайту, в манифестах указываете "display": "standalone" чтобы отключить UI браузера.

    После этого иконку сайта можно добавлять на рабочий стол мобильного устройства без магазина приложений, одними штатными средствами Android/iOS: меню -> Добавить на домашний экран. Под Windows 10 на десктопах тоже должно работать.

    Открываться сайт будет по тапу на такую иконку и будет похож на нативное приложение.

    Ссылки по теме
    Ответ написан
    Комментировать
  • Можно ли (и как) определить яндекс бота с помощью PHP?

    @granty
    1. Со 100% точностью - невозможно.

    2. Что в вашем понимании Яндекс.Бот? У Яндекса с десяток различных сервисов, которые самостоятельно лазят по сайтам с разными ЮзерАгентами, исполняют скрипты, применяют стили.
    Я.Метрика - это Яндекс.Бот? Яндекс.Директ это Яндекс.Бот?

    Определять по User Agent можно только роботов-индексаторов, и то не всех. Для индексации мобильной версии сайта, бот-индексатор приходит с User Agent настоящего мобильного устройства.
    И парсеры сайтов часто приходят с Яндексовским или Гугловским юзерагентом.

    Вычислять ботов по IP подсетей Яндекса тоже не даст 100% точности - роботы проверки на клоакинг приходят из "левых" подсетей, чаще всего из Корбины. И есть публичные Яндекс.прокси в "яндексовских" диапазонах IP, например, для режима turbo Яндекс-браузеров.
    Ответ написан
    Комментировать
  • PHP обработчик не получает POST запрос?

    @granty
    При 'Content-Type': 'application/json' массивы-обертки $_POST /$_GET на сервере не заполняются, так не присылается Имя_Параметра, а присылается только Значение_Параметра.
    Сервер не может обработать такие данные автоматически ($_POST - ассоциативный массив, а имя_переменной не прислано).

    Получить такие данные можно только через php://input
    Ответ написан
    1 комментарий
  • Как избавиться от гкода?

    @granty
    Чтобы избавиться от "говнокода", надо избавиться от "говноалгоритма".
    Программист всегда решает задачу в общем виде. Как это сделать - отлично показал usheninmike.
    Вся суть его алгоритма - в строке:
    $repeating = count($digits) - count(array_unique($digits));


    Я просто "допилил" его решение под вашу задачу:
    function win() {
      if (func_num_args() <=0 ) $digits = array(); // Нет аргументов
      elseif (func_num_args() >1 ) {        // > 1 аргумента
    	  for ($i=0, $digits=array(); $i < func_num_args(); $i++)
    	    $digits[] = func_get_arg($i);
    	  }
      else $digits = str_split( func_get_arg(0) );
      // ... тут надо проверить, что $digits не более 3-х элементов
      $repeating = count($digits) - count(array_unique($digits));
      return $repeating ? $repeating + 1 : 0;
      }

    Такая функция win() будет понимать:
    echo win().'<br>';
    echo win("").'<br>';
    echo win(1,4,1).'<br>';
    echo win(141).'<br>';
    echo win("141").'<br>';
    echo win("11").'<br>';
    echo win(11,11,1).'<br>';

    Disclaimer
    Функция отлично работает с ЦИФРАМИ. Но если понадобится считать СИМВОЛЫ в строке, результат по начале может показаться не очевидным - проверьте: win("§§§").'<br>';
    Почему - разберётесь сами :)
    Ответ написан
    Комментировать
  • Как на php сверить заданный параметр с именем домена?

    @granty
    Михаил Миргородский,
    1. Вы сравниваете 'http://' с 'https://' - строки явно не совпадут. Зачем вы добавляете схему, вам же надо только проверить доменное имя?


    2. $_SERVER['REQUEST_URI'] содержит запрошенный путь на сайте и имя файла:
    /                // запрос морды сайта
    /config.php      // запрос скрипта на сайте
    /upload/get.php  // запрос скрипта из папки
    Зачем вы их добавляете в проверку? Они ни как не влияют на проверку доменного имени.


    3. запрос к вашему сайту по HTTP/1.1:
    GET /path/page.php HTTP/1.1   // относительный URL
    Host: domain.ru
    или:
    GET http://domain.ru/path/page.php HTTP/1.1  // абсолютный URL с доменом
    Host: любой_текст_тут

    $_SERVER['HTTP_HOST'] всегда будет содержать значение из поля Host:. А там можно прислать что угодно, если в GET передаётся абсолютный URL с доменом. Спецификация требует игнорировать заголовок Host:, если в GET содержится абсолютный url - вы уверены, что ваш веб-сервер делает это?
    $_SERVER['HTTP_HOST'] присылается пользователем, доверять ему нельзя. Там может быть и IP-адрес сайта - вполне легальный запрос при определённых настройках веб-сервера.


    4. $_SERVER['SERVER_NAME'] устанавливается веб сервером и содержит имя домена из конфигурации вашего веб-сервера. Ему можно доверять, но есть и грабли. Вот кусок конфига Apache:
    ServerName domain.com        # основной домен
    ServerAlias www.domain.com   # домен с www
    ServerAlias m.domain.com     # мобильная версия сайта
    И, какое из этих 3-х имён пропадёт в $_SERVER['SERVER_NAME']?
    Если в конфиге установлено UseCanonicalName On - в $_SERVER['SERVER_NAME'] всегда будет имя из ServerName конфига веб сервера.
    Если нет - будет одно из этих 3-х имён - то, по которому была запрошена страница сайта:
    http://www.domain.com/index.php -> www.domain.com
    https://m.domain.com/images/upload.php -> m.domain.com
    ws://domain.com/ajax/get.php -> domain.com

    Ответ написан
    Комментировать
  • Как сделать чтобы пользователь смог сделать лайк только один раз php?

    @granty
    Голосование вторизованных посетителей
    Термин "пользователь" подразумевает, что посетитель авторизован(залогинен) на вашем сайте каким-либо способом. Через соцсети или прямым вводом логина пароля (как на форумах или этом сайте).
    В этом случае, Дмитрий Кузнецов выше уже ответил на ваш вопрос.

    Вот, я: с домашнего ноутбука, с ПК на работе или со смартфона в метро по дороге домой - это для вас один и тот же пользователь?
    Для сайта qna.habr.com - да, один и тот же. Потому, что мне надо залогиниться, чтобы совершать на этом сайте значимые действия.


    Голосование неавторизованных посетителей
    Если "лайкать" разрешено "неавторизованным посетителям" - вам предстоит нелёгкая борьба с накруткой лайков. Эта задача не решаема в полном объёме, все анонимные онлайн-голосовалки подвержены уязвимости накрутки.
    Можно попробовать EverCookie, но тогда вы будете идентифицировать на самого "пользователя", у его комп/ноут/смартфон.
    И такие "неубиваемые" куки нарушают закон ЕС о куках, то есть область их применения несколько ограничена.

    PS: Тут уже была тема защита от многократного голосования, посмотрите что там посоветовали.

    Для полноты картины - тематические ссылки на Хабр:
    Борьба с накрутками в рейтингах - страшные математические формулы
    Теория и практика накрутки голосов
    Накрутка голосов в опросе на сайте крупной телевиз...
    Ответ написан
    Комментировать
  • Как обновить страницу пользователя?

    @granty
    Использовать EventSource и Server-Sent Events (есть пример на PHP).
    Описание использования server-sent events на MDN.

    PS: IE/Edge не поддерживают эту технологию
    Ответ написан
    Комментировать
  • Как сделать временную ссылку не на скачивание?

    @granty
    Технически, есть 2 варианта:

    1. Отдавать скачиваемое на PHP. Для генерации временной ссылки можно использовать:

    A) класс NoDirectLinks. Временные ссылки надо складывать в БД и проверять их "валидность" и периодически чистить.

    B) делать временную ссылку вида "ID_ValidDate", которую шифровать методом обратимого шифрования. Если периодически менять ключ кодирования (и алгоритм), такую ссылку невозможно взломать и подделать.
    Внешне ссылка выглядит как хэш 56af5b4C..., но на сервере её можно декодировать обратно в "ID скачиваемой записи по БД" и "время жизни ссылки". Если время жизни истекло - контент не отдаётся. Вместо ID можно запаковывать и имя файла.

    В обоих вариантах делается файл типа get.php, который проверяет "валидность" временной ссылки и отдаёт контент.
    В вашем случае это плохо - при медленном интернете у юзера, загрузка видео/аудио будет идти долго, PHP-скрипт будет висеть и жрать ресурсы.


    2. Делать временную ссылку средствами операционной системы, например, через symlink() на PHP.
    Тогда медиа-файл будет отдаваться веб-сервером без участия PHP, что намного лучше.
    Всё-равно придется хранить эти ссылки в БД и решать вопрос их устаревания и чистки.


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

    PS: Я в своё время делал вариант 1 B) для платного скачивания документации - работало неплохо.

    PPS: Делать временную ссылку для проигрывания медиа-файлов не есть хорошо. Посмотрите как работают плееры ютуба (и другие, не показывающие реальный файл) - они бьют файл на кусочки и отдают частями. Но, даже в этом случае, стандарт HTML5 позволяет скачать файл целиком тем, кто прошарен :)
    Ответ написан
    Комментировать
  • Как правильно взять IP адрес пользователя?

    @granty
    Обычно, подобные вашему глюки возникают при использовании ELB (Elastic Load Balancer) хостером. Или когда сайт работает за реверс-прокси сервером (например, CloudFlare или хостеровский nginx).
    Там в $_SERVER['HTTP_X_FORWARDED_FOR'] прилетает строка из нескольких IP:
    "223.190.123.220, 66.102.6.83"
    "2402:3a80:10d3:e19f:e7e1:8c35:fb4a:b49c, 66.102.6.89"
    И, похоже, Yii::$app->request->userIP не знает как с ними поступать (в строке по сути невалидный IP, и filter_var($ip, FILTER_VALIDATE_IP) на нём обламывается).

    Как правильно определять IP посетителя в таких случаях (и какие ещё грабли вас поджидают), отлично изложено на Хабре: REMOTE_ADDR vs HTTP_X_FORWARDED_FOR.
    Вам надо узнать в какой переменной хостер присылает реальный IP посетителя, если он тупо добавляет её в $_SERVER['HTTP_X_FORWARDED_FOR'] - надо хотя бы узнать в начало или в конец. И выцеплять её оттуда.

    PS: Я с Yii не работал, но скорее всего его можно "настроить" под вашу конкретику, там наверняка это предусмотрено. Посмотрите echo '<pre>'.print_r($_SERVER, true).'</pre>'; что и под какими именами прилетает, может ваш хостер использует нестандартные заголовки для передачи реального IP посетителя.

    PPS: HTTP_X_FORWARDED_FOR в принципе нельзя использовать как IP-адрес. Если пользователь работает через свой прокси (или публичные Google-прокси, прокси Opera-mini и тп) - там бывает 3 IP-адреса и более, включая IPv6.
    Ответ написан
    Комментировать
  • Из textarea перевести массив с новой строки и записать в mysql все?

    @granty
    $new_coupon->code = $this->request->post('new_code', 'string');
    $arr =  preg_split("|[\r\n]|s", $new_coupon->code, -1, PREG_SPLIT_NO_EMPTY); // Все и непустые

    далее в цикле строки из $arr заносить в БД.
    Ответ написан
    Комментировать
  • Как убрать символ?

    @granty
    В PHP для этого предусмотрена функция stripslashes().
    Ответ написан
    Комментировать
  • Как правильно экранировать символы в SQL запросы, если нужно отправить HTML-код?

    @granty
    Никита Гуриев, сделайте
    $sql = "INSERT INTO `ваша_таблица`  data='".mysql_real_escape_string($data)."' WHERE ваше условие вставки";

    всё должно вставиться. Имя поля и имя таблицы поставьте ваши, $data из каммента Александра выше(полученные через Heredoc).

    Без Heredoc у вас не работало, потому, что вы строку брали в двойные кавычки ", и внутри неё использовали " без экранирования в:
    "src": '$NewFileDir',
    и переносы строк внутри " " могли отрабатываться неправильно.


    Есть железобетонный вариант вставить всё что угодно (если размера поля БД хватит):
    $sql = "INSERT INTO `ваша_таблица`  data='".base64_encode( gzcompress($data) )."'";

    а когда достаёте из БД - надо распаковать обратно:
    $data = gzuncompress( base64_decode( $row['data' ] ) );

    Из минусов:
    - доп нагрузка на запаковку-распаковку, но для вашей строки это ерунда. base64_encode() увеличивает размер данных примерно в 1.5 раза, но zip жмёт сильнее.

    Из плюсов:
    - SQL-инъекции отсутствуют как класс
    - ничего не надо экранировать.
    - кодировка данных - по барабану, вставится даже UTF8-данные в таблицу cp1251. Главное, потом их отдать в правильной кодировке (когда вытащите из таблицы и будете отправлять в браузер)
    Ответ написан
    Комментировать
  • Как вывести статьи в зависимости от сочетания двух select/option используя php и mysql?

    @granty
    Вы написали только страницу показа "селектов" из БД.

    1. Ваши < select id="station"> и < select id="flats"> надо вставить внутрь < form action=''> ..., сделать в ней кнопку submit и отправлять значения выбранных селектов на сервер, а он уже по ним должен выбрать статьи из БД и возвращать страницу с результатами.

    Надо добавить name='' к вашим селектам, по этим именам вы будете выбирать присланные в них значения из массивов $_POST/$_GET:
    < select id="station" name="station"> и < select id="flats" name="flats">

    2. Второй вариант - делать запрос к серверу на ajax (передавая значения выбранных селектов) и вставлять ответ от сервера на страницу.

    Поройтесь в интернете, там примеров навалом.

    PS: Потом вы столкнётесь с тем, что сначала посетитель должен выбрать < select id="station" name="station">, и в зависимости от выбранной станции ему должен динамически подгрузиться список < select id="flats" name="flats"> которые расположены рядом с этой станцией (а не все имеющиеся квартиры). Иначе теряется смысл выбора станции.
    Короче, продумывайте архитектуру, читайте про ajax/XMLHttpRequest (проще всего будет писать яваскрипты с помощью библиотеки jquery).
    Ида, примеров реализации этого в интернете навалом.
    Ответ написан
    3 комментария
  • Как создать кодировку подключения?

    @granty
    Для корректной работы клиента с сервером MySQL надо правильно установить 4 параметра кодировки подключения:

    character_set_client - указывает, в какой кодировке будут поступать данные от клиента;

    character_set_connection - указывает, в какую кодировку следует преобразовать данные полученые от клиента перед выполнением запроса;

    collation_connection - указывает, каким образом сравнивать между собой строки в запросах;

    character_set_results - указывает серверу на необходимость перекодировать результаты запроса в определенную кодировку перед выдачей их клиенту.

    Если запрос и данные в БД находятся в одинаковой кодировке, и перекодировка результата не требуется, то вместо установки character_set_client, character_set_connection, character_set_results достаточно выполнить:
    mysqli_query("SET NAMES 'utf8' ");

    или, если collation не был задан при создании БД:
    mysqli_query("SET NAMES 'utf8' COLLATE 'utf8_general_ci' ");


    PS: В MySQL версий > 4.1 кодировку и collation можно установить на уровне БД, на уровне таблицы, и даже на уровне отдельной записи в таблице, по ссылке выше есть примеры.
    Ответ написан