Egyedi REST API végpont biztonságosan WordPressben

WordPress 5 perc olvasás

A WordPress dokumentációjának hatsoros példája egy működő REST API végpontot ad, de nem éles üzemre valót. Egy jellemző történet: a fejlesztő regisztrál egy végpontot, amely minden kérésre 200 KB JSON-t ad vissza, hitelesítés és korlátozás nélkül. Egy hét múlva egy robot óránként több tízezer kéréssel tölti le, a webhely belassul, és a tárhely erőforráskorlátja is betelik.

Ez a cikk fejlesztőknek szól: mire figyeljen, amikor saját végpontot ír, és hogyan nézze át a meglévőket.

A két sor, amelyet a legtöbb példa kihagy

Egy végpontot a register_rest_route() függvénnyel regisztrál: névtér, útvonal, HTTP-metódus, a választ előállító callback és a permission_callback.

  • permission_callback dönti el, ki hívhatja a végpontot. Ha __return_true, a végpont nyilvános, bárki hívhatja az internetről. A WordPress 5.5 óta figyelmeztet, ha hiányzik.
  • args írja le, milyen paramétereket fogad el. Típussal (type), kötelezőséggel (required), tisztítással (sanitize_callback) és ellenőrzéssel (validate_callback) a WordPress a hibás bemenetet még azelőtt elutasítja, hogy a kódja lefutna. Enélkül bármi eljut a callback függvényig.

A meglévő végpontok átnézése

A webhely összes nem alapértelmezett végpontját és a jogosultság-ellenőrzésüket a WP-CLI listázza (a cPanel tárhely Terminal felületén vagy SSH-n):

cd ~/public_html
wp eval 'foreach ( rest_get_server()->get_routes() as $route => $handlers ) { if ( preg_match( "#^/(wp|oembed|wp-site-health|wp-block-editor)/#", $route ) || "/" === $route ) continue; foreach ( $handlers as $h ) { $p = $h["permission_callback"] ?? null; echo $route, " [", implode( ",", array_keys( $h["methods"] ) ), "] ", is_string( $p ) ? $p : ( $p ? "closure" : "NINCS" ), "\n"; } }'

Ahol __return_true áll, a végpont nyilvános. Nézze meg, mit ad vissza:

curl -s "https://pelda.hu/wp-json/sajat/v1/adatok" | head -c 1000

Ha a /wp-json/ címek 404-es hibát adnak, a közvetlen hivatkozások egyszerű módra vannak állítva; ilyenkor használja a https://pelda.hu/?rest_route=/sajat/v1/adatok formát. Ha a válasz e-mail-címeket, belső azonosítókat vagy piszkozatok tartalmát mutatja, a végpont adatot szivárogtat, és azonnal javítani kell. A WP-CLI alapjait a WP-CLI használatáról szóló cikk írja le.

Éles üzemre való végpont

add_action( 'rest_api_init', function () {
    register_rest_route( 'sajat/v1', '/termekek/(?P<id>\d+)', array(
        'methods'             => WP_REST_Server::READABLE, // GET
        'callback'            => 'sajat_termek_lekerdezese',
        'permission_callback' => 'sajat_olvashatja',
        'args'                => array(
            'id' => array(
                'required'          => true,
                'type'              => 'integer',
                'minimum'           => 1,
                'sanitize_callback' => 'absint',
            ),
        ),
    ) );
} );

function sajat_olvashatja( WP_REST_Request $request ) {
    // Bejelentkezett felhasználó (süti + nonce vagy alkalmazásjelszó).
    if ( current_user_can( 'read' ) ) {
        return true;
    }
    // Szerver-szerver kapcsolat API-kulccsal.
    $kulcs = $request->get_header( 'X-API-Key' );
    if ( $kulcs && defined( 'SAJAT_API_KULCS' ) && hash_equals( SAJAT_API_KULCS, $kulcs ) ) {
        return true;
    }
    return new WP_Error( 'rest_forbidden', 'Hitelesítés szükséges.', array( 'status' => 401 ) );
}

function sajat_termek_lekerdezese( WP_REST_Request $request ) {
    $termek = get_post( $request['id'] );
    if ( ! $termek || 'publish' !== $termek->post_status ) {
        return new WP_Error( 'nincs_ilyen', 'Nincs ilyen termék.', array( 'status' => 404 ) );
    }
    return rest_ensure_response( array(
        'id'    => $termek->ID,
        'cim'   => get_the_title( $termek ),
    ) );
}

Néhány szabály a példa mögött:

  • Csak azt adja vissza, ami kell. Ne a teljes WP_Post objektumot, hanem a szükséges mezőket, és csak a közzétett tartalmat.
  • Az API-kulcs ne az adatbázisba kerüljön, hanem a wp-config.php fájlba (define( 'SAJAT_API_KULCS', '…' );), és a hash_equals() függvénnyel hasonlítsa össze, ne ==-vel.
  • Szerkesztői végpontoknál (például a blokkszerkesztő saját blokkjainak adatai) a jogosultság a megfelelő képességhez kötődjön: current_user_can( 'edit_post', $id ), soha ne __return_true.
  • sanitize_callback és validate_callback: az első megtisztítja a bemenetet (absint, sanitize_text_field), a második elutasítja, ha érvénytelen. Az egyszerű típusellenőrzéshez az args séma (type, minimum, enum, format) is elég.

Hitelesítés

  • A webhely saját JavaScriptje (bejelentkezett felhasználó) a wp_rest nonce-szal hitelesít: a wp_create_nonce( 'wp_rest' ) értékét küldje az X-WP-Nonce fejlécben.
  • Külső alkalmazás, mobilapp, másik szerver alkalmazásjelszóval hitelesít: a felhasználó profiljában (Felhasználók › Profil, Alkalmazásjelszavak rész) hozza létre, és Basic hitelesítéssel küldje. Az alkalmazásjelszó külön visszavonható, és nem azonos a belépési jelszóval.

Nyilvános végpontok: gyorsítótár és korlátozás

Ha a végpont szándékosan nyilvános, védje a túlterheléstől.

Gyorsítótárazás. A REST-válaszokat a WordPress nem gyorsítótárazza. Ha a tartalom percekig érvényes, jelezze a CDN-nek és a böngészőnek:

$valasz = rest_ensure_response( $adatok );
$valasz->header( 'Cache-Control', 'public, max-age=300' );
return $valasz;

Egy drága lekérdezés eredményét a szerveren is tárolhatja átmeneti adatként (set_transient()), így a gyakori kérések nem futtatják újra.

Kéréskorlát. A WordPress nem korlátozza, ki hányszor hívhat egy végpontot. A leghatékonyabb egy CDN- vagy tűzfalszabály (például a Cloudflare kéréskorlát-szabálya a végpont útvonalára), mert a túlzott kérések így el sem érik a tárhelyet. PHP-ban egy egyszerű, IP-címenkénti számláló a permission_callback elején:

function sajat_kereskorlat() {
    $kulcs = 'kk_' . md5( $_SERVER['REMOTE_ADDR'] ?? '' );
    $db    = (int) get_transient( $kulcs );
    if ( $db >= 60 ) {
        return new WP_Error( 'rest_tul_sok_keres', 'Túl sok kérés.', array( 'status' => 429 ) );
    }
    set_transient( $kulcs, $db + 1, MINUTE_IN_SECONDS );
    return true;
}

Ez percenként 60 kérést enged IP-címenként. Ha a webhely CDN mögött van, a REMOTE_ADDR a CDN címe lehet; ilyenkor a CDN által küldött fejlécből (például a Cloudflare CF-Connecting-IP fejlécéből) vegye a látogató címét, de csak akkor, ha a kérés valóban a CDN-től érkezik.

Dokumentáció

A register_rest_route() egy schema paramétert is elfogad, amely JSON-sémában írja le a választ. A WordPress ezt a végpont OPTIONS kérésére visszaadja, és az OpenAPI-exportálók és a Postman is fel tudják dolgozni.

Kipróbálás

curl -s -o /dev/null -w "%{http_code}\n" "https://pelda.hu/wp-json/sajat/v1/termekek/123"
curl -s -H "X-API-Key: a-sajat-kulcsa" "https://pelda.hu/wp-json/sajat/v1/termekek/123"
curl -s "https://pelda.hu/wp-json/sajat/v1/termekek/abc"

Kulcs nélkül 401, kulccsal a termék adatai, érvénytelen azonosítóval 404 vagy 400 a helyes válasz. Az élesítés előtt egy staging másolaton próbálja ki, és ha a hibakereséshez naplózás kell, a WordPress hibakeresési módjáról szóló cikk segít. A WordPress általános biztonsági beállításait külön cikk foglalja össze.

Vissza: WordPress leírások (39)