
Atak trochę na wnuczka: fałszywe powiadomienie o podatności WordPressa CVE-2023-46182 prowadząca do przejęcia kontroli nad stroną
Od wczoraj zgłaszają się do nas klienci, którzy otrzymali nieco podejrzaną wiadomość e-mal. Wiadomość ta pochodzi niby od WordPress.org i informuje o wykrytej nowej podatności oraz jej łatce. Zachęca nawet do rozwiązania problemu. Czy to może być jakieś zagrożenie?
Przelewy24, czyli jak sprawić by Twoi klienci znienawidzili Twój sklep tuż po zamówieniu
W naszej pracy spotykamy się codziennie z wieloma problemami,z którymi muszą zmagać się właściciele jak również klienci sklepów internetowych. Czasem są one niewidoczne dla szarego kupującego, ale czasem uderzają w niego z tak dużą siłą, że nie pozostaje on obojętny.
Frustracja klienta narasta, a po osiągnięciu apogeum kupujący zazwyczaj obwinia za to sklep internetowy. Tak też było w tym przypadku, ale czy słusznie?
Tego się prosto nie odczaruje
Changelogi, diffy, podatności i inne zaklęcia
Co tam, panie, w biezpieczeństwie?
Podatność w WooCommerce Dynamic Pricing and Discounts, czyli znów import bez uprawnień
W ciągu ostatnich dwóch tygodni trafiło do nas kilka sklepów zainfekowanych przez podatność we wtyczce WooCommerce Dynamic Pricing and Discounts, więc warto chyba napisać o tym kilka słów.
Wtyczka WooCommerce Dynamic Pricing and Discounts pochodząca z Envato jest jedną z wielu tego typu wtyczek. Służy ona do ustalania dynamicznych rabatów i przecen dla produktów na podstawie różnych warunków. Trudno mi do końca powiedzieć, dlaczego ktoś miałby wybrać akurat tę wtyczkę, bo istnieje kilka świetnych darmowych alternatyw. Nie zmienia to jednak faktu, że wtyczka ta została kupiona prawie 20 tys. razy, więc jak na wtyczkę sklepową, jest dość popularna.
Wtyczka powstała w 2014 roku i przeglądając jej changelog, można odnieść wrażenie, że jest ciągle rozwijana, usprawniana i nie ma w niej prawie żadnych błędów. Opisy zmian są jednak bardzo lakoniczne, a to nigdy nie wróży niczego dobrego.
I tak po 7 latach istnienia wtyczki, 18 sierpnia wykryto w niej krytyczną podatność, która pozwala każdemu wykonać import ustawień. Nie wymaga to nawet zbyt wiele trudu.
Pechowa 13-tka, czyli krytyczna podatność wykryta w WooCommerce
13 lipca 2021 we wtyczkach WooCommerce i WooCommerce Blocks wykryte zostały krytyczne podatności. Podatność została zgłoszona w ramach programu HackerOne.
Błąd został już poprawiony i opublikowane zostały poprawione wersje. Tak – poprawka została opublikowana tego samego dnia. Jeśli więc używasz WooCommerce, to szybka aktualizacja do wersji 5.5.1 to dobry pomysł.
Pożar w serwerowni, czyli na co mi niezależny backup
Być może słyszałeś już kiedyś powiedzenie, że ludzie dzielą się na tych, którzy robią backupy i na tych, którzy zaczną je robić. Gdy jednak rozmawiam z klientami, często słyszę, że backupów własnej strony nie posiadają. Jedni w ogóle o tym nie pomyśleli. Inni zakładają, że hosting robi to za nich. I owszem – większość hostingów domyślnie wykonuje automatycznie kopie zapasowe, aby klienci nie musieli się tym martwić. Jednak czy aby na pewno tak jest?
Infekcja letsmakeparty3 / sdfsd234 – analiza przypadku
Bieżący tydzień minął nam pod znakiem czyszczenia infekcji na stronach, a szybkie wyszukanie jej oznak w Google pokazuje, że jeszcze sporo osób się z nią zmierzy. Opiszmy więc, jak ta infekcja wyglądała i jak działa.
Jak wygląda atak
Doklejenie szkodliwego kodu do pliku header.php
w motywie
W pliku header.php
doklejany jest następujący fragment kodu:
Flexible Checkout Fields – czego uczy nas podatność tej wtyczki?
No i doczekaliśmy się – mamy pierwszą (jeśli tytuł ten należy się komu innemu, to piszcie w komentarzach) dużą podatność z polskiej wtyczce, a „nagroda” ta wędruje do Flexible Checkout Fields od WP Desku. Przyjrzyjmy się, z czego owa podatność wynikała, jak została wykorzystana do atakowania stron i co zrobić, jeśli Twoja strona została zaatakowana…
ThemeGrill Demo Importer – czyli dlaczego nie zostawiać wtyczek zadaniowych na serwerze
Ten tydzień mija nam pod znakiem ratowania stron, które padły ofiarą ataku wykorzystującego podatność we wtyczce ThemeGrill Demo Importer. Przyjrzyjmy się, jak do tego całego zamieszania doszło i pomyślmy, jak można się było uchronić.
Zacznijmy od początku. ThemeGrill to firma tworząca motywy. Ich wtyczka Demo Importer miała ponad 200 tys. aktywnych instalacji, a na początku lutego WebARX znalazł w niej podatność, która pozwala zresetować bazę danych i hasło administratora każdej stronie, która z tej wtyczki korzysta.
Czy cache’owanie może pogorszyć wydajność strony?
Podobno, jeśli tytuł artykułu zawiera pytanie, to odpowiedź na nie zazwyczaj brzmi „Nie”. W przypadku tego zagadnienia jest jednak inaczej…
Tak, cache’owanie może pogorszyć wydajność strony. Może wręcz tę stronę zabić. Przekonał się o tym właściciel pewnego sklepu internetowego.
Sytuacja, jakich wiele. Klient znalazł wykonawcę, zamówił sklep i go otrzymał. Strona zbudowana została na gotowym motywie. Gdy klient chciał jakąś dodatkową funkcjonalność, wykonawca znikał na jakiś czas, testował kilka wtyczek (wszystko na stronie produkcyjnej), a następnie zostawiał tę, która sprawdzała się najlepiej. Brzmi znajomo, prawda?
(więcej…)Jak zielona jest Twoja strona
WordUp Trójmiasto #15: Aktualizacje – odczarujmy i poczarujmy
A prezentacja dostępna tutaj:
Aktualizacje: odczarujmy i poczarujmy
Aktualizacje: odczarujmy i poczarujmy
WordCamp Košice 2019: Should I update/wait/ignore?
A prezentacja (w wersji polskiej) dostępna tutaj:
Aktualizować, zwlekać, ignorować?>
Przechowywanie adresu IP użytkowników i jak tego nie robić
Na blogu dev.WPZlecenia pojawił się wczoraj artykuł sponsorowany pokazujący, jak nauczyć WordPressa przechowywania adresów IP logujących się użytkowników. Ponieważ komentarz do niego może być długi, a będzie miał też całkiem fajną wartość merytoryczną, to postanowiłem opublikować go tutaj. Zobaczymy sobie przy okazji, jak zły kod można krok po kroku poprawić.
Przede wszystkim trudno byłoby mi zaufać hostingowi, który wsadza mnie na minę i zapomina wspomnieć o ważnych konsekwencjach. Po pierwsze, jeśli chcemy sobie zapisywać adres IP, to ze względu na RODO musimy poinformować o tym fakcie użytkowników, wpisać to w regulamin i politykę prywatności, a przede wszystkim – uzasadnić potrzebę przechowywania tej właśnie informacji (bo nie możemy zbierać i przetwarzać danych bez uzasadnienia).
Natomiast, jeśli już przebrniemy przez całe to prawnicze zamieszanie… Fajnie byłoby robić to kodem, który ma ręce i nogi.
Czar metalowych czcionek
WordCamp Poznań 2018: Czar metalowych czcionek
A prezentacja dostępna tutaj:
Czar metalowych czcionek
Aktualizować, zwlekać, ignorować?
Czy (i jak) Gutenberga da się wyłączyć?
Od jakiegoś czasu o Gutenbergu robi się coraz głośniej. Także na WordUpach. Najczęstsze pytanie, jakie pada w kontekście prezentacji na ten temat brzmi „A czy ja muszę go używać? Czy można go będzie jakoś wyłączyć?”
Odpowiedzi słyszałem już różne… Że się nie da, bo Gutenberg będzie domyślnym edytorem i zostanie na nas wymuszone jego używanie. Że będą (już są) wtyczki, które pozwalają go wyłączyć. Itd…
Ale czy naprawdę pozostanie przy starym edytorze jest aż tak skomplikowane? Przecież mowa o WordPressie – na pewno jest do tego jakiś filtr 😉
Filtry, wtyczki, obiekty i inne czary
Zagrożenia REST API
WordCamp Gdynia 2016: Kreatywne czary z filtrami i akcjami
A prezentacja dostępna tutaj:
Kreatywne czary z filtrami i akcjami
Kreatywne czary z filtrami i akcjami
7 grzechów głównych twórców motywów (okiem programisty)
WordCamp Kraków 2015: Mity (nie)bezpieczenstwa
A prezentacja dostępna tutaj:
Mity (nie)bezpieczenstwa
Jak przetworzyć dane przesłane formularzem ContactForm7
ContactForm7 to chyba najpopularniejsza wtyczka do łatwego tworzenia formularzy, które można umieścić na stronie. Nie jest ona co prawda najwygodniejsza, najładniejsza, ani najbardziej intuicyjna, ale z pewnością jest szybka, bezpieczna i spełnia swoją rolę. A co więcej – dzięki przemyślanemu użyciu filtrów i akcji, pozwala na łatwe modyfikacje i integracje.
Zacznijmy od najprostszej i chyba najczęściej spotykanej modyfikacji, czyli:
Jak zrobić coś dodatkowego (zapisać do pliku, zachować w bazie danych, utworzyć wpis, itd.) z przesłanym formularzem?
Dzięki temu, że autor wtyczki przewidział taką możliwość, dodanie takiej integracji jest stosunkowo proste. Spójrzmy na poniższy kod, który realizuje chyba najprostszą formę takiej integracji, a jednocześnie jest świetnym startem do budowania swoich, bardziej rozbudowanych wersji:
function prefiks_process_submitted_form_data( $contact_form ) {
$submission = WPCF7_Submission::get_instance();
if ( $submission ) {
// Pobierz dane przesłane w formularzu
$submitted_name = $submission->get_posted_data('your-name');
$submitted_email = $submission->get_posted_data('your-email');
$submitted_subject = $submission->get_posted_data('your-subject');
$submitted_message = $submission->get_posted_data('your-message');
// ...
// Zrób coś z danymi (np. zapisz je do pliku)
file_put_contents('cf7-log.txt', 'Data: ' . date('c') . "\n", FILE_APPEND);
file_put_contents('cf7-log.txt', "Autor: {$submitted_name} <{$submitted_email}>\n", FILE_APPEND);
file_put_contents('cf7-log.txt', 'Temat: ' . $submitted_subject . "\n", FILE_APPEND);
file_put_contents('cf7-log.txt', "Wiadomość:\n" . $submitted_message, FILE_APPEND);
file_put_contents('cf7-log.txt', "\n--------------------\n", FILE_APPEND);
}
}
add_action( 'wpcf7_before_send_mail', 'prefiks_process_submitted_form_data' );
WordUp Kraków #jesień 2015: Bezpieczny kod – Trzy nieco trudniejsze kawałki
A prezentacja dostępna tutaj:
Bezpieczny kod: trzy łatwe kawałki
Wyczaruj sobie spokój
Bezpieczny kod – Trzy nieco trudniejsze kawałki
Mity (nie)bezpieczeństwa
Jak wyświetlić różną liczbę wpisów na różnych stronach?
Tym razem pytanie od znajomego otrzymane bezpośrednio. Mam jednak wrażenie, że z tym problemem spotkać można się dość często, a dotyczy ono modyfikowania liczby wpisów wyświetlanych na stronie w zależności od tego, na jakiej stronie aktualnie jesteśmy. Ale po kolei – zacznijmy od pytania:
Na stronie głównej 4 wpisy wyświetlam w sliderze, a potem resztę normalnie. Co zrobić, żeby paginacja działała poprawnie?
Podejrzewam, że przyda się kilka dodatkowych wyjaśnień. Na czym dokładnie polegał problem? Otóż na stronie listy wpisów wyświetlane było po 9 wpisów. Na stronie głównej bloga, wyświetlać miał się slider, a w nim 4 najnowsze wpisy, a poniżej 9 następnych wpisów (razem 13). Gdy przechodziliśmy na stronę drugą, to niektóre wpisy wyświetlały się ponownie.
Jak to naprawić? Prosto – musimy wytłumaczyć WordPressowi, ile wpisów gdzie chcemy wyświetlać. A możemy to zrobić tak:
function prefiks_change_posts_number_on_home( $query ) {
if ( is_home() && $query->is_main_query() ) {
$posts_per_page = get_query_var('posts_per_page', get_option('posts_per_page')); // <- domyślna liczba wpisów do wyświetlenia
$additional_posts = 4; // <- liczba dodatkowych wpisów na pierwszej stronie
if ( get_query_var('paged') < 2 ) {
$query->set( 'posts_per_page', $posts_per_page + $additional_posts );
} else {
$query->set( 'offset', $additional_posts + ( get_query_var( 'paged' ) - 1 ) * $posts_per_page );
}
}
}
add_action( 'pre_get_posts', 'prefiks_change_posts_number_on_home' );
Jak zmienić domyślne ustawienia galerii?
Dzisiaj na jednej z grup supoprtowych pojawiło się następujące pytanie:
Czy da się jakoś zmienić domyślne ustawienia galerii? Chciałbym mieć na start ustawione 4 kolumny oraz linkowanie do pliku, a nie strony załącznika.
No właśnie, da się? Oczywiście, że się da, a potrzebujemy w tym celu poniższych dwóch funkcji:
function prefiks_print_media_templates() {
?>
<script>
jQuery(document).ready(function() {
_.extend(wp.media.galleryDefaults, {
columns: '4',
link: 'file',
});
});
</script>
<?php
}
add_action('print_media_templates', 'prefiks_print_media_templates');
function prefiks_shortcode_atts_gallery($out, $pairs, $atts) {
if ( ! array_key_exists('columns', $atts) || ! intval( $atts['columns'] ) ) {
$out['columns'] = 4;
}
if ( ! array_key_exists('link', $atts) ) {
$out['link'] = 'file';
}
return $out;
}
add_filter('shortcode_atts_gallery', 'prefiks_shortcode_atts_gallery', 10, 3);
Bezpieczny kod – Trzy łatwe kawałki
Wpis zabezpieczony hasłem z ignorowaniem wielkości liter
Dzisiaj na Facebookowej grupie WordPress PL pojawiło się następujące pytanie:
… potrzebuję plugin, który bez różnicy czy hasło będzie wpisywane z dużych czy z małych liter, będzie otwierał dostęp do danego wpisu, widział ktoś może coś takiego? …
Na pierwszy rzut oka takie rozwiązanie może wydawać się kiepskim pomysłem – przecież po to ustawiamy hasło, żeby dostanie się do wpisu nie było zbyt proste, a ignorując wielkość liter, dość znacznie zmniejszamy złożoność hasła. Ale… przecież ta złożoność nadal pozostanie całkiem spora, a poza tym, hasło do wpisu to nie to samo, co hasło do panelu administracyjnego.
Jeśli więc rzeczywiście mamy z tym problem i możemy w ten sposób ułatwić życie naszych czytelników, to czemu by nie spróbować? Tym bardziej, że wcale nie będzie to nas kosztowało tak wiele wysiłku.
Wystarczy użyć poniższego kodu (tym razem musimy użyć aż dwóch funkcji):
function prefiks_uppercase_post_password_before_hashing() {
if ( isset($_POST['post_password']) && ! empty($_POST['post_password']) ) {
$_POST['post_password'] = mb_strtoupper($_POST['post_password']);
}
}
add_action( 'login_form_postpass', 'prefiks_uppercase_post_password_before_hashing' );
function prefiks_uppercase_post_password_before_saving($data, $postarr) {
if ( array_key_exists('post_password', $data) && ! empty($data['post_password']) ) {
$data['post_password'] = mb_strtoupper($data['post_password']);
}
return $data;
}
add_filter( 'wp_insert_post_data', 'prefiks_uppercase_post_password_before_saving', 10, 2 );
Aktualizować, zwlekać, ignorować?
Jeśli używasz WordPressa dłużej niż miesiąc, to pewnie przynajmniej raz dowiedziałeś się o jakiejś aktualizacji – wtyczki, motywu, albo WordPressa. Oprogramowanie jest rozwijane, pojawiają się nowe funkcje, wykrywane są błędy, które następnie są poprawiane. W ciągu ostatnich 12 miesięcy sam WordPress zmieniał wersję 14 razy, choć dzięki aktualizacjom automatycznym, część z nich mogłeś przeoczyć (WordPress wykonał je za Ciebie).
Do tego, aby aktualizować zawsze i to jak najszybciej nawołują zazwyczaj autorzy, blogi i serwisy newsowe, a także Twój WordPress, który o aktualizacjach przypomina na każdym kroku i zachęca do nich informując, że to tylko jedno kliknięcie.
Jeśli dodatkowo uświadomisz sobie, że znakomita większość kodu związanego z WordPressem rozwijana jest jawnie – czyli każdy ma dostęp do kodu poszczególnych wersji, a w większości przypadków także do informacji o zawartych w nich poprawkach (czyli o błędach zawartych w wersjach poprzednich), to chęć jak najszybszej aktualizacji powinna być wręcz naturalna.
Tyle w teorii. A jak to wygląda w praktyce?
Jak ograniczyć użytkownikom dostęp do panelu wp-admin?
Wyobraźmy sobie, że tworzymy nieco bardziej rozbudowany serwis, w którym użytkownicy mogą się rejestrować. Niekoniecznie jednak chcemy, aby byli oni świadomi, że serwis bazuje na WordPressie, albo chcemy wzbogacić i uprzyjemnić ich doświadczenie i stworzyć specjalny panel administracyjny. Tak czy inaczej nie chcemy, aby „szwędali” się nam po panelu administracyjnym WordPressa.
Żeby nie rozmieniać się na drobne, załóżmy, że zalogowanych użytkowników, którzy mają określoną rolę (w naszym przypadku subscriber
, chcemy przekierowywać na określoną stronę (naszserwis.pl/uzytkownik/
). I taką właśnie funkcjonalność realizuje poniższy kod:
function prefiks_restrict_wpadmin_access() {
if ( ! defined('DOING_AJAX') || ! DOING_AJAX ) {
$user = wp_get_current_user();
if ( in_array('subscriber', $user->roles) ) {
wp_redirect( site_url( '/uzytkownik/' ) ); // <- dostosuj swoich potrzeb
die;
}
}
}
add_action( 'admin_init', 'prefiks_restrict_wpadmin_access' );
Jak zmusić WordPressa, aby szukał tylko po tytule wpisu?
Gdy korzystamy z parametru s
WP_Query, WordPress wyszuka wpisów, które zawierają podane słowa w tytule lub treści. Zdarza się jednak, że chcemy wyszukiwać jedynie po tytule (bo jest to w danej chwili bardziej intuicyjne – np. jeśli tworzymy pole z automatycznym podpowiadaniem tytułu wpisu).
Poniższy kod pozwoli nam osiągnąć właśnie taki efekt.
function prefiks_search_by_title_only( $search, $query ) {
global $wpdb;
if ( empty( $search ) ) {
return $search;
}
$q = $wp_query->query_vars;
$n = ! empty( $q['exact'] ) ? '' : '%';
$search = $searchand = '';
foreach ( (array) $q['search_terms'] as $term ) {
$term = esc_sql( like_escape( $term ) );
$search .= "{$searchand}($wpdb->posts.post_title LIKE '{$n}{$term}{$n}')";
$searchand = ' AND ';
}
if ( ! empty( $search ) ) {
$search = " AND ({$search}) ";
if ( ! is_user_logged_in() ) {
$search .= " AND ($wpdb->posts.post_password = '') ";
}
}
return $search;
}
add_filter( 'posts_search', 'prefiks_search_by_title_only' );
Jak wykluczyć wpisy z podkategorii w archiwum kategorii?
Czy zdarzyło Ci się kiedyś, że na stronie z rozbudowanym drzewem kategorii, chciałeś ukryć wpisy z podkategorii w widoku archiwum danej kategorii? Z pytaniem tym spotykam się dość często, więc może warto pokazać, jak należy się za to zabrać.
Poniżej znajdziesz kod, który realizuje dokładnie takie zadanie:
function prefiks_exclude_subcategory_from_category_archive( $query ) {
if ( !is_admin() && $query->is_main_query() && is_category() ) {
$category_not_in = $query->get( 'category__not_in', array() );
if ( $query->get('cat', false) ) {
$query->set('tax_query', array( array(
'taxonomy' => 'category',
'operator' => 'IN',
'terms' => $query->get('cat'),
'include_children' => false,
) ));
}
}
}
add_filter( 'pre_get_posts', 'prefiks_exclude_subcategory_from_category_archive' );
Różny wygląd wpisów z użyciem jednej pętli
Na pewno spotkałeś się ze stronami lub motywami, które wyświetlając listę wpisów, używają dla nich różnego wyglądu. Na przykład pierwsze dwa artykuły wyświetlane są jako duże zdjęcie i tytuł, dwa następne jako tytuł, małe zdjęcie i zajawka, a pozostałe – jako same tytuły. Poniżej przykład takiego bloku listy artykułów z portalu gazeta.pl.
Taki układ artykułów stosowany jest dość często i niestety równie często implementowany jest w nieprawidłowy/niewydajny sposób, co prowadzi do wielu zbędnych zapytań do bazy danych i spowolnienia działania strony. A jak zrobić to poprawnie i przy użyciu tylko jednej pętli?
Gdzie kupić domenę i nie przepłacić?
Do płacenia za domeny pewnie każdy już przywykł tak samo, jak do corocznych faktur za hosting. Jestem wręcz pewien, że z bardzo dużym prawdopodobieństwem, domenę kupiłeś właśnie od swojej firmy hostingowej i nawet nie sprawdzałeś oferty konkurencji.
A szkoda, bo z mojego doświadczenia wynika, że znakomita większość osób za domenę przepłaca i to bardzo słono…
Czy jednak oszczędność na domenie nie będzie wiązała się z jakimiś przykrymi skutkami? Wiemy przecież, że często nadmierne oszczędności źle się kończą. Podejrzewam, że mało kto będzie dziś próbował nadmiernie oszczędzać na hostingu…
I tu mam dobrą wiadomość. Pomijając bardzo skrajne przypadki, dostawcy domen sprzedają nam dokładnie tę samą usługę o dokładnie tej samej jakości i dokładnie tych samych dodatkach.
Skąd więc tak duże różnice w cenie? Wynikają one głównie z rabatów, które dany dostawca posiada przy rejestracji domen oraz od jego marży.
Warto zatem zapoznać się z poniższym zestawieniem cen rejestracji i odnowienia domen.
WordCamp Warszawa 2014: XML-RPC praktyczne czary
A prezentacja dostępna tutaj:
XML-RPC praktyczne czary
Jak usunąć wpisy z danej kategorii ze strony głównej?
Na forum i grupach WP często pojawia się pytanie o to, jak sprawić, aby wpisy z wybranej kategorii nie pojawiały się na stronie głównej. Jak to zrobić poprawnie? Wbrew pozorom nie jest to wcale takie trudne – realizuje to poniższy fragment kodu:
function prefiks_exclude_category_from_homepage( $query ) {
if ( !is_admin() && $query->is_main_query() && is_home() ) {
$category_not_in = $query->get( 'category__not_in', array() );
if ( is_scalar($category_not_in) ) {
$category_not_in = array( $category_not_in );
} elseif ( !is_array($category_not_in) ) {
$category_not_in = array();
}
$category_not_in[] = 3; // <- zmień 3 na ID kategorii, którą chcesz ukryć
$query->set( 'category__not_in', $category_not_in );
}
}
add_filter( 'pre_get_posts', 'prefiks_exclude_category_from_homepage' );