Контакты

По бездействию пользователя на php. Подводные камни использования сессий в PHP

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

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

Цель этой статьи - осветить подводные камни использования сессий в 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.

аккаунт 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    требуется уточнить 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-адрес, через который работали Вы сами. Если этого не сделать, то возрастают риски блокировок и санкций (это лишь догадки, прямых достоверных сведений об алгоритмах наложения санкций ни у кого нет и никогда не будет).

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

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

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

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


    Ваш E-mail *:



    Затем нужно подправить 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

    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 и логин есть в базе, то отправим на него новый пароль, если пользователь забыл старый, ведь мы убедились при регистрации, что адрес почты действителен.

    Далее мы сделаем автоматический вход. Работать он будет так: при удачном входе с нажатым чекбоксом в 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 и напишем в самом начале страницы:

    Файл CONFIG.PHP

    less/reg/?mode=auth">Войти

  • Понравилась статья? Поделитесь ей