Подводные камни использования сессий в PHP. Сессии в PHP Регистрация пользователей PHP MySQL с активацией письмом

17.02.2023

Приветствую, уважаемое сообщество.

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

Цель этой статьи - осветить подводные камни использования сессий в PHP. Конечно, есть документация по PHP и масса примеров, и данная статья не претендует на полное руководство. Она призвана раскрыть некоторые ньюансы работы с сессиями и оградить разработчиков от ненужной траты времени.

Самым распространенным примером использования сессий является, конечно, авторизация пользователей. Начнем с самой базовой реализации, чтобы последовательно развивать ее по мере появления новых задач.

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

Function startSession() { // Если сессия уже была запущена, прекращаем выполнение и возвращаем TRUE // (параметр session.auto_start в файле настроек php.ini должен быть выключен - значение по умолчанию) if (session_id()) return true; else return session_start(); // Примечание: До версии 5.3.0 функция session_start()возвращала TRUE даже в случае ошибки. // Если вы используете версию ниже 5.3.0, выполняйте дополнительную проверку session_id() // после вызова session_start() } function destroySession() { if (session_id()) { // Если есть активная сессия, удаляем куки сессии, setcookie(session_name(), session_id(), time()-60*60*24); // и уничтожаем сессию session_unset(); session_destroy(); } }

Примечание: Подразумевается, что базовые знания о сессиях PHP у читателя имеются, поэтому принцип работы функций session_start() и session_destroy() освещать здесь не будем. Задачи верстки формы входа и аутентификации пользователя не относятся к теме статьи, поэтому их мы также опустим. Напомню только, что для идентификации пользователя в каждом последующем запросе, нам необходимо в момент успешного входа сохранить в сессионной переменной (с именем userid, например) идентификатор пользователя, который будет доступен во всех последующих запросах в пределах жизни сессии. Также необходимо реализовать обработку результата нашей функции startSession(). Если функция вернула FALSE - отобразить в браузере форму входа. Если функция вернула TRUE, и сессионная переменная, содержащая идентификатор авторизованного пользователя (в нашем случае - userid), существует - отобразить страницу авторизованного пользователя (подробнее об обработке ошибок см. дополнение от 2013-06-07 в разделе о сессионных переменных).

Пока все понятно. Вопросы начинаются, когда требуется реализовать контроль отсутствия активности пользователя (session timeout), дать возможность одновременной работы в одном браузере нескольких пользователей, а также защитить сессии от несанкционированного использования. Об этом и пойдет речь ниже.

Контроль отсутствия активности пользователя встроенными средствами PHP

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

Function startSession() { // Таймаут отсутствия активности пользователя (в секундах) $sessionLifetime = 300; if (session_id()) return true; // Устанавливаем время жизни куки ini_set("session.cookie_lifetime", $sessionLifetime); // Если таймаут отсутствия активности пользователя задан, устанавливаем время жизни сессии на сервере // Примечание: Для production-сервера рекомендуется предустановить эти параметры в файле php.ini if ($sessionLifetime) ini_set("session.gc_maxlifetime", $sessionLifetime); if (session_start()) { setcookie(session_name(), session_id(), time()+$sessionLifetime); return true; } else return false; }

Немного пояснений. Как известно, PHP определяет, какую именно сессию нужно запустить, по имени куки, передаваемом браузером в заголовке запроса. Браузер же, в свою очередь, получает этот куки от сервера, куда помещает его функция session_start(). Если время жизни куки в браузере истекло, он не будет передан в запросе, а значит PHP не сможет определить, какую сессию нужно запустить, и расценит это как создание новой сессии. Параметр настроек PHP session.gc_maxlifetime, который устанавливается равным нашему таймауту отсутствия активности пользователя, задает время жизни PHP-сессии и контролируется сервером. Работает контроль времени жизни сессии следующим образом (здесь рассматривается пример хранилища сессий во временных файлах как самый распространенный и установленный по умолчанию в PHP вариант).

В момент создания новой сессии в каталоге, установленном как каталог для хранения сессий в параметре настроек PHP session.save_path, создается файл с именем sess_, где - идентификатор сессии. Далее, в каждом запросе, в момент запуска уже существующей сессии, PHP обновляет время модификации этого файла. Таким образом, в каждом следующем запросе PHP, путем разницы между текущим временем и временем последней модификации файла сессии, может определить, является ли сессия активной, или ее время жизни уже истекло. (Механизм удаления старых файлов сессий более подробно рассматривается в следующем разделе).

Примечание: Здесь следует отметить, что параметр session.gc_maxlifetime действует на все сессии в пределах одного сервера (точнее, в пределах одного главного процесса PHP). На практике это значит, что если на сервере работает несколько сайтов, и каждый из них имеет собственный таймаут отсутствия активности пользователей, то установка этого параметра на одном из сайтов приведет к его установке и для других сайтов. То же касается и shared-хостинга. Для избежания подобной ситуации используются отдельные каталоги сессий для каждого сайта в пределах одного сервера. Установка пути к каталогу сессий производится с помощью параметра session.save_path в файле настроек php.ini, или путем вызова функции ini_set(). После этого сессии каждого сайта будут храниться в отдельных каталогах, и параметр session.gc_maxlifetime, установленный на одном из сайтов, будет действовать только на его сессии. Мы не станем рассматривать этот случай подробно, тем более, что у нас в запасе есть более гибкий вариант контроля отсутствия активности пользователя.

Контроль отсутствия активности пользователя с помощью сессионных переменных

Казалось бы, предыдущий вариант при всей своей простоте (всего пару дополнительных строк кода) дает все, что нам нужно. Но что, если не каждый запрос можно расценивать как результат активности пользователя? Например, на странице установлен таймер, который периодически выполняет AJAX-запрос на получение обновлений от сервера. Такой запрос нельзя расценивать как активность пользователя, а значит автоматическое продление времени жизни сессии является не корректным в данном случае. Но мы знаем, что PHP обновляет время модификации файла сессии автоматически при каждом вызове функции session_start(), а значит любой запрос приведет к продлению времени жизни сессии, и таймаут отсутствия активности пользователя не наступит никогда. К тому же, последнее примечание из предыдущего раздела о тонкостях работы параметра session.gc_maxlifetime может показаться кому-то слишком запутанным и сложным в реализации.

Для решения этой проблемы откажемся от использования встроенных механизмов PHP и введем несколько новых сессионных переменных, которые позволят нам контролировать время отсутствия активности пользователей самостоятельно.

Function startSession($isUserActivity=true) { $sessionLifetime = 300; if (session_id()) return true; // Устанавливаем время жизни куки до закрытия браузера (контролировать все будем на стороне сервера) ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; $t = time(); if ($sessionLifetime) { // Если таймаут отсутствия активности пользователя задан, // проверяем время, прошедшее с момента последней активности пользователя // (время последнего запроса, когда была обновлена сессионная переменная lastactivity) if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) { // Если время, прошедшее с момента последней активности пользователя, // больше таймаута отсутствия активности, значит сессия истекла, и нужно завершить сеанс destroySession(); return false; } else { // Если таймаут еще не наступил, // и если запрос пришел как результат активности пользователя, // обновляем переменную lastactivity значением текущего времени, // продлевая тем самым время сеанса еще на sessionLifetime секунд if ($isUserActivity) $_SESSION["lastactivity"] = $t; } } return true; }

Подытожим. В каждом запросе мы проверяем, не достигнут ли таймаут с момента последней активности пользователя до текущего момента, и если он достигнут - уничтожаем сессию и прерываем выполнение функции, возвращая FALSE. Если же таймаут не достигнут, и в функцию передан параметр $isUserActivity со значением TRUE - обновляем время последней активности пользователя. Все, что нам остается сделать - это определять в вызывающем скрипте, является ли запрос результатом активности пользователя, и если нет - вызывать функцию startSession со значением параметра $isUserActivity, равным FALSE.

Дополнение от 2013-06-07
Обработка результата функции sessionStart()

В комментариях обратили внимание на то, что возврат FALSE не дает полного понимания причины ошибки, и это абсолютно справедливо. Я не стал публиковать здесь подробную обработку ошибок (объем статьи и так не маленький), поскольку это не относится напрямую к теме статьи. Но учитывая комментарии, внесу ясность.

Как видно, функция sessionStart может вернуть FALSE в двух случаях. Либо сессию не удалось запустить из-за каких-то внутренних ошибок сервера (например, неправильные настройки сессий в php.ini), либо время жизни сессии истекло. В первом случае мы должны перебросить пользователя на страницу с ошибкой о том, что есть проблемы на сервере, и формой обращения в службу поддержки. Во втором случае мы должны перевести пользователя на форму входа и вывести в ней соответствующее сообщение о том, что время сессии истекло. Для этого нам необходимо ввести коды ошибок и возвращать вместо FALSE соответствующий код, а в вызывающем методе проверять его и действовать соответствующим образом.

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

Примечание: А что произойдет, если браузер был закрыт, и куки с именем сессии был автоматически уничтожен? Запрос к серверу при следующем открытии браузера не будет содержать куки сессии, и сервер не сможет открыть сессию и проверить таймаут отсутствия активности пользователя. Для нас это равносильно созданию новой сессии и никак не влияет на функционал и безопасность. Но возникает справедливый вопрос - а кто же тогда уничтожит старую сессию, если до сих пор ее уничтожали мы по истечении таймаута? Или она теперь будет висеть в каталоге сессий вечно? Для очистки старых сессий в PHP существует механизм под названием garbage collection. Он запускается в момент очередного запроса к серверу и чистит все старые сессии на основании даты последнего изменения файлов сессий. Но запуск механизма garbage collection происходит не при каждом запросе к серверу. Частота (а точнее, вероятность) запуска определяется двумя параметрами настроек session.gc_probability и session.gc_divisor. Результат от деления первого параметра на второй и есть вероятностью запуска механизма garbage collection. Таким образом, для того, чтобы механизм очистки сессий запускался при каждом запросе к севреру, эти параметры нужно установить в равные значения, например «1». Такой подход гарантирует чистоту каталога сессий, но, очевидно, является слишком накладным для сервера. Поэтому в production-системах по умолчанию устанавливается значение session.gc_divisor, равное 1000, что означает, что механизм garbage collection будет запускаться с вероятностью 1/1000. Если вы поэкспериментируете с этими настройками в своем файле php.ini, то сможете заметить, что в описанном выше случае, когда браузер закрывается и очищает все свои куки, в каталоге сессий какое-то время все еще остаются старые сессии. Но это не должно вас волновать, т.к. как уже было сказано, это ни коим образом не влияет на безопасность нашего механизма.

Дополнение от 2013-06-07

Предотвращение зависания скриптов из-за блокировки файла сессии

В комментариях подняли вопрос о зависании одновременно выполняющихся скриптов из-за блокировки файла сессии (как самый яркий вариант - long poll).

Для начала отмечу, что эта проблема напрямую не зависит от загруженности сервера или количества пользователей. Конечно, чем больше запросов, тем медленнее выполняются скрипты. Но это коссвенная зависимость. Проблема появляется только в пределах одной сессии, когда серверу приходит несколько запросов от имени одного пользователя (например, один из них long poll, а остальные - обычные запросы). Каждый запрос пытается получить доступ к одному и тому же файлу сессии, и если предыдущий запрос не разблокировал файл, то последующий будет висеть в ожидании.

Для сведения блокировки файлов сессий к минимуму настоятельно рекомендуется закрывать сессию путем вызова функции session_write_close() сразу после того, как выполнены все действия с сессионными переменными. На практике это означает, что не следует хранить в сессионных переменных все подряд и обращаться к ним на всем протяжении выполнения скрипта. А если и надо хранить в сессионных переменных какие-то рабочие данные, то считывать их сразу при старте сессии, сохранять в локальные переменные для последующего использования и закрывать сессию (имеется ввиду закрытие сессии с помощью функции session_write_close, а не уничтожение с помощью session_destroy).

В нашем примере это означает, что сразу после открытия сессии, проверки времени ее жизни и существования авторизованного пользователя, мы должны считать и сохранить все дополнительные необходимые приложению сессионные переменные (если такие существуют), после чего закрыть сессию с помощью вызова session_write_close() и продолжить выполнение скрипта, будь то long poll или обычный запрос.

Защита сессий от несанкционированного использования

Представим себе ситуацию. Один из ваших пользователей цепляет троян, который грабит куки браузера (в котором хранится наша сессия) и отправляет его на указанный email. Злоумышленник получает куки и использует его для подделки запроса от имени нашего авторизованного пользователя. Сервер успешно принимает и обрабатывает этот запрос, как если бы он пришел от авторизованного пользователя. Если не реализована дополнительная проверка IP-адреса, такая атака приведет к успешному взлому аккаунта пользователя со всеми вытекающими последствиями.

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

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

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

(Опустим ту часть кода, которая уже рассмотрена).

Function startSession($isUserActivity=true) { // Время жизни идентификатора сессии $idLifetime = 60; ... if ($idLifetime) { // Если время жизни идентификатора сессии задано, // проверяем время, прошедшее с момента создания сессии или последней регенерации // (время последнего запроса, когда была обновлена сессионная переменная starttime) if (isset($_SESSION["starttime"])) { if ($t-$_SESSION["starttime"] >= $idLifetime) { // Время жизни идентификатора сессии истекло // Генерируем новый идентификатор session_regenerate_id(true); $_SESSION["starttime"] = $t; } } else { // Сюда мы попадаем, если сессия только что создана // Устанавливаем время генерации идентификатора сессии в текущее время $_SESSION["starttime"] = $t; } } return true; }

Итак, при создании новой сессии (которое происходит в момент успешного входа пользователя), мы устанавливаем сессионную переменную starttime, хранящую для нас время последней генерации идентификатора сессии, в значение, равное текущему времени сервера. Далее в каждом запросе мы проверяем, не прошло ли достаточно времени (idLifetime) с момента последней генерации идентификатора, и если прошло - генерируем новый. Таким образом, если в течение установленного времени жизни идентификатора злоумышленник, получивший куки авторизованного пользователя, не успеет им воспользоваться, поддельный запрос будет расценен сервером как неавторизованный, и злоумышленник попадет на страницу входа.

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

Если мы хотим максимально обезопасить наши сессии, достаточно установить время жизни идентификатора в единицу или же вообще вынести функцию session_regenerate_id() за скобки и убрать все проверки, что приведет к регенерации идентификатора в каждом запросе. (Я не проверял влияние такого подхода на быстродействие, и могу только сказать, что функция session_regenerate_id(true) выполняет по сути всего 4 действия: генерация нового идентификатора, создание заголовка с куки сессии, удаление старого и создание нового файла сессии).

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

Возможность одновременной работы в одном браузере от имени нескольких пользователей

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

В наших предыдущих примерах мы не задавали явно имя сессии, поэтому использовалось имя, установленное в PHP по умолчанию (PHPSESSID). Это значит, что все сессии, которые создавались нами до сих пор, отправляли браузеру куки под именем PHPSESSID. Очевидно, что если имя куки всегда одинаковое, то нет возможности в пределах одного браузера организовать две сессии с одинаковым именем. Но если бы мы для каждого пользователя использовали собственное имя сессии, то проблема была бы решена. Так и сделаем.

Function startSession($isUserActivity=true, $prefix=null) { ... if (session_id()) return true; // Если в параметрах передан префикс пользователя, // устанавливаем уникальное имя сессии, включающее этот префикс, // иначе устанавливаем общее для всех пользователей имя (например, MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; ... }

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

Заключение

В заключение приведу полный конечный код наших функций для работы с сессиями PHP, включающий все рассмотренные выше задачи.

Function startSession($isUserActivity=true, $prefix=null) { $sessionLifetime = 300; $idLifetime = 60; if (session_id()) return true; session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); if (! session_start()) return false; $t = time(); if ($sessionLifetime) { if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) { destroySession(); return false; } else { if ($isUserActivity) $_SESSION["lastactivity"] = $t; } } if ($idLifetime) { if (isset($_SESSION["starttime"])) { if ($t-$_SESSION["starttime"] >= $idLifetime) { session_regenerate_id(true); $_SESSION["starttime"] = $t; } } else { $_SESSION["starttime"] = $t; } } return true; } function destroySession() { if (session_id()) { session_unset(); setcookie(session_name(), session_id(), time()-60*60*24); session_destroy(); } }

Надеюсь, эта статья сэкономит немного времени тем, кто никогда особо не углублялся в механизм сессий, и даст достаточно понимания этого механизма тем, кто только начинает знакомиться с PHP.

Технология сессии в PHP являются простым способом хранения информации для отдельно взятого пользователя сайта. Например: товары, добавленные в корзину магазина, настройки уведомлений и т.д. При первом запросе сайта к серверу, в браузере сохраняется в cookies уникальный идентификатор сессии пользователя. Затем идентификатор или связка идентификатора с IP адресом идентифицирует пользователя. Это может использоваться для сохранения состояния между запросами страниц. Идентификаторы сессий обычно отправляются браузеру через сессионный Cookie и используются для получения имеющихся данных сессии.

Сессии используют простую технологию: PHP будет либо получать данные существующей сессии, используя переданный идентификатор (обычно из сессионного cookie ), или, если ничего не передавалось, будет создана новая сессия. PHP заполнит суперглобальную переменную $_SESSION сессионной информацией после того, как будет запущена сессия. Когда PHP завершает работу, он автоматически сериализует содержимое суперглобальной переменной $_SESSION и отправляет для сохранения, используя сессионный обработчик для записи сессии.

По умолчанию PHP использует внутренний обработчик files для сохранения сессий, который установлен в INI-переменной session.save_handler . Этот обработчик сохраняет данные на сервере в директории, указанной в конфигурационной директиве session.save_path .

Самый простой пример использования сессии, например, вывод количества обращений к странице для каждого пользователя:

Каким способом возможна блокировка сессий

На сайте php в секции описания работы сессий есть примечание (http://php.net/manual/ru/session.examples.basic.php):

Сессии, использующие файлы (по умолчанию в PHP ), блокируют файл сессии сразу при открытии сессии функцией session_start() или косвенно при указании session.auto_start . После блокировки, ни один другой скрипт не может получить доступ к этому же файлу сессии, пока он не будет закрыт или при завершении скрипта или при вызове функции session_write_close() .

Скорее всего это станет проблемой для сайтов, которые активно используют AJAX и делают несколько одновременных запросов. Простейшим путем решить эту проблему будет вызов функции session_write_close() сразу же как только все требуемые изменения в сессии будут сделаны, предпочтительно ближе к началу работы скрипта. Также можно использовать другой механизм сессии, который поддерживает конкурентный доступ.

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

Самый простой пример блокировки сессий:

// только для одного скрипта ?>

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

Какие варианты решения данной проблемы существуют

Для хранения сессий можно использовать БД, такие как MySQL или PostgreSQL (что не совсем правильно, учитывая возможности большинства БД и возможную скорость работы в данной задаче), Memcached (не гарантирует хранение сессии, возможно ее удаление) и Redis , который мы считаем оптимальным хранилищем . По скорости он не уступает Memcached, но при этом может гарантировать сохранность данных.

И самое главное приемущество Redis - при хранении в нем сессий они не блокируются.

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

Дополнительные материалы

Разделы

SSH

FTP

Веб-приложения

  • Общие сведения по установке приложений (виртуальное окружение Docker)

Диагностика проблем

Домены

  • Аннулирование доменного имени в зоне.RU/.РФ для которого Beget является регистратором
  • Передача права администрирования доменного имени.RU/.РФ/.SU и международных зонах (Смена администратора домена)

аккаунт Yandex.Direct [для Yandex.Direct] был отключен из списка активных по причине достижения лимита количества капчей

Ошибка означает, что в целях обеспечения безопасности для аккаунта он был автоматически исключен из очереди на использование из того, что на нем попалось слишком много капчи. Максимальный лимит капчи для каждого аккаунта вы можете задать в . При желании вы можете увеличить этот лимит, например, до значения 50 или 90. Также в случае работы через несколько аккаутов для того, чтобы процесс сбора не останавливался раньше времени, на этой же вкладке настроек выберите 2й из 3х доступных режимов деактивации потоков.

Используемый вами прокси-сервер вернул код ошибки 407, которая однозначно говорит о том, что при попытке обращения к нему были переданы неверная пара логин/пароль или же прокси-сервер имеет защиту доступа по IP-адресу, и ваш текущий IP-адрес не соответствует настроенному на стороне прокси-сервера.

Пожалуйста, проверьте логин и пароль для прокси-сервера в "Настройках - Сеть" и в "Настройках - Парсинг - Yandex.Direct" (в случае, если вы указывали прокси-сервер вместе с аккаунтом вручную). После этого в "Настройках - Парсинг - Yandex.Direct" нажмите кнопку и не забудьте нажать кнопку "Сохранить изменения" в нижней части окна настроек.

фраза "XXX" не может быть отправлена на анализ в Yandex.Direct, т.к. она содержит запрещенные символы
фраза "XXX" не может быть отправлена на анализ в Yandex.Direct, т.к. в ней нарушен синтаксис минус-слов
фраза "XXX" не может быть отправлена на анализ в Yandex.Direct, т.к. она полностью состоит из стоп-слов
фраза "XXX" не может быть отправлена на анализ в Yandex.Direct, т.к. она содержит запрещенные или неправильно использованные символы
фраза "XXX" не может быть отправлена на анализ в Yandex.Direct, т.к. она содержит отдельные слова длиной больше 35 символов
фраза "XXX" не может быть отправлена на анализ в Yandex.Direct, т.к. она состоит более чем из 7 слов

Yandex.Direct может отвергать запрос, если в нем содержались недопустимые символы, если он был слишком длинным или в нем неверно были использованы операторы (!, ", +, -, ).

В отличие от Yandex.Wordstat в Yandex.Direct действует дополнительное ограничение на длину проверяемых фраз (не более 7 слов). Если вам необходима статистика по частоте для таких фраз, вы можете собрать частоты через классический метод Yandex.Wordstat (кнопка с иконкой лупы на панели задач).

после удачной загрузки страницы Яндекс.Директ не были обнаружены признаки валидности страницы

Если этой ошибке предшествуют сетевые ошибки или ошибки прокси-серверов, то в первую очередь необходимо проверить их качество. Для этого нужно остановить сбор, выключить использование прокси-серверов, в "Настройках - Парсинг - Yandex.Direct" нажать кнопку "Принудительно очистить данные об авторизации" (если вы работали в многопоточном режиме, то уменьшите кол-во потоков до 1, т.к. прокси-серверы были только что отключены).

Если сетевых ошибок до этого не было, то попробуйте остановить сбор и в "Настройках - Парсинг - Yandex.Direct" нажать кнопку "Принудительно очистить данные об авторизации" .

Также ошибка может возникать в случае изменения верстки на Yandex.Direct. В таких случаях вам нужно обновить программу до актуальной версии как только это будет возможно (обычно обновления против таких ошибок выходят в течение нескольких часов после обнаружения).

при сборе статистики Yandex.Direct ни для одной из фраз в отправленной на обработку пачке не была снята статистика

При работе с Yandex.Direct для достижения максимальной скорости сбора программа отправляет фразы на анализ пакетно (по несколько штук за раз).

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

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

В обоих случаях фразы попадают в конец очереди на анализ и будут проверены позже.

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

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

при обработке запроса (XXX) Yandex.Wordstat вернул ошибку: "Неверно задан запрос"

Yandex.Wordstat может отвергать запрос, если в нем содержались недопустимые символы, если он был слишком длинным или в нем неверно были использованы операторы (!, ", +, -, ).

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

поисковый запрос слишком длинный для Yandex.Wordstat и будет пропущен: "{0}"

Yandex.Wordstat может отвергать запрос, если он был слишком длинным.

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

ожидание ответа от Yandex.Wordstat при первичном обращении

Непосредственно после авторизации в аккаунте 1 раз за сеанс программа выполняет ожидание в соответствии с выставленными в задержками. Сообщение об окончании ожидания в журнале событий не предусмотрено: программа просто приступает к сбору статистики.

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

невозможность установить подключение для помощника при работе с Google.Adwords

Ошибка может возникать при проблемах при запуске или подключении к вспомогательному процессу сбора статистики.

Для запуска процесса необходимо установить компонент Microsoft Visual C++ для Visual Studio 2015, 2017 и 2019 . Скачать его можно по ссылке: vc_redist.x86.exe

Если установка компонента не помогла, попробуйте выполнить следующие инструкции:

  1. В компонентах Windows нужно отметить и установить все компоненты из раздела WCF-сервисы. Для этого нужно открыть Панель управления в Windows, ввести в поиск "Компоненты" , выбрать инструмент включения/отключения компонентов , который там будет найден.

    Уже в окне открывшегося инструмента нужно найти раздел .NET Framework (4.7) -> "WCF-сервисы" и отметить (включить) все компоненты. То же самое нужно сделать для пунктов из раздела .NET Framework (3.5) .

    Если ошибка сохранится, перейдите к шагу 2.

  2. Проверьте настройки безопасности антивируса и прочего защитного ПО. Также можно попробовать переименовать папку с программой (по умолчанию она устанавливалась в "Мои документы/Key Collector"). Если ошибка сохранится, перейдите к шагу 3.
  3. В в панели включите опцию "Использовать режим отладки" . Если ошибка сохранится, перейдите к шагу 4.
  4. В "Настройках - Парсинг - Google.Adwords" в панели "Дополнительные системные настройки" включите опцию , не меняя настройки портов.
  5. Если ошибка сохранится, выключите опцию "Использовать альтернативное подключение" в "Настройках - Парсинг - Google.Adwords" в панели "Дополнительные системные настройки" .
  6. Если ошибка сохранится, попробуйте запустить KeyCollectorWPF.exe от имени администратора (это можно сделать через контекстное меню).

фраза "XXX" была пропущена при сборе статистики Google.Adwords

Google.Adwords предъявляет свои требования к формату вхожных фраз для анализа. Так, например, невозможно получить статистику для слишком длинных фраз или фраз, содержащих посторонние спец. символы (любые символы кроме букв, цифр и пробелов). Программа автоматически пропускает такие фразы, чтобы не задержить процесс сбора для обработки заведомо недопустимых фраз.

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

при съеме слов с Google.Adwords для фразы "XXX" не было найдено ни одного результата

Как правило, данная ошибка встречается в двух случаях.

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

2. Аккаунт Adwords не настроен или не готов к работе.

В этом случае сперва необходимо проверить, работает ли съем статистики через браузер? Для этого нужно запустить браузер, зайти на https://adwords.google.ru

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

обнаружено подозрительно долгое бездействие в процессе сбора статистики Google.Adwords
обнаружено подозрительно долгое бездействие в процессе входа в аккаунт Google.Adwords

  1. Зайдите в Google.Adwords через браузер
  2. Авторизуйтесь под аккаунтом, который Вы используете при парсинге в программе

В случае, если данная ошибка продолжает появляться, необходимо выполнить проверку работы аккаунта. Работает ли съем статистики через браузер? Для этого нужно запустить браузер, зайти на https://adwords.google.com и попробовать снять статистику для любой фразы вручную. Затем нужно попробовать выгрузить результаты в CSV.

Также сразу заметим, что для корректной работы программы язык интерфейса в аккаунте должен быть строго русским (к региону сбора это отношения не имеет). Кроме того, аккаунт должен быть готов к работе. Это значит, что на нем должна быть хотя бы 1 активная или деактивированная кампания , иначе Adwords даже и не позволит вам попасть в раздел "Планировщика ключевых слов", которым вы хотите воспользоваться через интерфейс программы.

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

Попробуйте в "Настройках - Парсинг - Google.Adwords" в панели "Дополнительные настройки" нажать кнопку "Очистить кеш" и включить режим отладки.

О результах диагностики необходимо сообщить в службу технической поддержки.

в результате диагностики было выяснено, что процесс сбора статистики Google.Adwords требует попытки переавторизации в аккаунте

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

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

аккаунт требует верификации

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

  1. Откройте любой браузер в режиме инкогнито (с чистым кэшем) и зайдите на Google.Adwords .
  2. Выполните вход в аккаунт, который прописан в "Настройках - Парсинг - Google.Adwords" .
  3. При запросе подтверждения входа в аккаунт выберите удобный для Вас метод верификации и завершите предложенную процедуру.
  4. Запустите сбор статистики в программе

Если ни один из предложенных методов не решил проблему, то свяжитесь со службой технической поддержки через тикеты.

требуется уточнить ID подаккаунта

Ошибка возникает в случае, когда в используемом Вами для сбора статистике аккаунте Google.Adwords есть подключенные к нему подаккаунты.

Вы можете либо отключить от используемого аккаунта все связанные с ним подаккаунты, либо указать в "Настройках - Парсинг - Google.Adwords" цифровой идентификатор нужного Вам подаккаунта в формате: ЛогинАккаунта#XXX-XXX-XXXX:ПарольАккаунта , где XXX-XXX-XXXX - идентификатор нужного подаккаунта.

Если ни один из предложенных методов не решил проблему, то свяжитесь со службой технической поддержки через тикеты.

ошибка выбора подаккаунта

Ошибка возникает в случае, когда программе не удается выбрать указанный Вами в "Настройках - Парсинг - Google.Adwords" подаккаунт.

Проверьте корректность ввода логина и пароля основного аккаунта, а также цифровой идентификатор нужного Вам подаккаунта.

Если ни один из предложенных методов не решил проблему, то свяжитесь со службой технической поддержки через тикеты.

ошибка ввода аккаунта

"Настройках - Парсинг - Google.Adwords" аккаунт.

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

Если ни один из предложенных методов не решил проблему, то свяжитесь со службой технической поддержки через тикеты.

ошибка ввода пароля

Ошибка возникает в случае, когда Google.Adwords не принимает указанный Вами в "Настройках - Парсинг - Google.Adwords" пароль.

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

Если ни один из предложенных методов не решил проблему, то свяжитесь со службой технической поддержки через тикеты.

возникла ошибка в методе потока
что-то пошло не так. Ошибка в методе

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

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

В случае, если ошибка не пропадет даже после восстановления проекта, пожалуйста, сообщите о ней в службу технической поддержки.

не удалось загрузить файл изображения капчи

Данная ошибка может возникать у некоторых пользователей устаревших версий ОС Windows XP и др. в момент загрузки изображения капчи.

При возникновении ошибки, пожалуйста, попробуйте включить опцию "Принудительно использовать SSL 3 при загрузке файлов" в "Настройках - Сеть" .

Если ошибки не возникает, то опцию включать не следует.

ошибка NetworkMethods.LoadPage
ошибка NetworkMethods.PostDataAndLoadPage

В зависимости от второй части данного сообщения об ошибке могут быть разные причины данной ошибки. Наиболее часто встречаются ошибки вида "Время ожидания истекло", "Невозможно соедиться с прокси-серверов", "Соединение было принудительно сброшено" и "Невозможно разрешить удаленное имя".

При возникновении ошибки вида "Время ожидания истекло", пожалуйста, проверьте значение параметра "Таймаут ожидания ответа от сервера" в "Настройках - Парсинг - Общие" и в "Настройках - Парсинг - Yandex.Wordstat" (по умолчанию там стояли значения 30 000 мс). Если вы работаете через прокси-серверы, то дело может быть еще и в них. Также возможны ситуации, когда причиной является плохое качество связи или временные неполадки на стороне самого сервиса, с которого вы собираете статистику.

Причину остальных видов ошибок, прежде всего, следует искать либо в защитном ПО на ПК (фаеволы, антивирусы и пр.), либо в некачественных мертвых прокси-серверах (если вы их используете). Ошибки могут возникать также и при некачественном соединении с интернетом.

в данный момент все работники заняты. Переходим в режим ожидания

Данную ошибку возвращает используемый вами сервис автораспознавания капчи (который включается или отключается в "Настройках - Антикапча" ).

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

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

неверный ключ доступа к сервису. Проверьте правильность настроек доступа к сервису

Ошибка возникает, если в "Настройках - Антикапча" вы включили обработку капчи через один из сервисов автораспознавания, но при этом прописали неправильных ключ доступа.

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

капча еще не распознана. Выполняется повторный запрос результата

Данное сообщение носит исключительно информационный характер и не является ошибкой.

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

Данное сообщение можно встретить, если на странице ответа сервиса была обнаружена капча вида ReCaptcha, и если сама страница была загружена через один из прописанных в настройках программы прокси-сервер.

Капча ReCaptcha отличается повышенной сложностью обхода, и поддерживается сервисами автоматического распознавания Anti-Captcha.com и RuCaptcha.com .

Для более "естественного" распознавания желательно обеспечить данный сервис возможностью обработать Вашу капчу через тот же IP-адрес, через который работали Вы сами. Если этого не сделать, то возрастают риски блокировок и санкций (это лишь догадки, прямых достоверных сведений об алгоритмах наложения санкций ни у кого нет и никогда не будет).

Если Вы приняли решение обеспечить сервис автоматического распознавания доступом к Вашим прокси-серверам, то Вы можете включить соответствующую опцию "Разрешить передачу информации о прокси-серверах в сервисы автораспознавания" в "Настройках - Антикапча" (по умолчанию опция выключена).

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

Будем учиться делать простую аутентификацию пользователей на сайте. На сайте могут быть страницы только для авторизованных пользователей и они будут полноценно функционировать, если добавить к ним наш блок аутентификации. Чтобы его создать, нужна база данных MySQL. Она может иметь 5 колонок (минимум), а может и больше, если вы хотите добавить информацию о пользователях. Назовём базу данных “Userauth”.

Создадим в ней следующие поля: ID для подсчёта числа пользователей, UID для уникального идентификационного номера пользователя, Username для имени пользователя, Email для адреса его электронной почты и Password для пароля. Вы можете использовать для авторизации пользователя и уже имеющуюся у Вас базу данных, только, как и в случае с новой базой данных, создайте в ней следующую таблицу.

Код MySQL

CREATE TABLE `users` (`ID` int (11) NOT NULL AUTO_INCREMENT, `UID` int (11) NOT NULL, `Username` text NOT NULL, `Email` text NOT NULL, `Password` text NOT NULL, PRIMARY KEY (`ID`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Теперь создадим файл "sql.php". Он отвечает за подключение к базе данных. Данный код, во первых, создаёт переменные для сервера и пользователя, когда он подключается к серверу. Во-вторых, он выберет базу данных, в данном случае "USERAUTH". Этот файл нужно подключить в "log.php" и "reg.php" для доступа к базе данных.

Код PHP

//Ваше имя пользователя MySQL $pass = "redere"; //пароль $conn = mysql_connect ($server, $user, $pass);//соединение с сервером $db = mysql_select_db ("userauth", $conn);//выбор базы данных if (!$db) { //если не может выбрать базу данных echo "Извините, ошибка:(/>";//Показывает сообщение об ошибке exit (); //Позволяет работать остальным скриптам PHP } ?>

Далее страница входа, пусть она называется "login.php". Во-первых, она проверяет введённые данные на наличие ошибок. Страница имеет поля для имени пользователя, пароля, кнопку отправки и ссылку для регистрации. Когда пользователь нажмёт кнопку «Вход», форма будет обработана кодом из файла "log.php", а затем произойдёт вход в систему.

Код PHP

0) { //если есть ошибки сессии $err = "

"; //Start a table foreach ($_SESSION["ERRMSG"] as $msg) {//распознавание каждой ошибки $err .= ""; //запись её в переменную } $err .= "
" . $msg . "
"; //закрытие таблицы unset ($_SESSION["ERRMSG"]); //удаление сессии } ?> Форма входа
Имя пользователя
Пароль
Регистрация

Затем пишем скрипт для входа в систему. Назовём его "log.php". Он имеет функцию для очистки входных данных от SQL-инъекций, которые могут испортить ваш скрипт. Во-вторых, он получает данные формы и проверяет их на правильность. Если входные данные правильны, скрипт отправляет пользователя на страницу авторизованных пользователей, если нет – устанавливает ошибки и отправляет пользователя на страницу входа.

Код PHP

//начало сессии для записи function Fix($str) { //очистка полей $str = trim($str); if (get_magic_quotes_gpc()) { $str = stripslashes ($str); } //массив для сохранения ошибок $errflag = false ; //флаг ошибки $username = Fix($_POST["username"]);//имя пользователя $password = Fix($_POST["password"]);//пароль } //проверка пароля if ($password == "") { $errmsg = "Password missing"; //ошибка $errflag = true ; //поднимает флаг в случае ошибки } //если флаг ошибки поднят, направляет обратно к форме регистрации //записывает ошибки session_write_close(); //закрытие сессии //перенаправление exit (); } //запрос к базе данных $qry = "SELECT * FROM `users` WHERE `Username` = "$username" AND `Password` = "" . md5 ($password) . """; $result = mysql_query ($qry); //проверка, был ли запрос успешным (есть ли данные по нему) if (mysql_num_rows ($result) == 1) { while ($row = mysql_fetch_assoc ($result)) { $_SESSION["UID"] = $row["UID"];//получение UID из базы данных и помещение его в сессию $_SESSION["USERNAME"] = $username;//устанавливает, совпадает ли имя пользователя с сессионным session_write_close(); //закрытие сессии header("location: member.php");//перенаправление } } else { $_SESSION["ERRMSG"] = "Invalid username or password"; //ошибка session_write_close(); //закрытие сессии header("location: login.php"); //перенаправление exit (); } ?>

Сделаем страницу регистрации, назовём её "register.php". Она похожа на страницу входа, только имеет на несколько полей больше, а вместо ссылки на регистрацию – ссылку на login.php на случай, если у пользователя уже есть аккаунт.

Код PHP

0) { //если есть ошибки сессии $err = "

"; //начало таблицы foreach ($_SESSION["ERRMSG"] as $msg) {//устанавливает каждую ошибку $err .= ""; //записывает их в переменную } $err .= "
" . $msg . "
"; //конец таблицы unset ($_SESSION["ERRMSG"]); //уничтожает сессию } ?> Форма регистрации
Имя пользователя
E-mail
Пароль
Повтор пароля
У меня есть аккаунт

Теперь сделаем скрипт регистрации в файле "reg.php". В него будет включён "sql.php" для подключения к к базе данных. Используется и та же функция, что и в скрипте входа для очистки поля ввода. Устанавливаются переменные для возможных ошибок. Далее – функция для создания уникального идентификатора, который никогда ранее не предоставлялся. Затем извлекаются данные из формы регистрации и проверяются. Происходит проверка, что адрес электронной почты указан в нужном формате, а также, правильно ли повторно указан пароль. Затем скрипт проверяет, нет ли в базе данных пользователя с таким же именем, и, если есть, сообщает об ошибке. И, наконец, код добавляет пользователя в базу данных.

Код PHP

//начало сессии для записи function Fix($str) { //очистка полей $str = @trim($str); if (get_magic_quotes_gpc()) { $str = stripslashes ($str); } return mysql_real_escape_string ($str); } $errmsg = array (); //массив для хранения ошибок $errflag = false ; //флаг ошибки $UID = "12323543534523453451465685454";//уникальный ID $username = Fix($_POST["username"]);//имя пользователя $email = $_POST["email"]; //Email $password = Fix($_POST["password"]);//пароль $rpassword = Fix($_POST["rpassword"]);//повтор пароля //проверка имени пользователя if ($username == "") { $errmsg = "Username missing"; //ошибка $errflag = true ; //поднимает флаг в случае ошибки } //проверка Email if(!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@+(\.+)*(\.{2,3})$", $email)) { //должен соответствовать формату: [email protected] $errmsg = "Invalid Email"; //ошибка $errflag = true ; //поднимает флаг в случае ошибки } //проверка пароля if ($password == "") { $errmsg = "Password missing"; //ошибка $errflag = true ; //поднимает флаг в случае ошибки } //проверка повтора пароля if ($rpassword == "") { $errmsg = "Repeated password missing";//ошибка $errflag = true ; //поднимает флаг в случае ошибки } //проверка валидности пароля if (strcmp($password, $rpassword) != 0) { $errmsg = "Passwords do not match";//ошибка $errflag = true ; //поднимает флаг в случае ошибки } //проверка, свободно ли имя пользователя if ($username != "") { $qry = "SELECT * FROM `users` WHERE `Username` = "$username""; //запрос к MySQL $result = mysql_query ($qry); if ($result) { if (mysql_num_rows ($result) > 0) {//если имя уже используется $errmsg = "Username already in use"; //сообщение об ошибке $errflag = true; //поднимает флаг в случае ошибки } mysql_free_result ($result); } } //если данные не прошли валидацию, направляет обратно к форме регистрации if ($errflag) { $_SESSION["ERRMSG"] = $errmsg; //сообщение об ошибке session_write_close(); //закрытие сессии header("location: register.php");//перенаправление exit (); } //добавление данных в базу $qry = "INSERT INTO `userauth`.`users`(`UID`, `Username`, `Email`, `Password`) VALUES("$UID","$username","$email","" . md5 ($password) . "")"; $result = mysql_query ($qry); //проверка, был ли успешным запрос на добавление if ($result) { echo "Благодарим Вас за регистрацию, " .$username . ". Пожалуйста, входите сюда"; exit (); } else { die ("Ошибка, обратитесь позже"); } ?>

Ещё нужно сделать скрипт для выхода пользователя из системы. Он прекращает сессию для пользователя с данным уникальным идентификатором и именем, а затем перенаправляет пользователя на страницу входа в систему.

Код PHP

И, наконец, скрипт "auth.php" можно использовать, чтобы сделать страницы доступными только для авторизованных пользователей. Он проверяет данные входа и, если они верны, позволяет пользователю просматривать страницы, а если нет, просит авторизоваться. Кроме того, если кто-то попытается взломать сайт создав одну из сессий, она будет прервана, как в общем случае.

Код PHP

Одно из условий в коде выше является предметом вопроса в .

Следующий код нужно вставить на страницу для авторизованных пользователей, она называется, например, "member.php", а у Вас может называться как угодно.

Код PHP

Вам разрешён доступ к этой странице. Выйти ( )

Аутентификация пользователей готова!

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

Начнем с добавления полей в таблицу « users » . Нам нужно поле для хранения e-mail адреса, поле для состояния пользователя (0 - неактивирован, 1 - активирован) и поле с датой регистрации.





Затем нужно подправить save_user.php , добавив проверку на корректность e-mail адреса и отправку письма для подтверждения регистрации. В письме находится ссылка с двумя переменными, передающимися методом get: логин и сгенерированный, уникальный для каждого пользователя, код. Код нам нужен, чтобы пользователь не смог активировать свой аккаунт без письма, а это даст нам уверенность, что введенный адрес почты, действительно, принадлежит ему. Добавим следующий код, после извлечения из глобальных переменных отправленных данных:

If (isset($_POST["email"])) { $email = $_POST["email"]; if ($email == "") { unset($email);} }
if (empty($login) or empty($password)or empty($code) or empty($email))
//добавим переменную с e - mail адресом
//если пользователь не ввел логин или пароль, то выдаем ошибку и останавливаем скрипт
{
exit ("Вы ввели не всю информацию, вернитесь назад и заполните все поля!"); //останавливаем выполнение сценариев
}
if (!preg_match("/+@+\.{2,3}/i", $email)) //проверка е-mail адреса регулярными выражениями на корректность
{exit ("Неверно введен е-mail!");}

В запросе добавления нового пользователя необходимо дописать и дату регистрации. Если в базу удачно вставлены данные и пользователь зарегистрирован, то нужно отправить сообщение на введеный адрес:

// если такого нет, то сохраняем данные
$result2 = mysql_query ("INSERT INTO users (login,password,avatar,email,date) VALUES("$login","$password","$avatar","$email",NOW())");
// Проверяем, есть ли ошибки
if ($result2=="TRUE")
{
$result3 = mysql_query ("SELECT id FROM users WHERE login="$login"",$db);//извлекаем идентификатор пользователя. Благодаря ему у нас и будет уникальный код активации, ведь двух одинаковых идентификаторов быть не может.
$myrow3 = mysql_fetch_array($result3);
$activation = md5($myrow3["id"]).md5($login);// код активации аккаунта . Зашифруем через функцию md5 идентификатор и логин. Такое сочетание пользователь вряд ли сможет подобрать вручную через адресную строку.
$subject = "Подтверждение регистрации";//тема сообщения
$message = "Здравствуйте! Спасибо за регистрацию на citename.ru\nВаш логин: ".$login."\n
Перейдите по ссылке, чтобы активировать ваш аккаунт:\nhttp://localhost/test3/activation.php?login=".$login."&code=".$activation."\nС уважением,\n
Администрация citename.ru";//содержание сообщение
mail($email, $subject, $message, "Content-type:text/plane; Charset=windows-1251\r\n");//отправляем сообщение

Echo "Вам на E-mail выслано письмо с cсылкой, для подтверждения регистрации. Внимание! Ссылка действительна 1 час. Главная страница"; //говорим о отправленном письме пользователю
}

Сообщение отправлено! Теперь пользователь откроет его и перейдет по указанной ссылке на страницу, которая будет проверять код активации. Убедившись, что код верный, мы подтверждаем регистрацию, меняя в базе значение поля activation с «0» на «1».

Создаем файл activation.php

include ("bd.php");// файл bd . php должен быть в той же папке, что и все остальные, если это не так, то просто измените путь
$result4 = mysql_query ("SELECT avatar FROM users WHERE activation="0" AND UNIX_TIMESTAMP() - UNIX_TIMESTAMP(date) > 3600");//извлекаем аватарки тех пользователей, которые в течении часа не активировали свой аккаунт. Следовательно их надо удалить из базы, а так же и файлы их аватарок
if (mysql_num_rows($result4) > 0) {
$myrow4 = mysql_fetch_array($result4);
do
{
//удаляем аватары в цикле, если они не стандартные
if ($myrow4["avatar"] == "avatars/net-avatara.jpg") {$a = "Ничего не делать";}
else {
unlink ($myrow4["avatar"]);//удаляем файл
}
}
while($myrow4 = mysql_fetch_array($result4));
}
mysql_query ("DELETE FROM users WHERE activation="0" AND UNIX_TIMESTAMP() - UNIX_TIMESTAMP(date) > 3600");//удаляем пользователей из базы
if (isset($_GET["code"])) {$code =$_GET["code"]; } //код подтверждения
else
{ exit("Вы зашли на страницу без кода подтверждения!");} //если не указали code , то выдаем ошибку
if (isset($_GET["login"])) {$login=$_GET["login"]; } //логин,который нужно активировать
else
{ exit("Вы зашли на страницу без логина!");} //если не указали логин, то выдаем ошибку
$result = mysql_query("SELECT id FROM users WHERE login="$login"",$db); //извлекаем идентификатор пользователя с данным логином
$activation = md5($myrow["id"]).md5($login);//создаем такой же код подтверждения
if ($activation == $code) {//сравниваем полученный из url и сгенерированный код
mysql_query("UPDATE users SET activation="1" WHERE login="$login"",$db);//если равны, то активируем пользователя
echo "Ваш Е-мейл подтвержден! Теперь вы можете зайти на сайт под своим логином! Главная страница";
}
else {echo "Ошибка! Ваш Е-мейл не подтвержден! Главная страница";
//если же полученный из url и сгенерированный код не равны, то выдаем ошибку
}
?>

E-mail адрес подтвержден, теперь мы знаем, что этот адрес принадлежит данному пользователю, на него можно будет послать пароль, если пользователь его забудет, или другие уведомления. Но в чем различие активированных пользователей, от неактивированных? И те и другие могут зайти на сайт, следовательно, нам нужно ограничить в доступе неактивированных. Откроем файл testreg . php и допишем еще одно условие в запросе к базе:

$result = mysql_query("SELECT * FROM users WHERE login="$login" AND password="$password" AND activation="1"",$db); //извлекаем из базы все данные о пользователе с введенным логином
//мы дописали « AND activation ="1"», то есть пользователь будет искаться только среди активированных. Желательно добавить это условие к другим подобным проверкам данных пользователя

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

Автоматический вход.






Зарегистрироваться


Забыли пароль?

Ссылка есть, а файла нет. Давайте же напишем send _ pass . php . В нем мы спросим у пользователя логин и адрес почты. Если введенный e-mail и логин есть в базе, то отправим на него новый пароль, если пользователь забыл старый, ведь мы убедились при регистрации, что адрес почты действителен.

if (isset($_POST["login"])) { $login = $_POST["login"]; if ($login == "") { unset($login);} } //заносим введенный пользователем логин в переменную $login, если он пустой, то уничтожаем переменную
if (isset($_POST["email"])) { $email = $_POST["email"]; if ($email == "") { unset($email);} } //заносим введенный пользователем e-mail, если он пустой, то уничтожаем переменную
if (isset($login) and isset($email)) {//если существуют необходимые переменные

Include ("bd.php");

$result = mysql_query("SELECT id FROM users WHERE login="$login" AND email="$email" AND activation="1"",$db);// такой ли у пользователя е - мейл
$myrow = mysql_fetch_array($result);
if (empty($myrow["id"]) or $myrow["id"]=="") {
//если активированного пользователя с таким логином и е-mail адресом нет
exit ("Пользователя с таким e-mail адресом не обнаружено ни в одной базе ЦРУ:) Главная страница");
}
//если пользователь с таким логином и е-мейлом найден, то необходимо сгенерировать для него случайный пароль, обновить его в базе и отправить на е-мейл
$datenow = date("YmdHis");//извлекаем дату
$new_password = md5($datenow);// шифруем дату
$new_password = substr($new_password, 2, 6); //извлекаем из шифра 6 символов начиная со второго. Это и будет наш случайный пароль. Далее запишем его в базу, зашифровав точно так же, как и обычно.

$new_password_sh = strrev(md5($new_password))."b3p6f";// зашифровали
mysql_query("UPDATE users SET password="$new_password_sh" WHERE login="$login"",$db);// обновили в базе
//формируем сообщение

$message = "Здравствуйте, ".$login."! Мы сгененриоровали для Вас пароль, теперь Вы сможете войти на сайт citename.ru, используя его. После входа желательно его сменить. Пароль:\n".$new_password;//текст сообщения
mail($email, "Восстановление пароля", $message, "Content-type:text/plane; Charset=windows-1251\r\n");// отправляем сообщение

Echo "На Ваш e-mail отправлено письмо с паролем. Вы будете перемещены через 5 сек. Если не хотите ждать, то нажмите сюда.";//перенаправляем пользователя
}
else {//если данные еще не введены
echo "


Забыли пароль?


Забыли пароль?



Введите Ваш логин:



Введите Ваш E-mail:






";
}
?>

Далее мы сделаем автоматический вход. Работать он будет так: при удачном входе с нажатым чекбоксом в cookie будет вписываться auto = “yes”. Если сервер увидит, что в браузере auto = “yes”, то запустит сессию, а переменные возьмет там же, в cookie. Затем идет проверка запущенных сессий по базе.

Откроем testreg . php и допишем после удачного входа код:

If (isset($_POST["save"])){
//Если пользователь хочет, чтобы его данные сохранились для последующего входа, то сохраняем в куках его браузера

setcookie("id", $myrow["id"], time()+9999999);}
if (isset($_POST["autovhod"])){
//Если пользователь хочет входить на сайт автоматически
setcookie("auto", "yes", time()+9999999);
setcookie("login", $_POST["login"], time()+9999999);
setcookie("password", $_POST["password"], time()+9999999);
setcookie("id", $myrow["id"], time()+9999999);}

Теперь надо запустить в нужном месте сессию, если стоит автоматический вход. Откроем index.php и напишем в самом начале страницы:

// вся процедура работает на сессиях. Именно в ней хранятся данные пользователя, пока он находится на сайте. Очень важно запустить их в самом начале странички!!!
session_start();
include ("bd.php");// файл bd.php должен быть в той же папке, что и все остальные, если это не так, то просто измените путь
if (isset($_COOKIE["auto"]) and isset($_COOKIE["login"]) and isset($_COOKIE["password"]))
{//если есть необходимые переменные
if ($_COOKIE["auto"] == "yes") { // если пользователь желает входить автоматически, то запускаем сессии
$_SESSION["password"]=strrev(md5($_COOKIE["password"]))."b3p6f"; //в куках пароль был не зашифрованный, а в сессиях обычно храним зашифрованный
$_SESSION["login"]=$_COOKIE["login"];// сессия с логином
$_SESSION["id"]=$_COOKIE["id"];//идентификатор пользователя
}
}

Вот теперь нам даже выход не поможет, что бы сменить пользователя! В exit . php удаляются сессии, которые все равно создаются на index . php , благодаря cookie, которые хранятся очень долго! Давайте уж все-таки починим файл exit . php , а то он не функционален в случае автоматического входа. Надо всего лишь очистить в cookie автоматический вход, после удаления переменных из сессии:

Setcookie("auto", "", time()+9999999);//очищаем автоматический вход

Так же необходимо добавить в update _ user . php после обновления логина в сессии:

$_SESSION["login"] = $login;//Обновляем логин в сессии
if (isset($_COOKIE["login"])) {
setcookie("login", $login, time()+9999999);// Обновляем логин в куках
}

И то же самое с паролем

$result4 = mysql_query("UPDATE users SET password="$password" WHERE login="$old_login"",$db);// обновляем пароль
if ($result4=="TRUE") {//если верно, то обновляем его в сессии
$_SESSION["password"] = $password;
if (isset($_COOKIE["password"])) {
setcookie("password",$_POST["password"], time()+9999999);// Обновляем пароль в куках , если они есть
}

Ну вот и все. Надеюсь, что у Вас все получится! Удачи!