Trumpai: API saugumui PHP svarbiausi penki dalykai: tik HTTPS, autentifikavimas kiekviename neviešame galiniame taške, teisių patikra kiekvienam atskiram objektui, griežtos metodo, turinio tipo ir užklausos dydžio ribos bei trumpi klaidų pranešimai be vidinių detalių. Prie to ateina užklausų ribojimas ir saugumui svarbių įvykių žurnalas. Nuo spragų pačiame PHP interpretatoriuje kaip PHP2Shell tai nepadeda; tam reikia PHP atnaujinimo.
Sąsaja už svetainės
Savaitę po 2026-10-21 daug įmonių tikrino savo svetaines. Sąsajos už jų ant sąrašo atsidurdavo rečiau. O būtent ten vyksta jautrūs procesai: klientų portalo programa pasiima sąskaitas, prekių apskaitos sistema įrašo atsargas į parduotuvę, partneris per webhook praneša apie užsakymus. Šios API dažnai buvo sukurtos greitai, vienam tikslui, ir po to niekada nebepažiūrėtos.
API neturi sąsajos, kuri parodytų klaidas. Trūkstama teisių patikra kasdienybėje niekam nekrenta į akis, kol visi klientai užklausia tik tai, ką turi. Užpuolikas, priešingai, bando būtent tai, kas nenumatyta.
Pirmas žingsnis todėl yra inventorizacija. Kokie galiniai taškai apskritai egzistuoja? Dažnai randamos senos versijos kaip /api/v1/, kurios po perėjimo į /v2/ niekada neišjungtos, bandomieji galiniai taškai iš kūrimo ar viešai pasiekiamas sąsajos aprašas, išvardijantis vidines funkcijas. Kiekvienas nebereikalingas galinis taškas turėtų būti išjungtas. Kiekvienas, kuris lieka, turi turėti atsakingą asmenį.
API saugumas PHP: pagrindinės taisyklės
OWASP REST Security Cheat Sheet apibendrina patikrintas taisykles. PHP projektams jas galima išversti į trumpą sąrašą:
- Tik HTTPS. Žetonai ir prisijungimo duomenys niekada negali būti perduodami nešifruoti.
- Kiekvienas galinis taškas tikrina pats. Autentifikavimas gali vykti centralizuotai, o sprendimas dėl prieigos priimamas galiniame taške.
- Teisė pagal objektą. Ne tik „prisijungęs“, bet „gali būtent šį duomenų įrašą“.
- Nustatyti leidžiamus metodus. Kas aiškiai neleidžiama, atmetama su 405.
- Tikrinti turinio tipą ir dydį. Neteisingas tipas duoda 415, per didelės užklausos 413.
- Tikrinti įvestis. Tipas, ilgis, formatas ir reikšmių intervalas, serverio pusėje.
- Jokių paslapčių URL. Žetonai ir raktai priklauso antraštei, o ne užklausos eilutei (query string).
- Trumpos klaidos. Jokių stack trace, kelių ar SQL pranešimų klientui.
- Užklausų ribojimas. Per daug užklausų atsakoma su 429.
- Apsaugoti administravimo galinius taškus. Nepasiekiami viešai arba tik su stipriu prisijungimu.
Objekto teisė: dažniausia klaida
Pavyzdys šią problemą padaro apčiuopiamą. Galinis taškas pateikia sąskaitas adresu /api/rechnungen/{id}. Tikrinama, ar yra galiojantis žetonas. Netikrinama, ar sąskaita priklauso žetono kliento. Kas savo sąskaitos numerį padidina vienetu, pamato kaimyno.
Sprendimas paprastas ir vis dėlto turi būti įgyvendintas kiekvienoje vietoje: pati užklausa apriboja iki teisėtos srities.
<?php
// Leisti tik GET metodą
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
header('Allow: GET');
exit;
}
$kundeId = $auth->kundeId(); // iš patikrinto žetono
$id = filter_var($routeId, FILTER_VALIDATE_INT);
if ($id === false) {
http_response_code(400);
exit;
}
// Teisė įrašyta užklausoje: tik savos sąskaitos
$stmt = $pdo->prepare('SELECT nummer, betrag, datum FROM rechnungen WHERE id = ? AND kunde_id = ?');
$stmt->execute([$id, $kundeId]);
$rechnung = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$rechnung) {
http_response_code(404); // neišduoti, ar toks ID egzistuoja
exit;
}
header('Content-Type: application/json');
echo json_encode($rechnung, JSON_THROW_ON_ERROR);
Užklausa su vietos žymekliais kartu saugo ir nuo SQL injekcijos, žr. SQL injekcijos PHP prevencija. Kaip PHP pateikia užklausos duomenis, tokius kaip metodas, antraštės ir kūnas (body), aiškina Superglobals: $_GET, $_POST, $_FILES ir kiti.
Žetonų teisingas tikrinimas
Daugelis API dirba su JSON Web Token. OWASP Cheat Sheet tam nurodo aiškias taisykles: žetonai turi būti pasirašyti, nepasirašyti žetonai atmetami, o patikros algoritmas nustatomas serveryje, o ne perimamas iš žetono. Išdavėjas, auditorija ir galiojimo laikas taip pat priklauso patikrai. Tam naudokite prižiūrimą biblioteką, o ne savas implementacijas.
Savi žetonai, pavyzdžiui, webhook ar vienkartinėms nuorodoms, turi kilti iš kriptografiškai saugaus atsitiktinių skaičių generatoriaus ir būti lyginami pastoviu laiku. Kaip tai daroma PHP, rodo Saugūs atsitiktiniai skaičiai ir žetonai PHP.
Kiekvienam klientui ir kiekvienam partneriui duokite savą raktą su būtent tomis teisėmis, kurių jam reikia. Tada nutekėjusį raktą galima užblokuoti atskirai, nenutraukiant visų kitų prijungimų.
Ribų nustatymas: dydis, tipas ir tempas
API turėtų priimti tik tai, ko tikisi. Tai prasideda nuo turinio tipo: galinis taškas, apdorojantis JSON, atmeta viską kita. Didžiausią užklausos dydį apribokite keliais lygmenimis, žiniatinklio serveryje ir PHP per post_max_size. Ši riba taip pat priklauso pereinamosioms priemonėms, kurias php.net nurodė dėl PHP2Shell. Kaip ją prasmingai pasirinkti, aprašo post_max_size ir upload_max_filesize prasmingas ribojimas.
Užklausų ribojimas saugo nuo identifikatorių perrinkimo ir nuo perkrovos. Jį galima įgyvendinti žiniatinklio serveryje, prieš tai esančiame tarpiniame serveryje arba programoje. Svarbu, kad ribos galiotų kiekvienam raktui ar naudotojui, o ne tik pagal IP adresą.
API, kurias kviečia tiesiai iš naršyklės, prisideda CORS, t. y. taisyklės, kurioms svetimoms svetainėms leidžiama siųsti užklausas. Šias antraštes nustatykite tik jei tokie kreipiniai tikrai numatyti, ir leidžiamas kilmes nurodykite atskirai, užuot leidę bet kokią kilmę. Pakaitos simbolis, leidžiantis visas svetaines, galiniuose taškuose su prisijungimu beveik visada yra klaida.
Galiausiai žurnalas: nepavykę prisijungimai, atmesti žetonai ir atsisakyta prieiga priklauso žurnalui, patys žetonai ne. Be šių įrašų ataka prieš API lieka nematoma.
Kaip PHP SOS padeda konkrečiai?
- Surenkame visas jūsų PHP sistemų sąsajas, taip pat pamirštas ir vidines, ir tikriname, kurios pasiekiamos viešai.
- Skaitome teisių logiką ir tikslingai išbandome, ar svetimi duomenų įrašai pasiekiami.
- Nustatome dydžio, tipo ir tempo ribas žiniatinklio serveryje ir PHP ir suderiname jas su jūsų klientais.
- Išsamiai patikrai iš išorės tai derinome su saugumo auditu ir įsiskverbimo testu.
Išvada: API yra tiek saugi, kiek jos aplaidžiausias galinis taškas. Kas teises tikrina pagal objektą, nustato aiškias ribas ir klaidas laiko trumpas, uždaro dažniausias spragas gerokai anksčiau, nei kas nors tikslingai jų ieško.
Dažni klausimai
Ar API raktas kaip apsauga pakanka?
Jautriems duomenims ne. API raktas dažniausiai identifikuoja programą, o ne naudotoją, ir lengvai perduodamas toliau. OWASP REST Security Cheat Sheet pataria API raktų nenaudoti kaip vienintelės jautrių išteklių apsaugos.
Kas yra objekto teisė ir kodėl jos taip dažnai trūksta?
Turima omenyje patikra, ar prisijungęs naudotojas gali matyti ar keisti būtent šį duomenų įrašą. Jos dažnai trūksta, nes kūrėjai tikrina tik tai, ar apskritai kas nors prisijungęs. Tada pakanka pakeisti identifikatorių URL, kad būtų gauti svetimi duomenys.
Ar gerai apsaugota API saugo nuo PHP2Shell?
Ne. PHP2Shell slypi PHP interpretatoriuje ir paveiktuose veikimo režimuose veikia dar prieš paleidžiant jūsų kodą. Nuo to padeda tik PHP atnaujinimas su paleidimu iš naujo. Užklausų dydžio ribos vis tiek prasmingos ir priklauso php.net pereinamosioms priemonėms.
Kokius būsenos kodus API turėtų grąžinti saugumo klaidų atveju?
Įprasta 401 be galiojančio prisijungimo, 403 trūkstant teisių, 405 neleidžiamam metodui, 413 per didelei užklausai, 415 neteisingam turinio tipui ir 429 per daug užklausų. Pats atsakymas lieka trumpas ir be vidinių detalių.