Сайт на WordPress взломали: как убрать спам-страницы из индекса Яндекса за вечер

Сайт на WordPress взломали: как убрать спам-страницы из индекса Яндекса за вечер
Анатолий Кузнецов
Анатолий Кузнецов
SEO-оптимизатор с 20-летним стажем. Автор блога seo-prodvizhenie-biznesa.ru о продвижении и доработке сайтов.

Сайт на WordPress взломали — и первое, что видит владелец, это не изменённая главная и не баннер хакера, а внезапный обвал позиций и странные строки в выдаче: «купить диплом», «займ без отказа», иероглифы вперемешку с латиницей. Файлы можно почистить за час. Проблема в том, что поисковик уже забрал в индекс несколько тысяч чужих страниц, и они продолжают жить своей жизнью даже после того, как на сервере от них не осталось ни байта.

Ниже — порядок, по которому такая история закрывается за один вечер: как убедиться, что это именно взлом, что делать в первые двадцать минут, чем чистка отличается от лечения и почему сама по себе она ничего не решает. В SEO с 2005 года, и сайты после взлома приходят на разбор регулярно — почти всегда с одной и той же незакрытой частью работы.

Как отличить взлом от других причин падения

Позиции падают по десятку причин, и половина не связана с безопасностью: переезд на новый шаблон, ошибка в robots.txt, смена хостинга, обновление алгоритма. У взлома есть отпечаток, который видно снаружи, без доступа к серверу. Проверка занимает пять минут.

  • Оператор site: в поиске. Запрос вида site:вашдомен.ру плюс отдельно site:вашдомен.ру займ, site:вашдомен.ру диплом, site:вашдомен.ру casino. Если в выдаче появляются адреса, которых вы не создавали, дальше можно не гадать.
  • Число страниц в индексе. В Яндекс Вебмастере раздел «Индексирование» — «Страницы в поиске». Сайт из 200 статей, у которого в поиске 4 800 адресов, заражён почти наверняка. Скачок на графике даёт и дату: с неё начинают отсчёт при поиске изменённых файлов.
  • Раздел «Диагностика». Яндекс сообщает о вредоносном коде отдельным уведомлением. Появляется оно не мгновенно: отсутствие ничего не доказывает, наличие — доказывает всё.
  • Просмотр как робот. В Вебмастере есть инструмент, который показывает страницу глазами поискового робота. Классический сценарий заражения WordPress — клоакинг: человеку отдаётся нормальная страница, роботу и переходу из поиска — спамная. Браузером вы её не увидите никогда.
  • Дата изменения файлов. По SSH одна команда показывает всё, что менялось за последние двое суток: find /путь/к/сайту -type f -mtime -2 -name "*.php". Ядро WordPress свои файлы просто так не переписывает. Если в выдаче оказались файлы из wp-includes и корня — это не обновление.

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

Чем спам-страницы бьют по позициям

Механизм ущерба состоит из четырёх слоёв, и они складываются, а не заменяют друг друга.

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

Второй — расход обхода. Лимит на количество страниц, которые робот берёт за визит, конечный и общий на весь сайт. Пять тысяч сгенерированных адресов съедают его целиком: новые статьи перестают индексироваться, обновления старых не доходят до индекса неделями.

Третий — исходящие ссылки. Спам-страницы почти всегда ставят ссылки на сторонние ресурсы, ради этого всё и делается. Сайт превращается в донора для чужой ссылочной схемы, и алгоритмы, отвечающие за качество исходящего профиля, реагируют.

Четвёртый — пометка в выдаче. Когда вредоносный код подтверждён, у сайта в результатах поиска появляется предупреждение. Оно убивает переходы задолго до того, как упадут позиции: человек видит красную строчку и не кликает.

При этом взлом сам по себе не обнуляет историю домена и не даёт вечного «бана». Вопрос в скорости: чем дольше спам живёт в индексе, тем дольше возвращаются позиции.

Первые двадцать минут: изоляция

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

  1. Полная копия «как есть». Архив файлов и дамп базы до любых правок — не для восстановления, а как материал для расследования и источник случайно удалённого.
  2. Смена всех паролей. Панель хостинга, FTP и SSH, пользователь базы данных, администраторы WordPress. Пароль базы меняется вместе со строкой в wp-config.php, иначе сайт ляжет.
  3. Сброс сессий. Замена восьми констант-ключей в wp-config.php (AUTH_KEY и остальные) разлогинивает всех, включая того, кто зашёл с украденной сессией. Строки генерируются на официальном сервисе ключей WordPress.
  4. Удаление лишних администраторов и понижение ролей у всех, кому админка не нужна.
  5. Проверка задач cron в таблице опций: там часто прописан хук, который каждый час восстанавливает удалённый бэкдор. Пока он там, чистка бессмысленна.

Только после этого имеет смысл трогать файлы.

Где искать заражение в файлах WordPress

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

Где смотреть Признак заражения Что делать
Файлы ядра (корень, wp-admin, wp-includes) Отличие от эталона той же версии, свежая дата изменения, длинная строка base64 в начале файла Не чистить вручную. Скачать дистрибутив своей версии и перезалить каталоги целиком, кроме wp-content
wp-content/uploads Файлы .php среди картинок, каталоги с бессмысленными именами, .htaccess внутри папок загрузок PHP в загрузках не нужен никогда. Удалять, затем запретить исполнение через правило веб-сервера
Файлы темы (functions.php, header.php, footer.php) Вставки eval, gzinflate, preg_replace с модификатором /e, вызовы file_get_contents к внешнему домену Сравнить с копией темы из репозитория или из своего бэкапа, вернуть чистую версию
Каталоги плагинов Плагин, которого нет в списке в админке; папка с датой изменения свежее остальных Удалить плагин целиком и поставить заново из официального источника
.htaccess в корне Правила RewriteCond по User-Agent или Referer, перенаправляющие переходы из поиска Заменить файл на стандартный блок WordPress, сохранив свои правила отдельно
База данных Скрипты в wp_options, чужой домен в siteurl, тысячи записей неизвестного типа в wp_posts Правки только по дампу и с проверкой, что тип записи действительно не ваш

Быстрее всего заражение находится сравнением с эталоном, а не глазами: для ядра это официальный архив нужной версии, для плагинов — их же дистрибутивы. Файлы, которых нет ни в одном эталоне, изучаются первыми. Типовые точки входа собраны в материале про способы взлома сайта: в девяти случаях из десяти дырой оказывается не WordPress, а заброшенный плагин.

Чистка без поломки сайта

Главное правило чистки: не удалять то, что не опознано. Файл с непонятным именем может оказаться частью коммерческого плагина, а строка кодировки в теме — легальной защитой лицензии. Удалили — сайт отдал белый экран, и вы чините уже две проблемы вместо одной.

Порядок такой. Перезаливаются каталоги ядра из официального дистрибутива той же версии — wp-admin, wp-includes и файлы в корне, кроме wp-config.php и .htaccess. Плагины именно переустанавливаются, а не обновляются: обновление поверх заражённого каталога оставляет лишние файлы на месте. Тема возвращается к чистой версии, свои правки переносятся из дочерней.

Дальше — база. Записи неизвестного типа удаляются запросом с явным указанием типа и статуса, а не «всё, что новее такой-то даты». В опциях проверяются siteurl, home, виджеты и автозагружаемые опции подозрительного размера. В wp-config.php часто оставляют одну строку подключения внешнего файла в самом начале.

Последнее — права: каталоги 755, файлы 644, wp-config.php 600 или 400. Права 777, которые ставят «чтобы заработало», — самая частая причина повторного взлома через неделю после чистки. Остальные принципы собраны в материале о том, как защитить сайт от взлома.

Главная ошибка: файлы вычистили, индекс не тронули

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

Ключевой момент — какой ответ отдавать. После чистки адреса обычно начинают отдавать 404, а WordPress часто подменяет его редиректом на главную или на похожую запись. Это худший вариант: робот видит рабочую страницу и оставляет адрес в индексе. Почему так происходит — в материале про битые ссылки и неправильные редиректы.

Тип мусора Правильный ответ Почему так
Сгенерированные спам-страницы (тысячи адресов по шаблону) 410 Gone Прямой сигнал «удалено навсегда». Обрабатывается быстрее 404 и не оставляет надежды на возврат
Единичные страницы, у которых был свой смысл до подмены 200 с восстановленным содержимым Адрес накопил сигналы, терять его незачем — достаточно вернуть нормальный текст
Спамные адреса, совпадающие по структуре с вашими разделами 301 на профильный раздел Работает, только если содержимое действительно близко. Иначе поисковик посчитает перенаправление некорректным
Адреса с параметрами, порождённые скриптом 410 плюс запрет параметра в robots.txt Число комбинаций бесконечно, точечное удаление не закончится никогда
Страницы, попавшие в индекс с чужого поддомена Удаление поддомена и 410 на уровне сервера Поддомен в панели Вебмастера — отдельный сайт со своей очередью на удаление

Технически 410 для группы адресов удобнее всего ставить одним правилом на уровне сервера — по шаблону пути или по параметру, а не списком из четырёх тысяч строк. Правило проверяется до правки: адрес открывается через инструмент проверки ответа сервера в Вебмастере, и в ответе должен быть именно 410.

Переобход: как ускорить выброс мусора

После того как ответы настроены, остаётся заставить робота их увидеть. Инструментов три, и они дополняют друг друга.

Переобход страниц в Яндекс Вебмастере. Суточный лимит зависит от сайта, обычно это десятки адресов. Отправлять туда все четыре тысячи бессмысленно — отправляются самые заметные: те, что реально показываются в выдаче по спамным словам.

Инструмент удаления страниц. Работает, если адрес отдаёт 404, 410 или закрыт в robots.txt. Принимает и отдельные адреса, и префикс каталога — основной способ выбросить массовый мусор.

IndexNow. Протокол мгновенного уведомления работает в обе стороны: им сообщают не только о новых страницах, но и об изменившихся, включая удалённые. Если он уже настроен, отправка списка удалённых адресов ускоряет реакцию; подробности — в материале про IndexNow и моментальную индексацию.

Дальше — контроль. Раз в несколько дней повторяется запрос с оператором site и спамными словами, а в Вебмастере отслеживается число страниц в поиске: график должен идти вниз ступеньками. Стоит на месте через две недели — значит, часть адресов по-прежнему отдаёт 200; их ищут выгрузкой страниц в поиске и проверкой кодов по списку, порядок описан в разборе как проверить индексацию сайта в Яндексе. Параллельно запрашивается перепроверка в «Диагностике», иначе пометка в выдаче висит дольше нужного.

Что закрыть после чистки

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

  • Обновить всё. Ядро, темы, плагины. Заброшенные плагины, которые не обновлялись больше года, удалить и заменить — именно они дают большинство дыр.
  • Удалить неиспользуемое. Отключённый плагин остаётся исполняемым кодом на диске. Отключение не защищает, удаление защищает.
  • Запретить PHP в загрузках. Правило веб-сервера, которое не отдаёт .php из каталога wp-content/uploads, закрывает самый популярный способ закрепиться.
  • Отключить редактор файлов в админке строкой define('DISALLOW_FILE_EDIT', true); в wp-config.php. Тот, кто получил доступ к админке, теряет возможность править код прямо из браузера.
  • Ограничить попытки входа и включить двухфакторную проверку для администраторов. Страницу входа при этом переименовывать не нужно — она должна остаться на стандартном адресе, иначе ломаются интеграции и восстановление доступа.
  • Настроить резервные копии с хранением вне того же сервера и с глубиной хотя бы в тридцать дней. Копия за вчера бесполезна, если заражение произошло месяц назад и всё это время лежало тихо.
  • Следить за числом страниц в поиске. Резкий рост — ранний сигнал, что адреса создаются без вашего ведома.

Про сроки. Мусор уходит из индекса за две–четыре недели при активной работе с переобходом, позиции возвращаются медленнее и не всегда полностью. Первых недель пугаться не стоит: падение идёт по инерции, пока индекс ещё содержит спам. Если через полтора-два месяца динамики нет, дело уже не во взломе, а в обычной технической части — с неё начинается любой бесплатный аудит сайта.

Частые вопросы

Хостинг сам почистил сайт антивирусом. Этого достаточно?
Недостаточно, и по двум причинам. Антивирус хостинга ищет сигнатуры известных шеллов и хорошо находит файлы, но почти никогда не трогает базу, задачи cron и лишних администраторов. Плюс он ничего не делает с индексом — а это и есть та часть, из-за которой позиции не возвращаются.

Можно ли просто восстановиться из бэкапа и забыть?
Можно, если копия точно старше заражения и вы знаете дату. Но откат возвращает и уязвимость, через которую вошли, поэтому сразу после восстановления обновляют всё и закрывают дыру. И в любом случае остаётся работа с индексом: бэкап сайта не откатывает индекс поисковика.

Ставить ли noindex на спам-страницы вместо 410?
Нет. Метатег noindex требует, чтобы страница открывалась и отдавала 200, то есть спам продолжает существовать. Для удалённого содержимого правильный сигнал — код ответа, а не метатег. Noindex уместен для своих служебных страниц, которые должны работать, но не показываться в поиске.

Закрыть весь сайт в robots.txt на время чистки — хорошая идея?
Плохая. Робот перестанет заходить и не увидит ни ваших исправлений, ни кодов 410, а страницы в индексе останутся замороженными в том виде, в каком были. Сайт должен оставаться открытым, чтобы переобход состоялся.

Спам вернулся через неделю после чистки. Где искать?
В трёх местах по порядку: запланированные задачи в таблице опций, файлы вне корня сайта (соседние домены на том же аккаунте хостинга) и учётные записи FTP, которые заводят отдельно от паролей панели. Если на аккаунте несколько сайтов, лечить нужно все сразу — иначе они перезаражают друг друга.

Насколько это вообще реально сделать самому за вечер?
Реально, если есть доступ по SSH, свежая копия и понимание, где что лежит. Если сайт коммерческий, заражение старше месяца или на аккаунте несколько проектов — быстрее разобрать ситуацию вместе: SEO-консультация по вашему сайту обычно закрывает вопрос о порядке действий за одну встречу. Дальше всё равно нужны месяцы обычной работы, и как она устроена — в разделе SEO-продвижение сайтов.

Коротко

  • Взлом подтверждается снаружи: оператор site со спамными словами, скачок числа страниц в поиске, просмотр страницы глазами робота и свежие даты изменения PHP-файлов.
  • Порядок обязателен: сначала изоляция (пароли, ключи сессий, лишние администраторы, задачи cron), потом чистка. В обратную сторону не работает — скрипт восстанавливает удалённое.
  • Ядро и плагины не лечат вручную, а перезаливают из официальных дистрибутивов; вручную разбирают только тему, базу и .htaccess.
  • Чистка файлов не убирает страницы из поиска. Массовому мусору нужен код 410, единичным подменённым страницам — восстановленное содержимое, и никаких редиректов на главную.
  • Выброс из индекса ускоряют инструмент удаления страниц по префиксу каталога, переобход самых заметных адресов и IndexNow; контроль — по графику страниц в поиске.
  • Повтор закрывают обновления, удаление неиспользуемых плагинов, запрет PHP в загрузках, отключение редактора файлов и бэкапы с глубиной от месяца — при сохранении стандартного адреса входа.

Увеличьте позиции и продажи вашего сайта

Профессиональное SEO-продвижение с гарантией результата. Выберите подходящую услугу:

Анатолий Кузнецов — SEO-оптимизатор

Остались вопросы по продвижению?

Меня зовут Анатолий Кузнецов, я SEO-оптимизатор с 20-летним стажем. Разберу ваш сайт, отвечу на вопросы и подскажу, что улучшить для роста позиций в Яндексе и Google.

Связаться со мной →

Комментарии

Олимпиада Цуканова

В поиске 11 тысяч страниц вместо трёхсот, все про кредиты. Файлы почистил программист неделю назад, а число не падает совсем. Что я делаю не так?

Анатолий Кузнецов автор

Скорее всего, ничего дополнительно не делаете — а надо. Чистка файлов и удаление из индекса это две независимые задачи, вторая сама не запускается. Проверьте прямо сейчас: возьмите любой спамный адрес из выдачи и посмотрите код ответа сервера. Если там 200 или редирект на главную, робот считает страницу живой и никуда её не денет. Нужен 410 по шаблону пути — одним правилом на весь каталог, а не списком. После этого идите в инструмент удаления страниц в Вебмастере и отправляйте туда префикс каталога, он принимает целые разделы. График страниц в поиске начнёт спускаться ступеньками через несколько дней, это нормальная скорость.

Ярополк Юрлов

Смотрю сайт в браузере — всё чисто. Но в выдаче вместо моих заголовков какая-то реклама букмекеров. Как такое возможно?

Анатолий Кузнецов автор

Это клоакинг, самый неприятный вариант заражения именно потому, что глазами его не поймать. Скрипт смотрит на User-Agent и на источник перехода: обычному посетителю отдаёт вашу страницу, роботу и переходу из поиска — спамную. Проверяется двумя способами. Первый — инструмент «Просмотр как робот» в Вебмастере, он покажет ровно то, что видит поисковик. Второй — открыть страницу в режиме инкогнито, перейдя на неё именно из поисковой выдачи, а не по прямому адресу. Код при этом обычно сидит в .htaccess в виде правил RewriteCond по User-Agent или в начале index.php одной длинной строкой. Начните с этих двух файлов.

Вениамин Ремезов

Вопрос по таблице: почему 410, а не 404? В документации пишут, что для поисковика разницы почти нет.

Анатолий Кузнецов автор

Разница есть, и она в скорости, а не в конечном результате. 404 означает «сейчас не найдено» — робот допускает, что страница вернётся, и заходит проверять её ещё несколько раз, растягивая удаление на недели. 410 означает «удалено навсегда», и повторные проверки сокращаются. На объёме в три сотни адресов вы разницу не заметите, на объёме в несколько тысяч она превращается в лишний месяц. Плюс чисто практический момент: 410 вы ставите правилом сервера осознанно, а 404 в WordPress легко превращается в редирект или в страницу с кодом 200 из-за плагина или темы, и об этом узнают слишком поздно.

Калерия Ушанова

Удалила всё подозрительное из uploads, сайт лёг белым экраном. Восстановила из бэкапа вместе с вирусом. Теперь боюсь трогать.

Анатолий Кузнецов автор

Белый экран после удаления — почти всегда признак того, что удалён файл, который где-то подключается через include. Лечится это не возвратом всего архива, а точечно: включите вывод ошибок в wp-config, откройте сайт, и PHP прямо назовёт путь к отсутствующему файлу. Дальше решайте по имени: если это часть плагина — переставьте плагин целиком, если непонятная вставка — уберите строку подключения из того файла, который её вызывает. И порядок на будущее: копия делается перед чисткой, чистка идёт от ядра к плагинам и только потом к загрузкам, а после каждого шага сайт открывается и проверяется. Тогда вы всегда знаете, какое именно действие что сломало.

Пелагея Гвоздарёва

Нашла в wp_options задачу, которая каждый час дёргает какой-то внешний адрес. Удалять прямо в phpMyAdmin или это опасно?

Анатолий Кузнецов автор

Удалять можно, но аккуратно и не всю опцию целиком. В строке cron лежит сериализованный массив со всеми задачами сайта, включая легальные — проверку обновлений, отправку писем, работу плагинов. Если стереть значение полностью, WordPress пересоберёт его сам, но часть плагинов свои задачи потеряет до следующей активации. Правильнее убрать конкретный хук: сделать дамп таблицы, открыть значение, найти имя задачи с внешним адресом и удалить только её ветку, сохранив структуру массива. И обязательно найдите файл, который эту задачу регистрирует, иначе она вернётся при первом же обращении к сайту.

Трифон Ефимцев

У меня на аккаунте хостинга шесть сайтов. Заражённым выглядит один. Проверять остальные обязательно?

Феодора Зюзина

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

Герасим Наливкин

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

Демьян Пестов

Про права 777 отдельное спасибо. Хостинг сам предлагал их поставить, когда не работала загрузка картинок. Поставил, через месяц получил всё описанное.

Тамила Оглоблина

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

Никанор Хомутов

Будут, но это не страшно. Внешние ссылки на несуществующие адреса просто дают 410 и постепенно перестают учитываться. Хуже, когда такие адреса продолжают отдавать 200 через кэш плагина, который забыли сбросить.

Юстина Шелестова

Не хватает раздела про почту. У нас после взлома домен попал в спам-листы, письма с форм перестали доходить, и заметили это только через три недели по отсутствию заявок.

Прокрутить вверх