WordPress: 9 дірок, які знаходить наш аудит - і як їх закрити

3 хв читанняWordPressбезпекааудит

Не загальний список порад, а рівно те, що перевіряє наш безкоштовний аудит по кожному сайту. З кодом для кожного пункту.

Наш безкоштовний аудит перевіряє сайт за понад шістдесятьма пунктами, девʼять з них - про безпеку. Нижче всі девʼять, у тому порядку важливості, який використовує сам аудит: спочатку критичні, потім важливі, потім дрібні, але безкоштовні.

Критичні: знаходяться за хвилину скануванням

1. .env і .git відкриті назовні. У .env лежать паролі до бази й ключі платіжних систем, у .git - уся історія коду. Автоматичні сканери щодня перевіряють ці адреси на кожному сайті, який їм трапляється - питання не «чи знайдуть», а «коли».

<FilesMatch "^\.(env|git)">
    Require all denied
</FilesMatch>
RewriteRule ^\.git - [F,L]
На Apache/LiteSpeed - у .htaccess. Ключі, які вже там лежали, вважайте вже витеклими і перевипустіть.

2. http не переводить на https. Хто зайшов без шифрування, лишається на незахищеному звʼязку - у публічному Wi-Fi пароль і дані форми видно відкритим текстом. Google до того ж рахує http- і https-версію двома різними сайтами.

Виправлення - постійний 301-редірект на рівні сервера, а не в PHP: WordPress, завантажений лише для того, щоб перенаправити на себе ж, - зайва секунда на кожен неправильний запит.

Важливі: не критично, але дорога прогалина

3. /wp-json/wp/v2/users віддає логіни адміністраторів будь-кому. Половина злому - вгадати логін; тут його вгадувати не треба, WordPress називає його сам.

add_filter( 'rest_endpoints', function ( $endpoints ) {
    if ( is_user_logged_in() ) {
        return $endpoints;
    }
    unset( $endpoints['/wp/v2/users'] );
    unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    return $endpoints;
} );

4. Немає HSTS. Без цього заголовка перший захід на сайт усе одно йде відкритим http, навіть коли редірект на https налаштований, - і саме той перший запит можна перехопити. Браузер має знати наперед, що сюди можна заходити лише через https.

add_action( 'send_headers', function () {
    header( 'Strict-Transport-Security: max-age=15552000; includeSubDomains' );
} );
max-age у секундах - тут пів року, мінімум, який має сенс.

5. Сайт можна вбудувати в чужу сторінку. Без заборони на фрейми хтось інший вставляє ваш сайт у невидимий iframe і накриває його своїми кнопками: людина думає, що тисне у вас, а тисне в них. Для кабінету чи форми замовлення це вже гроші, а не абстрактний ризик.

add_action( 'send_headers', function () {
    header( 'X-Frame-Options: SAMEORIGIN' );
} );

6. Куки сесії без Secure і HttpOnly. Без HttpOnly куку прочитає будь-який сторонній скрипт на сторінці, без Secure вона їде відкритим каналом. Якщо це кука сесії адміністратора - украсти її означає зайти на сайт замість нього.

Тут є пастка, характерна саме для WordPress за проксі чи CDN: ядро вирішує, чи ставити прапорець Secure, за функцією is_ssl(), а вона дивиться на $_SERVER['HTTPS']. Якщо TLS завершується на CDN, а не на самому сервері, WordPress цього не бачить і тихо не додає Secure, навіть коли відвідувач насправді на https.

if ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
    && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
    $_SERVER['HTTPS'] = 'on';
}
На самому початку wp-config.php - до того, як щось інше встигне запитати is_ssl().

Дрібні: але безкоштовні

7. readme.html і xmlrpc.php відкриті. readme.html прямо називає версію WordPress - скануванню лишається звірити її зі списком відомих дірок. xmlrpc.php дозволяє перебирати сотні паролів за один запит.

Той самий фільтр, що ми давали в статті про швидкість заради економії запиту, тут працює заради безпеки - вимкнути xmlrpc, якщо ним нічого не користується.

add_filter( 'xmlrpc_enabled', '__return_false' );

8. Сервер називає свою версію. Заголовок на кшталт X-Powered-By: PHP/7.4 підказує зловмиснику, який саме набір відомих вразливостей пробувати першим. Не діра сама по собі - безкоштовна підказка.

9. Немає Content-Security-Policy. CSP - це список того, звідки сторінці дозволено вантажити скрипти. Без нього чужий скрипт, що потрапив через вразливий плагін чи скомпрометовану рекламну вставку, виконується нарівні з вашим власним кодом.

CSP - єдиний пункт із девʼяти, який не можна просто увімкнути: спершу треба зібрати, які домени сайт справді використовує. Почніть з Content-Security-Policy-Report-Only, подивіться в консоль браузера тиждень, і лише тоді вмикайте по-справжньому.

9перевірок безпеки в нашому безкоштовному аудиті - усі дев'ять із цього поста

Жодного з цих дев'яти пунктів не видно на головній сторінці - вони не впливають на те, як сайт виглядає, тому їх ніхто не помічає, поки не стане пізно. Саме тому аудит перевіряє їх автоматично, а не сподівається, що хтось згадає про це сам.