Коллега подкинул халтурку – вычистить любительского проекта на движке mediawiki вирусы и контент, добавленный спамерами и быдлохакерами.
История проста – сайт “Энциклопедии Подмосковья” на базе mediawiki несколько лет назад сломали (то ли через эксплойт, то ли через открытую кому попало регистрацию), потом боты надобавляли туда всякой ереси… а потом хостер отключил сайт за рассылку спама и вирусную активность.
Чисткой вирусов я решил не заниматься – смысл, если все равно движок будем обновлять до latest-версии вместе с переездом на VPS?
Занялся базой.
Разумеется, бэкап! 2.2 гектара sql-кода. Я малость прифигел, залил файл к себе на хост и полез разбираться:
table_name | Size (Kb) | Rows count |
---|---|---|
externallinks | 43542.2 | 311138 |
page | 9708.5 | 121542 |
revision | 52002.0 | 566351 |
text | 1176187.4 | 566397 |
user | 598265.4 | 804910 |
Есть и другие таблицы с паразитными данными, но это самое объёмное.
Как видно, объемы паразитных данных таковы, что без автоматизации и анализа структуры базы не обойтись. Я пошел по пути наименьшего сопротивления и пошел читать доку Mediawiki: Руководство:Макет базы данных. А потом стал составлять запросы.
Начинается все, очевидно, с пользователей (user
):
DELETE FROM `user` WHERE user_id > 8; OPTIMIZE TABLE `user`
Теперь удаляем из таблицы с ревизиями страниц (revision
) только те ревизии, которые сделали пользователи, не найденные в таблице user
(то есть сделанные спамерами, именно их мы удалили предыдущим запросом). Связь: revision.rev_user -> user.user_id
. Согласно документации, пользователь с rev_user
= 0 – системный, ревизии, сделанные от его лица надо отставить в неприкосновенности.
DELETE FROM `revision` WHERE `rev_user` != 0 AND `rev_user` NOT IN ( SELECT u.user_id FROM user u ); OPTIMIZE TABLE `revision`;
Теперь чистим таблицу заголовков страниц (page
). Удаляем страницы, идентификаторы которых не найдены в таблице ревизий ( page.page_id <=> revision.rev_page
):
DELETE FROM page WHERE page_id NOT IN ( SELECT r.rev_page FROM revision r ); OPTIMIZE TABLE page;
Теперь чистим таблицу с контентом страниц (`text`) , логика удаления аналогична предыдущей, только связь идёт по `text.old_id <=> revision.rev_text_id`.
DELETE FROM `text` WHERE old_id NOT IN ( SELECT r.rev_text_id FROM revision r GROUP BY rev_text_id ORDER BY rev_text_id ); OPTIMIZE TABLE `text`;
Это очень неторопливый запрос, у меня он отрабатывал часа полтора, хотя на SSD-дисках будет в разы быстрее. Думаю, его можно было бы соптмизировать через вставку в новую таблицу тех и только тех строк из старой, которые имеют ревизию, но я не стал проверять.
Теперь чистим таблицу `pagelinks` по связи `pagelinks.pl_from <=> page.page_id`
DELETE FROM pagelinks WHERE pl_from NOT IN ( SELECT p.page_id FROM page p ); OPTIMIZE TABLE pagelinks;
Чистим таблицу `recentchanges` по связи `по связи recentchanges.rc_user <=> user.user_id`
DELETE FROM recentchanges WHERE rc_user NOT IN ( SELECT u.user_id FROM `user` u ); OPTIMIZE TABLE recentchanges;
Чистим таблицу `watchlist` по связи `watchlist.wl_user <=> user.user_id`
DELETE FROM watchlist WHERE wl_user NOT IN ( SELECT u.user_id FROM user u ); OPTIMIZE TABLE watchlist;
Чистим таблицу externallinks
по связи externallinks.el_from <=> page.page_id
DELETE FROM externallinks WHERE el_from NOT IN ( SELECT p.page_id FROM page p ); OPTIMIZE table externallinks;
Это тоже довольно долгий запрос, но с чистой text не сравнить.
Теперь чистим таблицу `imagelinks` по связи `page.page_id <=> imagelinks.il_from`
DELETE FROM imagelinks WHERE il_from NOT IN ( SELECT p.page_id FROM page p ); OPTIMIZE TABLE externallinks;
Таблицы кэшей и индексов можно просто обнулить:
TRUNCATE TABLE searchindex; TRUNCATE TABLE objectcache;
После этих несложных манипуляций БД уже похожа на правду:
table_name | Size (Kb) | Rows count |
---|---|---|
externallinks | 2.9 | 24 |
page | 99.4 | 1161 |
revision | 331.9 | 1868 |
text | 24303.7 | 1864 |
user | 8.3 | 8 |
Или в SQL-дампе ~25 мегабайт. Придется еще подшлифовать ручками – удалить записи из page, text, revision, появившися, похоже, в результате взлома аккаунта админа.
В следующей серии – установка mediawiki на VPS и апгрейд имеющейся вики.
Отправить ответ