Безопасность сайта

Фишинговая страница на вашем домене: как найти и убрать

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

11 минут чтения 2 Дмитрий Акиндинов
Содержание
  1. Как об этом узнают: четыре сценария
  2. Где обычно лежат такие страницы
  3. Поиск: три способа, все нужны
  4. Чистка: порядок, который не превращается в неделю мучений
  5. Снятие блокировки: что делать в каком порядке
  6. Через что зашли: реальная статистика по моим случаям
  7. Первая неделя после инцидента: чек-лист
  8. Сколько это стоит и когда звать
  9. Частые вопросы

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

Утром приходит письмо от хостера: ваш аккаунт распространяет фишинг, у вас 24 часа. Открываете сайт, а он выглядит как обычно, ничего не сломано, заявки идут. Только по адресу вроде вашдомен.ру/wp-content/uploads/2024/07/sber/index.html лежит копия страницы входа в банк, и туда идёт трафик из чужих рассылок. Фишинговая страница на сайте почти всегда живёт незаметно для владельца: её не видно ни в меню, ни в карте сайта, ни в админке. Разберу, как её найти, чем вычистить, как потом снять красный экран в браузерах и закрыть ту дыру, через которую всё это приехало.

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

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

Как об этом узнают: четыре сценария

Владелец сайта не находит фишинг сам. Он получает его как новость извне, и от того, кто именно принёс новость, зависит запас времени.

Письмо от хостера. Самый частый вариант в России. Timeweb, Beget, REG.RU получают жалобу через абузу от банка или от Group-IB, автоматом парсят её и шлют клиенту ультиматум. Формулировка обычно жёсткая: удалите вредоносный контент в течение 24 часов, иначе аккаунт будет заблокирован. Блокируют реально, целиком, вместе с почтой на домене и всеми остальными сайтами в аккаунте. У меня был клиент, у которого из-за фишинга в одном старом сайте на том же хостинге легли ещё три рабочих.

Красный экран в Chrome. Google Safe Browsing помечает домен как Deceptive site ahead, и после этого сайт не открывается ни в Chrome, ни в Яндекс.Браузере, ни в Safari, ни в Firefox: базу используют все. Заходят клиенты, видят красное полотно, звонят вам. Обойти предупреждение можно в два клика, но делать этого не станет почти никто, так что считайте, что сайт на это время просто выключен.

Уведомление в Яндекс.Вебмастере. Раздел «Безопасность и нарушения» загорается красным, тип угрозы указывается как социальная инженерия или мошеннический контент. Позиции при этом начинают падать в течение нескольких дней.

Жалоба клиента. Реже, но бывает: человеку пришло письмо со ссылкой на ваш домен, он узнал название компании и написал вам. Такое стоит воспринимать как подарок, потому что это самый ранний сигнал.

Дальше все четыре сценария сходятся в одну работу: найти, вычистить, доказать, что чисто, закрыть вход.

Где обычно лежат такие страницы

Фишинговый комплект (в отчётах его называют kit) заливают туда, где скрипту разрешена запись и куда владелец никогда не смотрит. За годы набор мест почти не меняется.

МестоЧто там встречаетсяКак часто вижу
wp-content/uploads/2024/**/папка с именем банка или сервиса, внутри index.html, css, картинкиочень часто
wp-content/uploads/.cache/, .tmp/скрытые каталоги с точкой в начале именичасто
корень сайта, папки wp/, css/, fonts/одиночный wp-login.php или verify.php в неожиданном местечасто
/bitrix/tmp/, /upload/ в Битриксераспакованный zip с готовой копией страницы банкасредне
/image/catalog/ в OpenCarthtml и php вперемешку с картинками товаровсредне
чужие домены в том же аккаунте хостингастарый неподдерживаемый сайт, через который зашлинедооценено

Последняя строка важнее остальных. На shared-хостинге все сайты аккаунта обычно работают под одним системным пользователем, и php-процесс одного сайта спокойно пишет в папку соседнего. Заброшенная Joomla 3 десятилетней давности в подпапке становится входом ко всему остальному.

Ещё одна деталь. Комплект почти никогда не приезжает один. Рядом лежит загрузчик: маленький php-файл, через который злоумышленник заливает новые версии, если старую удалили. Про такие файлы и их повадки есть подробный разбор: /blog/shell-v-papke-uploads/. Удалить только видимую фишинговую страницу и успокоиться значит через неделю получить второе письмо от хостера.

Поиск: три способа, все нужны

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

По датам изменения

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

# всё, что изменилось за 20 дней, кроме кэша и логов
find /home/u/user/site.ru/public_html -type f 
  ( -name '*.php' -o -name '*.html' -o -name '*.htm' ) 
  -mtime -20 
  -not -path '*/cache/*' -not -path '*/logs/*' 
  -printf '%TY-%Tm-%Td %TH:%TM  %pn' | sort

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

По расположению

Html и php там, где их быть не должно, это готовый список подозреваемых. В uploads по определению не может лежать ни одного исполняемого файла.

# исполняемое и html в медиатеке WordPress
find wp-content/uploads -type f 
  ( -name '*.php' -o -name '*.phtml' -o -name '*.html' -o -name '*.htm' ) -ls

# скрытые каталоги внутри медиатеки
find wp-content/uploads -type d -name '.*'

Каждая строка в выводе первой команды это находка. Нормальных исключений ровно два: index.php с заглушкой в корне uploads и файлы некоторых плагинов кэша, которые всё равно лучше проверить глазами.

По содержимому

Фишинговые комплекты собирают на конвейере, и внутри у них повторяются одни и те же куски: отправка введённых данных в Telegram, mail с темой Result, base64 с картинками логотипов банка.

grep -rIl --include='*.php' --include='*.html' 
  -e 'api.telegram.org' 
  -e 'sendMessage?chat_id' 
  -e 'eval(base64_decode' 
  -e 'FILE_APPEND' 
  -e 'РезультатыResult' 
  . 2>/dev/null

Дальше проверяем целостность ядра. Для WordPress с установленным WP-CLI это одна команда, и она честно сравнивает файлы с эталоном из репозитория:

wp core verify-checksums
wp plugin verify-checksums --all

Что она не покажет: изменения в теме и в файлах, которых нет в официальных сборках. Тема проверяется руками или сравнением с чистой копией из архива поставщика.

Если есть логи веб-сервера, стоит сразу посмотреть, кто и когда обращался к найденным файлам. Разбор логов после инцидента я расписывал в отдельной статье: /blog/kak-nayti-tochku-vzloma-po-logam/.

Чистка: порядок, который не превращается в неделю мучений

  1. Снимите полный бэкап в текущем виде, до чистки. Да, он заражённый. Он нужен как доказательство и как страховка, если удалите лишнее. Кладите его вне хостинга: в Яндекс Объектное Хранилище или просто себе на диск.
  2. Заберите отдельно дамп базы. wp db export dump-before.sql или через phpMyAdmin, если SSH нет.
  3. Удалите найденное. Не переименовывайте в .bak и не оставляйте «на посмотреть»: файл с расширением .php.bak на многих конфигурациях всё ещё исполняется.
  4. Проверьте базу. Фишинг сидит в файлах, но рядом с ним часто едет спам-инъекция и лишний администратор.
  5. Смените все пароли: FTP, SSH, панель хостинга, базу, всех администраторов WordPress. Заодно поднимите новый ключ, если пускали кого-то по паролю.
  6. Прогоните поиск заново через сутки. Если загрузчик остался, за сутки он обычно проявляет себя.

Проверка базы на лишних админов и на автозагрузку мусора:

SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM wp_users u
JOIN wp_usermeta m ON m.user_id = u.ID
WHERE m.meta_key = 'wp_capabilities'
  AND m.meta_value LIKE '%administrator%';

SELECT option_name, LENGTH(option_value) AS len
FROM wp_options
WHERE autoload = 'yes'
ORDER BY len DESC
LIMIT 20;

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

Полный порядок чистки WordPress со всеми развилками я расписывал здесь: /blog/kak-ochistit-wordpress-ot-virusov/.

После чистки закройте исполнение php в медиатеке. На Apache это лежит в wp-content/uploads/.htaccess:

<FilesMatch ".(?i:php|phtml|php[0-9]|phar|cgi|pl|html?)$">
  Require all denied
</FilesMatch>

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

Права на файлы тоже стоит вернуть в норму: 644 на файлы, 755 на каталоги. 777 на папке uploads это открытое приглашение, и оно встречается чаще, чем хотелось бы.

Снятие блокировки: что делать в каком порядке

Пока сайт помечен как фишинговый, ни SEO, ни реклама, ни рассылки не работают. Порядок подачи заявок такой.

Google Search Console. Раздел «Проблемы безопасности». После чистки жмёте «Запросить проверку», в комментарии пишете конкретику: что нашли, где лежало, что удалили, что закрыли. Общая фраза «мы всё почистили» удлиняет рассмотрение. Обычный срок ответа по фишингу: от нескольких часов до трёх суток. Красный экран снимается автоматически, как только Google подтвердит.

Яндекс.Вебмастер. Раздел «Безопасность и нарушения», кнопка «Я всё исправил». Яндекс перепроверяет медленнее, ориентируйтесь на срок от суток до недели. Повторная заявка при неудачной проверке доступна не сразу, поэтому не жмите кнопку, пока не убедились, что чисто.

Хостер. Отвечайте письмом в тот же тикет, не заводите новый. Приложите список удалённых путей и скриншот пустого вывода поиска. Если у хостера стоит собственный сканер (у Timeweb и Beget они есть), попросите прогнать проверку принудительно, иначе флаг в панели может висеть ещё неделю.

Антивирусные базы. Если домен попал в списки Kaspersky, Dr.Web или в базы вроде PhishTank, подавайте отдельные заявки на снятие. Для PhishTank достаточно комментария к записи, для антивирусов есть формы false positive на их сайтах. Обычно это самый долгий пункт: до двух недель.

Отдельно про почту. Если на домене висит рассылка, проверьте, не улетел ли домен в чёрные списки Spamhaus и в постмастеры Mail.ru и Яндекса. Что делать с репутацией домена после инцидента, разобрано тут: /blog/sayt-v-chyornom-spiske-yandeksa-google/.

Через что зашли: реальная статистика по моим случаям

Точка входа почти всегда одна из четырёх. Гадать не надо, надо проверять по порядку.

Устаревший плагин или тема. Лидер с большим отрывом. Раз в пару месяцев в популярном плагине находят уязвимость произвольной загрузки файлов, и в течение суток по сети идёт массовое сканирование. Классика: старые версии File Manager, форм обратной связи, конструкторов страниц, галерей. Если сайт год не обновлялся, вход был открыт задолго до инцидента.

Украденный FTP-пароль. Пароль сохранён в FileZilla на компьютере менеджера, на компьютере стоял стилер, пароль ушёл. Симптом узнаваемый: в логах FTP один вход из непонятной страны, залито сразу всё и без единого php-эксплойта. Если такое видите, чистить надо не только сайт, но и компьютер.

Заброшенный сайт в том же аккаунте. Тестовая копия, старый лендинг, поддомен old.вашдомен.ру. Никто не помнит, что он есть, никто его не обновляет.

Форма загрузки без проверок. Резюме, фотографии, документы. Разработчик проверил расширение по строке из $_FILES['name'], а её отдаёт браузер, то есть злоумышленник. Файл photo.php уезжает в uploads как есть.

Как отличить: смотрите время первого появления чужого файла и ищите в access-логах запросы за минуту-две до него. В 90 процентах случаев там будет один POST на конкретный скрипт плагина, и это ответ.

Первая неделя после инцидента: чек-лист

  • [ ] Полный бэкап до чистки лежит вне хостинга.
  • [ ] Все найденные файлы удалены, поиск через сутки дал пустой вывод.
  • [ ] wp core verify-checksums проходит без замечаний.
  • [ ] Список администраторов в базе проверен, лишние удалены.
  • [ ] Пароли сменены: панель хостинга, FTP, SSH, база, все админы CMS.
  • [ ] Исполнение php в uploads закрыто, права на файлы 644, на папки 755.
  • [ ] Ядро CMS и все плагины обновлены, неиспользуемые удалены, а не деактивированы.
  • [ ] Заброшенные сайты и поддомены в аккаунте либо обновлены, либо снесены.
  • [ ] Заявки поданы: Search Console, Вебмастер, хостер, антивирусные базы.
  • [ ] Включена двухфакторная аутентификация в админке и ограничение попыток входа.
  • [ ] Настроен ежедневный бэкап с хранением вне хостинга, проверено восстановление.
  • [ ] Настроен мониторинг изменений файлов, чтобы следующий раз узнать первым.

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

# эталон после чистки
find /home/u/user/site.ru/public_html -type f -name '*.php' 
  -exec md5sum {} ; | sort -k2 > /home/u/user/private/checksums.txt

# ежедневная проверка по крону
find /home/u/user/site.ru/public_html -type f -name '*.php' 
  -exec md5sum {} ; | sort -k2 > /tmp/now.txt
diff /home/u/user/private/checksums.txt /tmp/now.txt || 
  mail -s "site.ru: изменились php-файлы" you@example.com < /tmp/now.txt

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

Сколько это стоит и когда звать

Разовая чистка фишинга у частного специалиста в 2026 году стоит порядка 8 000-20 000 рублей в зависимости от размера сайта и от того, насколько глубоко залезли. Дороже становится, когда нет бэкапов и приходится восстанавливать содержимое по кэшу поисковиков. Плюс отдельно время на переписку с Google и Яндексом: заявки подать просто, дождаться и переподать при отказе дольше.

Абонентка эту историю в основном предотвращает: обновления раз в месяц, закрытый uploads, бэкапы вне хостинга и мониторинг файлов стоят дешевле одной чистки. Годовой тариф у меня 35 000 рублей, помесячный от 15 000, ориентиры и что входит есть на /#pricing. Что туда не входит: разработка с нуля и дизайн, я обслуживаю уже существующие сайты.

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

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

Как найти фишинговую страницу на своём сайте

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

Что делать, если хостер дал 24 часа

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

Через что заходят чаще всего

Через устаревший плагин или тему с известной уязвимостью. Реже через украденный пароль FTP. Пока точка входа открыта, страницы возвращаются в течение нескольких дней после чистки.

Сколько стоит чистка

У частного специалиста порядка 8-20 тысяч рублей в зависимости от размера сайта и глубины заражения. Абонентское обслуживание с мониторингом обходится дешевле разовых чисток, если случай не единичный.

Передайте техническую часть в надёжные руки.

Слежу за хостингом, бэкапами и обновлениями. Когда что-то пойдёт не так – звоните мне, не своему программисту, не в спортзал и не маме.

Короткие вопросы по теме

Все вопросы по теме "Безопасность сайта"

Заявка

Обсудим задачу

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

Куда удобнее ответить?