Wstęp: Obietnica AI vs Rzeczywistość WordPressa
Wykorzystanie modeli językowych (LLM), takich jak GPT-4, Claude 3.5 Sonnet czy Gemini Pro, do pisania kodu wtyczek dla WordPressa stało się codziennością wielu programistów i właścicieli stron. Wizja jest kusząca: opisujesz słownie potrzebną funkcjonalność, a sztuczna inteligencja generuje gotowy plik PHP w kilkanaście sekund.
Problem polega na tym, że modele językowe doskonale znają strukturę składniową PHP, ale nie rozumieją intencji bezpieczeństwa ani specyfiki działania ekosystemu WordPressa. Uczą się one na gigantycznych zbiorach publicznego kodu, w których znajdują się miliony przestarzałych, niezabezpieczonych wtyczek napisanych w ciągu ostatnich dwudziestu lat. W rezultacie AI generuje kod, który wygląda na poprawny, przechodzi testy działania (funkcjonalność działa), ale pod spodem zawiera krytyczne luki bezpieczeństwa.
Jako inżynierowie WordPressa musimy stosować zasadę ograniczonego zaufania (Zero Trust) wobec każdego fragmentu kodu wygenerowanego przez AI. Niniejszy przewodnik opisuje pięć najpowszechniejszych wzorców błędów bezpieczeństwa, które regularnie wykrywamy podczas audytów kodu stworzonego przez sztuczną inteligencję, wraz z konkretnymi przykładami podatnego kodu oraz metodami ich naprawy zgodnie z WordPress Coding Standards (WPCS).
1. Mylenie is_admin() z weryfikacją uprawnień
To prawdopodobnie najczęstszy błąd logiczny popełniany przez AI przy próbie zabezpieczenia kodu. Kiedy poprosisz model o zabezpieczenie funkcji tak, aby tylko administrator miał do niej dostęp, AI bardzo często generuje warunek bazujący na funkcji is_admin().
Dlaczego to nie działa?
Wbrew swojej nazwie, funkcja is_admin() nie weryfikuje, czy aktualnie zalogowany użytkownik jest administratorem systemu. Sprawdza ona jedynie, czy aktualne zapytanie dotyczy strony wewnątrz panelu administracyjnego (np. dowolnego adresu URL zaczynającego się od /wp-admin/).
Każde żądanie AJAX wysyłane do /wp-admin/admin-ajax.php — niezależnie od tego, czy wysyła je administrator, zalogowany subskrybent o najniższych uprawnieniach, czy nawet niezalogowany użytkownik — sprawi, że funkcja is_admin() zwróci wartość true.
Podatny kod wygenerowany przez AI:
// Przykład kodu wygenerowanego przez AI do obsługi zapisu konfiguracji wtyczki
add_action( 'admin_init', 'ai_save_plugin_settings' );
function ai_save_plugin_settings() {
// AI błędnie uważa, że to zabezpiecza przed nieautoryzowanym zapisem
if ( is_admin() ) {
if ( isset( $_POST['my_plugin_option'] ) ) {
update_option( 'my_plugin_option', $_POST['my_plugin_option'] );
}
}
}
Bezpieczny kod (poprawny audyt):
Aby poprawnie zweryfikować uprawnienia użytkownika, należy zastosować funkcję current_user_can(), przekazując do niej odpowiednią rolę lub — co jest znacznie bardziej zalecane — konkretną zdolność (capability), np. manage_options.
add_action( 'admin_init', 'secure_save_plugin_settings' );
function secure_save_plugin_settings() {
// 1. Zawsze sprawdzaj konkretne uprawnienia zalogowanego użytkownika
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Nie masz uprawnień do wykonania tej akcji.', 'secure-plugin' ) );
}
if ( isset( $_POST['my_plugin_option'] ) ) {
// Dodatkowo wprowadzamy sanityzację (patrz sekcja 4)
$sanitized_value = sanitize_text_field( wp_unslash( $_POST['my_plugin_option'] ) );
update_option( 'my_plugin_option', $sanitized_value );
}
}
2. Całkowity brak weryfikacji Nonce (CSRF)
Ataki typu Cross-Site Request Forgery (CSRF) polegają na zmuszeniu przeglądarki uwierzytelnionego użytkownika (np. administratora) do wykonania niechcianej akcji na stronie bez jego wiedzy. W ekosystemie WordPressa podstawową linią obrony przed CSRF są tokeny bezpieczeństwa zwane nonces.
Sztuczna inteligencja niemal nagminnie pomija generowanie i weryfikację nonces w formularzach administracyjnych, żądaniach AJAX oraz przy obsłudze endpointów REST API. Dzieje się tak, ponieważ weryfikacja nonces wymaga dodatkowego kodu i spójnego połączenia widoku (formularza HTML) z kontrolerem (odbiorcą żądania w PHP), co dla modeli LLM bywa trudne do zsynchronizowania w ramach jednej odpowiedzi.
Podatny kod wygenerowany przez AI (obsługa AJAX):
// Rejestracja endpointu AJAX przez AI
add_action( 'wp_ajax_ai_delete_post', 'ai_delete_post_handler' );
function ai_delete_post_handler() {
// AI sprawdza uprawnienia, ale całkowicie ignoruje CSRF (brak nonce)
if ( ! current_user_can( 'delete_posts' ) ) {
wp_send_json_error( 'Brak uprawnień.' );
}
$post_id = intval( $_POST['post_id'] );
wp_delete_post( $post_id );
wp_send_json_success( 'Wpis usunięty.' );
}
Bezpieczny kod (poprawny audyt):
Formularz wyjściowy musi generować pole nonce za pomocą funkcji wp_nonce_field(), a obsługa żądania PHP musi zweryfikować ten token przy użyciu check_ajax_referer() lub wp_verify_nonce().
// W widoku formularza lub skrypcie JS przekazującym dane
// wp_nonce_field( 'delete_post_action', 'my_nonce_field' );
// Rejestracja bezpiecznego endpointu AJAX
add_action( 'wp_ajax_secure_delete_post', 'secure_delete_post_handler' );
function secure_delete_post_handler() {
// 1. Walidacja CSRF przy użyciu check_ajax_referer
check_ajax_referer( 'delete_post_action', 'my_nonce_field' );
// 2. Walidacja uprawnień użytkownika
if ( ! current_user_can( 'delete_posts' ) ) {
wp_send_json_error( 'Brak uprawnień.' );
}
// 3. Walidacja i rzutowanie typu danych wejściowych
if ( ! isset( $_POST['post_id'] ) ) {
wp_send_json_error( 'Brak ID wpisu.' );
}
$post_id = absint( $_POST['post_id'] );
if ( wp_delete_post( $post_id ) ) {
wp_send_json_success( 'Wpis został bezpiecznie usunięty.' );
} else {
wp_send_json_error( 'Błąd podczas usuwania wpisu.' );
}
}
3. Podatności SQL Injection w zapytaniach do $wpdb
Gdy wtyczka potrzebuje komunikacji z bazą danych poza standardową klasą WP_Query, AI sięga po obiekt $wpdb. Niestety, mechanizmy LLM bardzo często zapominają o konieczności parametryzacji zapytań SQL, wstrzykując zmienne z tablic superglobalnych ($_GET, $_POST) bezpośrednio do zapytań SQL poprzez konkatenację ciągów tekstowych.
Dlaczego to niebezpieczne?
Bezpośrednie wstrzyknięcie wartości do zapytania SQL pozwala napastnikowi na zmianę logiki zapytania bazy danych. Może to prowadzić do odczytania haseł użytkowników, usunięcia całej bazy danych lub eskalacji uprawnień do poziomu administratora.
Podatny kod wygenerowany przez AI:
// Funkcja wyszukiwania użytkowników napisana przez AI
function ai_find_users_by_city( $city_name ) {
global $wpdb;
// Krytyczny błąd: bezpośrednie wstrzyknięcie zmiennej do zapytania SQL
$query = "SELECT * FROM {$wpdb->prefix}custom_users WHERE city = '" . $city_name . "'";
return $wpdb->get_results( $query );
}
Bezpieczny kod (poprawny audyt):
Do bazy danych nie może trafić żadne zapytanie zawierające dynamiczne zmienne, które nie zostało wcześniej przetworzone przez metodę $wpdb->prepare(). Metoda ta działa jak sparametryzowane zapytanie, podstawiając wartości pod znaczniki formatu i automatycznie je zabezpieczając.
function secure_find_users_by_city( $city_name ) {
global $wpdb;
// 1. Zabezpieczamy zapytanie używając prepare() ze znacznikami formatu
// %s oznacza ciąg znaków, %d oznacza liczbę całkowitą, %f oznacza zmiennoprzecinkową
$query = $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}custom_users WHERE city = %s",
$city_name
);
return $wpdb->get_results( $query );
}
Dodatkowo, jeśli zmienna $city_name pochodzi bezpośrednio z żądania użytkownika, przed przekazaniem jej do funkcji należy ją oczyścić (np. za pomocą sanitize_text_field()).
4. Brak sanityzacji wejścia i eskapowania wyjścia (XSS)
Cross-Site Scripting (XSS) to jedna z najpowszechniejszych podatności we wtyczkach WordPressa. Polega ona na wstrzyknięciu złośliwego kodu JavaScript na stronę internetową, który jest następnie wykonywany w przeglądarce innych użytkowników.
Modele AI często mylą sanityzację (czyszczenie danych przed zapisem) z eskapowaniem (zabezpieczanie danych podczas wyświetlania). Czasami AI stosuje sanityzację na wejściu, ale całkowicie zapomina o eskapowaniu na wyjściu, co i tak pozostawia otwartą furtkę dla ataków typu Stored XSS.
Podatny kod wygenerowany przez AI:
// Przykład formularza opinii generowanego przez AI
function ai_display_user_feedback() {
$feedbacks = get_option( 'ai_user_feedback_list', [] );
echo '<div class="feedback-list">';
foreach ( $feedbacks as $feedback ) {
// Krytyczny błąd: bezpośrednie wyświetlenie danych bez eskapowania
echo '<p class="feedback-item">' . $feedback['user_comment'] . '</p>';
}
echo '</div>';
}
Bezpieczny kod (poprawny audyt):
Złota zasada bezpieczeństwa WordPressa brzmi: Sanitize Early, Escape Late (sanityzuj jak najwcześniej, eskapuj jak najpóźniej — tuż przed samym wyrenderowaniem kodu HTML).
Do eskapowania danych na wyjściu stosujemy wyspecjalizowane funkcje:
esc_html()— dla zwykłego tekstu wewnątrz tagów HTML.esc_attr()— dla wartości atrybutów HTML (np.value="",alt="").esc_url()— dla adresów URL.wp_kses()— gdy dopuszczamy bezpieczne tagi HTML (np. pogrubienie, linki).
function secure_display_user_feedback() {
$feedbacks = get_option( 'secure_user_feedback_list', [] );
echo '<div class="feedback-list">';
foreach ( $feedbacks as $feedback ) {
// 1. Zabezpieczamy dane wyjściowe odpowiednią funkcją eskapującą
$comment = isset( $feedback['user_comment'] ) ? $feedback['user_comment'] : '';
echo '<p class="feedback-item">';
echo esc_html( $comment );
echo '</p>';
}
echo '</div>';
}
5. Podatności typu Arbitrary File Upload (Niebezpieczne przesyłanie plików)
Umożliwienie użytkownikom przesyłania plików (np. formularze kontaktowe z załącznikami, systemy wgrywania avatarów) to jedno z najbardziej krytycznych miejsc na stronie. Błąd w tym obszarze pozwala na wgranie na serwer pliku o rozszerzeniu .php, co skutkuje całkowitym przejęciem kontroli nad serwerem (Remote Code Execution - RCE).
Sztuczna inteligencja generując kod przesyłania plików, często opiera się na natywnych funkcjach PHP takich jak move_uploaded_file(), sprawdzając rozszerzenie pliku za pomocą prostych operacji na ciągach znaków, które można łatwo obejść (np. poprzez przesyłanie plików z rozszerzeniami file.php.jpg lub file.phtml).
Podatny kod wygenerowany przez AI:
// Obsługa przesyłania plików napisana przez AI
function ai_handle_avatar_upload() {
if ( isset( $_FILES['avatar'] ) ) {
$file_name = $_FILES['avatar']['name'];
$ext = pathinfo( $file_name, PATHINFO_EXTENSION );
// AI uważa, że to weryfikuje typ pliku
if ( in_array( $ext, ['jpg', 'jpeg', 'png'] ) ) {
$target = wp_upload_dir()['path'] . '/' . $file_name;
move_uploaded_file( $_FILES['avatar']['tmp_name'], $target );
}
}
}
Bezpieczny kod (poprawny audyt):
W WordPressie należy bezwzględnie unikać bezpośredniego korzystania z move_uploaded_file(). Zamiast tego należy używać wbudowanych funkcji bezpieczeństwa, takich jak wp_handle_upload(), która automatycznie weryfikuje prawdziwy typ MIME pliku (nie tylko rozszerzenie), sprawdza poprawność przesyłania oraz integruje się z systemem uprawnień WordPressa.
function secure_handle_avatar_upload() {
// 1. Weryfikacja Nonce dla bezpieczeństwa formularza
if ( ! isset( $_POST['avatar_upload_nonce'] ) || ! wp_verify_nonce( $_POST['avatar_upload_nonce'], 'upload_avatar_action' ) ) {
wp_die( esc_html__( 'Błąd autoryzacji tokena.', 'secure-plugin' ) );
}
// 2. Sprawdzenie uprawnień zalogowanego użytkownika
if ( ! current_user_can( 'upload_files' ) ) {
wp_die( esc_html__( 'Nie masz uprawnień do wgrywania plików.', 'secure-plugin' ) );
}
if ( isset( $_FILES['avatar'] ) ) {
// Wymagane pliki biblioteki WordPress do obsługi uploadu
if ( ! function_exists( 'wp_handle_upload' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
// 3. Definiujemy dozwolone typy plików (MIME)
$allowed_mimes = [
'jpg|jpeg' => 'image/jpeg',
'png' => 'image/png'
];
$upload_overrides = [
'test_form' => false,
'mimes' => $allowed_mimes // Nadpisujemy dozwolone formaty
];
// 4. Bezpiecznie przetwarzamy plik przez rdzeń WordPressa
$movefile = wp_handle_upload( $_FILES['avatar'], $upload_overrides );
if ( $movefile && ! isset( $movefile['error'] ) ) {
// Plik wgrany poprawnie, zwraca adres URL pliku
$avatar_url = $movefile['url'];
update_user_meta( get_current_user_id(), 'user_avatar', $avatar_url );
} else {
// Obsługa błędu przesyłania
error_log( 'Błąd wgrywania pliku: ' . $movefile['error'] );
}
}
}
Metodologia Audytu: Jak wdrożyć proces “Zero Trust” dla kodu AI?
Aby uchronić się przed podatnościami generowanymi przez sztuczną inteligencję, każdy zespół programistyczny powinien wdrożyć ustrukturyzowany proces weryfikacji. Oto kroki, które należy podjąć przed wdrożeniem jakiegokolwiek wygenerowanego kodu na środowisko produkcyjne:
Krok 1: Wdrożenie statycznej analizy kodu (Lintery i PHPCS)
Pierwszą linią obrony jest automatyzacja. Skonfiguruj WordPress Coding Standards (WPCS) dla narzędzia PHP_CodeSniffer. Narzędzie to automatycznie przeskanuje pliki PHP wygenerowane przez AI i wskaże brakujące funkcje sanityzacji, eskapowania czy weryfikacji nonces.
# Uruchomienie PHPCS z regułami WordPress-Extra na wygenerowanym pliku wtyczki
vendor/bin/phpcs --standard=WordPress-Extra path/to/generated-plugin.php
Krok 2: Manualny przegląd kodu (Checklista “Zero Trust”)
Każda funkcja obsługująca żądanie HTTP, wywołanie AJAX, REST API lub interakcję z bazą danych musi przejść manualny audyt według poniższej listy kontrolnej:
graph TD
A[Otrzymanie kodu wtyczki od AI] --> B{Czy kod obsługuje wejście użytkownika?}
B -- Tak --> C[Sprawdź obecność Nonce i Capability Checks]
B -- Nie --> D{Czy kod modyfikuje bazę danych?}
C --> C1[Wdrożenie weryfikacji nonce za pomocą check_ajax_referer]
C --> C2[Wdrożenie current_user_can do kontroli uprawnień]
C1 --> D
C2 --> D
D -- Tak --> E[Sprawdź użycie metody $wpdb->prepare]
D -- Nie --> F{Czy kod wyświetla zmienne w przeglądarce?}
E --> E1[Wdrożenie sparametryzowanych zapytań SQL]
E1 --> F
F -- Tak --> G[Sprawdź obecność funkcji esc_html, esc_attr, esc_url]
F -- Nie --> H[Kod gotowy do testów integracyjnych]
G --> G1[Wdrożenie poprawnego eskapowania na wyjściu]
G1 --> H
Krok 3: Testy funkcjonalne z kontami o niskich uprawnieniach
Zanim uznasz wtyczkę za bezpieczną, zaloguj się na konto testowe o roli “Subskrybent” (Subscriber) i spróbuj wywołać endpointy AJAX/REST zdefiniowane we wtyczce za pomocą narzędzi takich jak Postman lub curl. Jeśli wtyczka pozwoli na wykonanie operacji modyfikacji danych lub odczytania chronionych informacji, weryfikacja uprawnień leży u podstaw.
Podsumowanie i Rekomendacje dla Inżynierów
Sztuczna inteligencja jest potężnym narzędziem przyspieszającym pisanie kodu, ale nie zastąpi odpowiedzialności inżynieryjnej. Traktuj każdy fragment kodu wygenerowany przez LLM jak kod napisany przez niedoświadczonego praktykanta.
Zawsze pamiętaj o wdrożeniu poprawnych capability checks (current_user_can()), weryfikacji mechanizmów anty-CSRF (wp_verify_nonce()), zabezpieczaniu zapytań SQL za pomocą $wpdb->prepare(), a także o bezwzględnym czyszczeniu danych na wejściu (sanitize_*) i zabezpieczaniu ich przed wyrenderowaniem na wyjściu (esc_*).
Wdrażając te zasady oraz rygorystyczny proces automatycznego i manualnego audytu kodu, możesz bezpiecznie czerpać korzyści z szybkości pracy z AI, zachowując jednocześnie stabilność, szczelność i bezpieczeństwo aplikacji WordPress swoich klientów.






