PHP левелап (5.x ==> 7.x). Проблемы и решения

Вордпресс давно горит красненьким: обновите свой PHP. Ну и хрен с тобой — гори!

Но тут, как беременному, приспичило селёдки перевести WP-сайты на статику и убрать WP с сервера. А модный плагин WP2Static в открытую шантажирует – работаю только на PHP 7.

Ну давай, блин, обновляться!..

Начал с локальной машины, где всё крутится на Denwer 3 (A2.2.22, P5.3.13, M5.5).

Сначала накатил PHP 5.6.19 + Apache 2.40 отсюда и затем повысил PHP до 5.6.40 из архива дистрибутивов по этой инструкции (ну а вдруг?! 😉 ). Вордпресс не оценил.

Оокей! Едем дальше.

Тогда старый сервер отправил в архив и перешёл на новую сборку Denwer (Apache 2.4, PHP 7, MySQL 5.7).

Вордпресс обновился, WP2Static подтянулся, тестовый сайт загнал в статику.

Супер, чо!

Но голос капитана Зелёного в моей голове:

И понеслась… 😀

Запуск OpenGoo 1.3.1 в 2019

Нежно люблю дерево задач из древнего OpenGoo.

В своё время искал, перепробовал многое, но остановился на этом комбайне, из которого юзаю одну только функциональность – дерево задач.

Оно умеет назначать задачи разным юзерам, выращивать их деревом, создавать шаблоны из выращенных деревьев.

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

Хреновина ресурсоёмкая, по нагрузке на сервер не супер-оптимизированная, насколько я могу судить по периодическому 500 ответу сервака.

Но вот привык.

А на новом сервере оно не взлетело.

Суть, как и во многом далее – в использовании в OpenGoo устаревшего драйвера mysql (который в PHP7 выпилили) и отсутствие обратной совместимости с новым mysqli, который идёт с PHP 7.

ОКей, починяю этот примус и запускаю древнючий OpenGoo 1.3.1 в 2019 году 😉 :

    1. Перевёл все обращения к MySQL базе под формат mysqli. Для этого установил на сервер скрипт, путь к директории – относительно сервера:
      /home/бла-бла-бла/convert
    2. Указал в конфиге OpenGoo использовать mysqli адаптер, вместо mysql. По инструкции:
      define('DB_ADAPTER', 'mysqli');
    3. Ну и собсно добавил сам адаптер-класс в папку
      /opengoo/environment/library/database/adapters

И оно завелось (на самом деле нет).

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

Запуск GeoIP для PHP5

yum install php-pear gcc make php-devel – установит php-pear пакет для компилирования исходников

yum install GeoIP GeoIP-data GeoIP-devel – установит пакеты geoip

pecl install geoip – соберёт PECL библиотеку geoip.so
echo "extension=geoip.so" > /etc/php.d/geoip.ini – включит библиотеку после рестарта апача.

Если есть альтернативный PHP (у меня это PHP 7.3), то под него собираем свой модуль:

/opt/php73/bin/pecl install geoip-1.1.1 – соберёт PECL библиотеку geoip.so

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

Google PageSpeed Insights : и снова здравствуйте!

Было уже. Раз и два

Тут что-то зашёл снова и приуныл: 52/100.

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

Фонт блокирует отображение содержимого, написаного им, пока не прогрузится сам, а ютюб подгружает js код какого-то дикого объёма. Всё это фигачится с разных узлов и тормозит мои страницы.

Надо решать.

font-display : swap

В 2019-ом с фонтом всё оказалось просто. Гугл добавил поддержку конструкции font-display:swap в свой css загрузки шрифта. Инструкция говорит браузеру не дожидаться загрузки шрифта, а отображать текст, следующим по списку шрифтом. Типа некогда ждать, пиши, чем есть, потом перепишем. 😉

Всё, что требуется – это добавить ключ &display=swap при описании шрифта. В моём styles.css это так:

@import url('https://fonts.googleapis.com/css?family=Roboto:100,400&display=swap&subset=cyrillic');

Если теперь открыть этот .css, то увидим инструкцию
font-display:swap. PageSpeed её тоже увидит.

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

Блокировка страницы из-за YouTube фрейма

Здесь уже приходится делать подъём с переворотом. ;(

Суть рекомендаций в сети: в div блоке отображаем миниатюру нужного видоса, а при клике по нему javascript’ом подменяем картинку фреймом с YouTube-роликом.

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

Проблема здесь в том, что современные браузеры игнорируют ключ autoplay=1 и не стартуют видос при замене pic на frame. Для запуска видоса приходится дважды жамкать на значок play: для замены pic на frame и второй раз для запуска видоса в этом frame. Это конечно понижает опыт общения юзера с сайтом. ;(

У себя кастомизировал вот этот рецепт

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

Полезные ссылки

Минимайзер и бьютификатор всего: html, css, js –
FREEFORMATTER.COM

Упаковщик .png и .jpg файлов – https://tinypng.com

Как применить антиспам фильтр Thunderbird ко всем учётным записям сразу

А никак!

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

У меня в Thunderbird заведено 8 учётных записей. Есть фильтр по содержанию в теме. В нём что-то около 30 стопслов. Я приуныл, осознав, что придётся заново воссоздавать его ещё в семи учётках. Погуглил на эту тему…

И да, есть способ проще – скопировать фильтры из одной учётки в другую!

Как скопировать антиспам фильтр в Thunderbird

Суть в том, что файл с фильтрами (msgFilterRules.dat) создаётся отдельно для каждой учётки в её папке (ThunderbirdFolder\Data\profiles\ImapMail\MailServerName) и для локальной папки (ThunderbirdFolder\Data\profiles\Mail\Local Folders).

Остаётся в текстовом редакторе скопировать нужный фильтр (ограничен строками name= и condition=) из учётки, где он уже настроен в остальные учётки:

 

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

Ещё нюанс. Скопировал фильтр, а оно при запуске пишет – “фильтр не работает, потому что файл не читаем, Thunderbird создал новый”. 

Воттакак?!

Оказалось проблема с кодировкой (в стопсловах есть кириллица). Некоторые учётки используют Windows-1251, а некоторые UTF-8. Перевёл правила в UTF-8 и всё поехало, как надо.

Осталось два вопроса. Если кто даст ответ – будет здорово!

  1. Что за хрень с кодировками в разных учётках? Подозреваю из-за этого не работает и поиск по архивам сообщений.
  2. Как указать в Thunderbird, чтобы он отфильтрованные сообщения килял прямо на сервере, не подгружая локально и не тратя мой мобильный трафик?

 

Полуавтоматическое продление сертификатов Let’s Encrypt

Минуло 90 дней или все гвозди забиваем в последний день. 😉

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

Оказалось всё просто:

  • открываем консоль. Я открываю из Bitvise SSH, потому что NetBox для FarManager, к сожалению, виснет
  • переходим в директорию с установленным certbot. У меня это httpd-cert в директории с web-юзерами
  • выполняем команду ./certbot-auto renew

Это всё. Оно само прошлось по созданным ранее сертификатам и обновило их и ключи:Да, не понял почему, но вызов certbot-auto без ./ вызывало ошибку command not found. Так что их наличие – это важно!

…надо бы в cron команду прописать, но что-то стало лениво – впереди для этого целых 90 дней! 😀

NB: И, блин, перезапусти после этого Nginx!

service nginx restart

А то ключи новые лежат, а сервак браузеру старые отдаёт, сайты недоступны, а ты – тормоз.

Я взломал вашу MySQL базу. Доброжелатель

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

На скринах, в обрезанном интерфейсе неизвестной мне программы, были данные из базы одного из моих проектов.

Дениса занервничал. 😁

Темой безопасности и уязвимостей на сайтах, признаться, никогда особо не интересовался. Слышал, что-то конечно про SQL-injection, что-то читал про DDoS , но в голову не брал. А тут нате вам – приплыли.

Пришлось пройти и этот квест.

Причина, кстати, в расπздяйстве (фильтруйте и проверяйте параметры в URL) и закономерному при этом наказании — MySQL-инъекции. 😉

Взламываю свою базу данных MySQL

Поломана MySQL-база. Значит там и проблема. Это раз.

Данные в интерфейсе какой-то программы с обрезанным названием. Значит процесс автоматизирован. Это два.

Загуглил про SQL-инъекции и вот оно – то, что нужно. Статья старая, но расписано как надо и в картинках для чайников. Стало понятно, как злоумышленник запускает нужные SQL-запросы – через оператор UNION, прилепляемый к параметру в URL страницы сайта.

Открыл сырые логи сайта — ~500 обращений к сайту с IP моего доброжелателя с использованием этой конструкции:

Здесь можно бы и остановиться — причина понятна, пофиксить и забыть.

Да, но нет!

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

Безуспешно гуглил по всякому, а когда отчаялся и решил искать комплексные решения анализа уязвимостей сайта, в рекомендациях Netsparker’а увидел знакомый скриншот…

SQLi Dumper – та самая программа для поиска и взлома уязвимых баз данных MySQL.

Как оказалось, с этой программой, процесс взлома «дырявых» баз и просмотр данных из них доступны любому идиоту. Софтина сама находит дураков и показывает их данные в пару кликов.

У меня цель была другая – исследовать свой сайт.

Вставил URL с параметром из логов, нажал GO и GoToDumper, когда она немного потрепавшись с сервером нашла уязвимость. Потом Get Databases, Get Tables, Get Columns, Dump Data. И да, вот они мои данные, полученные через backdoor:

Блокирую взлом базы данных MySQL

Суть взлома, как по книжке: параметр из URL (это ID поля из базы) без проверок и фильтров прямиком идёт в SQL запрос. Злоумышленник через оператор UNION цепляет к нему свой SQL запрос и сервер его послушно выполняет.

То есть вместо валидного обращения к новостной статье #314:

https://apasscracker.com/news/shownews.php?newsid=314

получается запрос:

https://apasscracker.com/news/shownews.php?newsid=314999999.9' union all select 1,2,3,4,5,[t],7,8,9,10,11,12,13 and '0'='0

которым SQLi Dumper и раскручивает базу дальше.

Поскольку, по логике алгоритма, ID – это всегда целочисленное значение, то закрыть эту конкретную дырку элементарно — просто в скрипте явно приводим полученный из URL параметр к целочисленному значению:

$id=intval($id);

Всё!

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

Вместо эпилога

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

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

PS: и ещё прочтённый за эти дни ништячок — как узнать, что сервер DDoS’ят.

Удаление ВСЕХ ожидающих комментариев в WordPress

Зашел в панель, а у меня там полторы тысячи комментов на модерацию – #спамернедремлет! 🙂

Хотел было плагины для массового удаления комментариев из WordPress поизучать, поставить, запустить, но решил обойтись простым MySQL запросом:

DELETE FROM  wp_comments WHERE comment_approved =0

Дешево и сердито! 🙂

При апруве комментариев их атрибут  “comment_approved” меняется на “1”, поэтому они остались невредимыми.

А вот новые удалились ВСЕ ПОЛНОСТЬЮ! Следи за этим: если в комментариях может быть что-то ценное, то такой радикальный способ не подойдёт.

Google PageSpeed Insights : Оптимизируйте код JavaScript и CSS

Продолжаю собирать монетки. Было 77/100. Станет 95/100. 😉

“Удалите из верхней части страницы код JavaScript и CSS, блокирующий отображение,” – говорит нам Гугль.

Ерунда какая!

Сам же гугль хочет responsible design видеть. А он строится на собственных стилях шаблонов и стилях фрэймворков (в моём шаблоне это Bootstrap).

Если все стили оптом отправить в ссылку к </body>, то плывёт отображение страницы.

Решение в исходнике самого Google:

#стилиоднойстрокойТо есть я тупо запилил все критические стили в строку и прописал их явно в хидер шаблона. 150Kb текста в шаблоне выглядят дико, но монетки от PageSpeed Insights я получил – 95/100.

Для минимизации и обратного форматирования .css использовал Dan’s Tools. Там, кстати, много нужных штук.

Да, а все JavaScripts, второстепенные CSS и линки на GoogleFonts были сосланы в низ страницы, к закрывающему тегу.

Работает.

UPD: Минимайзер Дэна жмёт не максимально, как оказалось. Вот этот пакует лучше.

Google PageSpeed Insights : Используйте кеш браузера

Прогибаемся под рекомендации Большого Брата 😉

Для сервера на Apache есть два метода, за каждый из которых отвечает свой модуль – mod_headers или mod_expires. Я использовал mod_headers.

По совету отсюда, прописал в .htaccess конструкцию:

<ifModule mod_headers.c>
<FilesMatch “\.(js|css|txt)$”>
Header set Cache-Control “max-age=604800” #храним неделю
</FilesMatch>
<FilesMatch “\.(flv|swf|ico|gif|jpg|jpeg|png)$”>
Header set Cache-Control “max-age=2592000” #храним месяц
</FilesMatch>
<FilesMatch “\.(pl|php|cgi|spl|scgi|fcgi)$”>
Header unset Cache-Control #запрещаем кэширование
</FilesMatch>
</IfModule>

Прописал, а оно – чик и не работает. 😉

Оказалось, что сам модуль на сервере не установлен. Установил mod_headres и перезапустил Apache, как рекомендовано здесь:

1. a2enmod headers – включил модуль

2. service apache2 restart – перезапустил службу Apache

Удовлетворённость PageSpeed Insights для мобильника сайтом составила 77/100

Оптимизирую дальше

UPD: “А какого править .htaccess для каждого сайта-то?” – подумал я и запузырил конструкцию <IfModule mod_headers.c> в конфиг файл самого апача. Перезапустил его и все сайты сервера отправляют одинаковые директивы.

Как попасть в выдачу Гугля за 30 минут?

Элементарно!

В панели Google Webmaster Tools у нужного сайта в разделе Сканирование -> Просмотреть как Googlebot ввести адрес новой страницы и жамкнуть Добавить в индекс.

Войля: не прошло и часу – страница уже там 😉