PHP-tietoturva
PHP-tietoturva: näin estät PHP-haavoittuvuudet koodissasi, Composer-riippuvuuksissasi ja buildissasi
PHP:n suojaaminen tarkoittaa kolmea kerrosta. Kirjoittamassasi koodissa: validoi syötteet, käytä parametrisoituja kyselyjä ja kehyksen escapointia äläkä koskaan deserialisoi epäluotettavaa dataa. Composer-paketeissa, joista olet riippuvainen: aja composer audit, tallenna composer.lock versionhallintaan ja estä turvattomat versiot. CI:ssä, joka asentaa ne: tallenna, mitä todella asennettiin, ja rajoita, mihin buildi voi yhdistää. Useimmat PHP-tietoturvaoppaat pysähtyvät ensimmäiseen kerrokseen. Tämä kattaa kaikki kolme.
Päivitetty
- Koodisi Injektiot, XSS, deserialisointi, tiedostojen käsittely, istunnot Kehysten oletukset, koodikatselmointi, PHPStan, Psalm, Semgrep
- Composer-riippuvuutesi Tunnetut haavoittuvuudet, hylätyt ja haitalliset paketit, lukitustiedoston ajautuminen, asennuksenaikainen koodi composer audit, Composerin käytännöt, Dependabot tai Renovate
- Buildisi Mitä CI todella asensi ja ajoi, mistä se haki ja mihin se pääsi Kiinnitetyt actionit, vähimpien oikeuksien tokenit, egress-käytäntö, buildin aikana tallennettu SBOM
Tällä sivulla: PHP2Shell, PHP-haavoittuvuudet, joilla on yhä merkitystä, konfiguraatio ja tuetut versiot, staattinen analyysi, Composer-riippuvuuksien tietoturva, PHP:n toimitusketjuhyökkäykset, CI-buildi, SBOM:t ja työkalut kerroksittain.
PHP2Shell: PHP-haavoittuvuus, joka on paikattava nyt
PHP-projekti julkaisi 21.10.2026 versiot PHP 8.5.12, 8.4.27, 8.3.36 ja 8.2.35 korjatakseen kaksi kriittistä haavoittuvuutta itse PHP:ssä. Ne voivat mahdollistaa etäkoodin suorittamisen ilman tunnistautumista. Kummankin CVSS v4.0 -pistemäärä on 9.2, ja yhdessä ne tunnetaan nimellä PHP2Shell:
- CVE-2026-109618: muistin loppumisen käsittely voi keskeyttää PHP:n, kun pyynnön data on alustettu vasta osittain, ja pyynnön siivous voi sitten käsitellä virheellistä dataa kelvollisena.
- CVE-2026-109619: pyynnön omistama tila voi säilyä sen muistin vapauttamisen jälkeen, ja saman workerin käsittelemä seuraava pyyntö voi käyttää sitä.
PHP:n tietoturvatiedotteen mukaan vaarassa on jokainen web-pyyntöjä palveleva asennus, mukaan lukien PHP-FPM, Apache ja mod_php, php-cgi, FrankenPHP ja LiteSpeed, ja suurimman riskin se arvioi koskevan mod_php:tä. PHP 8.1 ja sitä vanhemmat eivät saa PHP-projektilta uutta julkaisua. Vakavuuden vuoksi PHP:n tietoturvatiimi julkaisi kuitenkin minimaaliset lähdekoodin backportit PHP 7.4:lle, 8.0:lle ja 8.1:lle niille, jotka buildaavat PHP:n lähdekoodista tai joilla ei ole LTS-toimittajaa, ja osa jakeluista ja LTS-toimittajista toimittaa omia backport-korjauksiaan. Mitä tehdä:
- Päivitä korjattuun julkaisuun. Jos käytössä on 8.1 tai vanhempi, päivitä tuettuun haaraan, ota käyttöön toimittajasi backport-paketti tai asenna virallinen lähdekoodin backport, jos buildaat PHP:n itse.
-
Buildaa uudelleen jokainen konttikuva, joka sisältää PHP:n, ja ota se uudelleen käyttöön. Vanha kuvatagi ajaa vanhaa
PHP:tä, kunnes se korvataan, joten tarkista, mitä on käytössä, eikä vain sitä, mitä repositoriossa on:
docker run --rm IMAGE php -v. -
Jos et voi päivittää tänään, ota käyttöön väliaikaiset lievennykset
PHP:n tietoturvatiedotteesta, kuten
session.upload_progress.enabled=0. Aseta sephp.ini-tiedostoon, FPM-pooliin tai palvelinkonfiguraatioon, joka on voimassa ennen pyynnön jäsentämistä, ja käynnistä jokainen worker uudelleen. Älä luota tässäini_set()-kutsuun tai FPM:n.user.ini-tiedostoon: tiedote varoittaa molemmista. PHP sanoo selvästi, että nämä lievennykset pienentävät altistumista mutta eivät tee asennuksesta turvallista.
Huomaa, mitä composer audit ei voi tässä kertoa: PHP2Shell on ajoympäristössä, ei Composer-paketissa, joten
se ei koskaan näy composer.lock-tiedostossa. Ajamasi PHP tulee peruskuvastasi tai käyttöjärjestelmän
paketeista, mikä on tämän oppaan kolmas kerros. Versiotaulukko, aikajana ja skripti, joka tarkistaa jokaisen kuvatagin:
PHP2Shell: mitä paikata ja miten löytää jokainen haavoittuva PHP.
PHP-haavoittuvuudet, joilla on yhä merkitystä
PHP-sovellusten haavoittuvuusluokat ovat tuskin muuttuneet vuosikymmeneen. Muuttunutta on se, että Laravel, Symfony ja moderni PHP sulkevat useimmat niistä oletuksena, joten todelliset bugit kasautuvat kohtiin, joissa koodi kiertää nämä oletukset. Injektio on kohta A05 OWASP Top 10:2025 -listalla, ja Software Supply Chain Failures on uutena kohdassa A03, joka on tämän oppaan jälkimmäisen puoliskon aihe.
| Haavoittuvuus | Miten se näkyy PHP:ssä | Korjaus | Laravel ja Symfony |
|---|---|---|---|
| SQL-injektio | Pyynnön data liitetään SQL:ään, myös raakakyselyjen ohitusreittien kautta | PDO:n valmistellut lauseet sidotuilla parametreilla | Eloquent ja Doctrine sitovat parametrit; whereRaw(), DB::raw() ja yhdistelty DQL eivät |
| Cross-site scripting (XSS) | Käyttäjän syöte tulostetaan HTML:ään ilman escapointia | Escapoi tulostettaessa htmlspecialchars()-funktiolla tai mallimoottorilla |
Bladen {{ }} ja Twig escapoivat oletuksena; {!! !!} ja |raw eivät |
| Cross-site request forgery (CSRF) | Tilaa muuttava lomake tai AJAX-päätepiste hyväksyy minkä tahansa pyynnön, jossa on istuntoeväste | Istuntokohtainen token, joka tarkistetaan jokaisessa POST-, PUT-, PATCH- ja DELETE-pyynnössä, sekä SameSite-evästeet |
Laravel tarkistaa tokenin web-reiteillä (@csrf lomakkeissa); Symfony Forms sisältää ja tarkistaa sen oletuksena; tarkistuksesta pois jätetyillä reiteillä sitä ei ole |
Koodi-injektio eval()-kutsun kautta |
Pyynnön data tai käyttäjän muokattavissa oleva malli tai kaava päätyy eval()-kutsuun |
Älä käytä eval()-kutsua; kuvaa syöte kiinteään joukkoon operaatioita tai käytä jäsennintä tarvitsemillesi lausekkeille |
Mikään kehys ei suojaa omia eval()-kutsujasi |
| Objekti-injektio | unserialize() evästeille, pyynnön datalle tai välimuistimerkinnöille, joihin hyökkääjä voi kirjoittaa |
Käytä JSONia ulkopuoliselle datalle; jos sinun on pakko deserialisoida, hylkää objektit | Mikään kehys ei suojaa omia unserialize()-kutsujasi |
| Tiedostojen sisällyttäminen ja lataukset | Syötteestä muodostettu polku välitetään include-lauseelle; lataukset tallennetaan web-juuren alle asiakkaan tiedostonimellä |
Salli sisällytettävät tiedostot sallittujen listalla; tallenna lataukset web-juuren ulkopuolelle generoiduilla nimillä | Laravelin store() generoi tiedostonimet; validoi tyyppi ja koko palvelimella |
| Tyyppimuunnokset (type juggling) | == vertaa tokeneita tai tiivisteitä, jolloin "0e123" == "0e456" on tosi |
===, hash_equals() salaisuuksille, password_verify() salasanoille |
Kehysten apufunktiot vertaavat turvallisesti; omat vertailusi ovat sinun vastuullasi |
| Heikot istunnot ja evästeet | Istuntotunnisteet ovat JavaScriptin luettavissa, kulkevat HTTP:n yli tai säilyvät kirjautumisen yli | Secure-, HttpOnly- ja SameSite-evästeet, strict mode, uusi istuntotunniste kirjautumisen jälkeen | Molemmat luovat istuntotunnisteen uudelleen kirjautumisessa |
| Server-side request forgery (SSRF) | file_get_contents() tai cURL käyttäjän antamaan URL-osoitteeseen, myös wrappereilla kuten file:// ja phar:// |
Salli skeemat ja palvelimet sallittujen listalla, estä sisäiset osoitteet, poista allow_url_fopen käytöstä, jos sitä ei tarvita |
Ei oletussuojausta |
| Komentoinjektio | Syöte välitetään funktioille exec(), shell_exec(), system() tai proc_open() |
Vältä shelliä; välitä argumentit taulukkona tai escapoi kukin escapeshellarg()-funktiolla |
Symfony Process ja Laravelin Process-fasadi hyväksyvät argumenttitaulukon |
| Massasijoitus (mass assignment) | User::create($request->all()) antaa pyynnön asettaa kentän is_admin |
Luettele täytettävät kentät tai välitä vain validoitu syöte | Eloquent-mallit ovat oletuksena suojattuja; $guarded = [] poistaa suojauksen |
SQL-injektio: haavoittuva ja korjattu
// Vulnerable: the id goes straight into the SQL string
$rows = $pdo->query("SELECT * FROM orders WHERE id = " . $_GET['id']);
// Fixed: a prepared statement with a bound parameter
$stmt = $pdo->prepare('SELECT * FROM orders WHERE id = ?');
$stmt->execute([$_GET['id']]);
$rows = $stmt->fetchAll();
// Laravel: whereRaw() takes SQL as written, where() binds the value
Order::whereRaw("status = '" . $request->input('status') . "'")->get(); // vulnerable
Order::where('status', $request->input('status'))->get(); // fixed
XSS: haavoittuva ja korjattu
<?php // Plain PHP: escape on output
echo '<p>' . $comment . '</p>'; // vulnerable
echo '<p>' . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8') . '</p>'; // fixed ?>
{{-- Blade: {{ }} escapes, {!! !!} prints raw HTML --}}
<p>{!! $comment !!}</p> {{-- vulnerable --}}
<p>{{ $comment }}</p> {{-- fixed --}}
{# Twig: autoescape is on, |raw turns it off #}
<p>{{ comment|raw }}</p> {# vulnerable #}
<p>{{ comment }}</p> {# fixed #}
Objekti-injektio: haavoittuva ja korjattu
// Vulnerable: an attacker-controlled cookie becomes PHP objects, and any
// class with a __wakeup() or __destruct() method in vendor/ is a gadget
$prefs = unserialize($_COOKIE['prefs']);
// Fixed: JSON carries data, not objects
$prefs = json_decode($_COOKIE['prefs'], true, 512, JSON_THROW_ON_ERROR);
// If you must read serialized data, refuse objects
$prefs = unserialize($data, ['allowed_classes' => false]);
Phar-deserialisointia, muunnelmaa, jossa phar://-polkuun kohdistuva tiedosto-operaatio deserialisoi
arkiston metatiedot, rajattiin PHP 8.0:ssa: phar-metatietoja
ei enää deserialisoida, kun phar
avataan, vaan vain, kun koodi kutsuu getMetadata()-metodia. Se on yksi syy lisää jättää PHP 7 taakse.
Tiedostojen sisällyttäminen: haavoittuva ja korjattu
// Vulnerable: ?page=../admin/reset reaches any .php file on the disk
include __DIR__ . '/pages/' . $_GET['page'] . '.php';
// Fixed: only names on an allow-list can be included
$pages = ['home' => 'home.php', 'pricing' => 'pricing.php'];
$file = $pages[$_GET['page'] ?? 'home'] ?? 'home.php';
include __DIR__ . '/pages/' . $file;
Konfiguraation ja ajoympäristön koventaminen
Turvallinenkin sovellus voi vuotaa ajoympäristönsä kautta. Nämä php.ini-asetukset noudattavat
OWASP PHP
Configuration Cheat Sheetiä:
| Asetus | Tuotantoarvo | Miksi |
|---|---|---|
display_errors |
Off (ja log_errors = On) |
Pinojäljet paljastavat polkuja, kyselyjä ja joskus salaisuuksia |
expose_php |
Off |
Lopettaa tarkan PHP-version mainostamisen vastausotsakkeissa |
allow_url_include |
Off |
Estää etä-URL-osoitteiden include-sisällyttämisen; vanhentunut PHP 7.4:stä alkaen |
allow_url_fopen |
Off, jos sovellus ei hae URL-osoitteita tiedostofunktioilla |
Kaventaa SSRF:ää file_get_contents()-funktion kautta |
disable_functions |
Esimerkiksi exec, shell_exec, system, passthru, proc_open, popen |
Vie shellin hyökkääjältä, joka saa koodia ajoon; luettele vain funktiot, joita sovelluksesi ei koskaan kutsu |
open_basedir |
Sovelluksen hakemisto | Rajaa polut, joita PHP:n tiedostofunktiot voivat avata; suojakaide, ei eristysraja |
| Istuntoevästeet | session.cookie_secure = 1, session.cookie_httponly = 1, session.cookie_samesite = Lax tai Strict, session.use_strict_mode = 1 |
Pitää istuntotunnisteet poissa salaamattomasta HTTP:stä ja JavaScriptin ulottuvilta ja hylkää tunnisteet, joita palvelin ei ole myöntänyt |
file_uploads |
Off, jos sovellus ei vastaanota latauksia |
Poistaa kokonaisen syötepolun |
Pysy tuetussa PHP-versiossa
PHP antaa jokaiselle haaralle kaksi vuotta aktiivista tukea ja sen jälkeen kaksi vuotta pelkkiä tietoturvakorjauksia, kertoo php.net. PHP 8.1:n elinkaari päättyi 31.12.2025, ja PHP 8.2 seuraa 31.12.2026. Elinkaarensa päähän tullut haara ei saa PHP-projektilta uusia julkaisuja, ja juuri siihen asemaan PHP2Shell asetti 8.1:n käyttäjät: heidän ainoa virallinen korjauksensa oli lähdekoodin backport.
- PHP 8.1 End of life, päättyi 31.12.2025
- PHP 8.2 Vain tietoturvakorjaukset, 31.12.2026 asti
- PHP 8.3 Vain tietoturvakorjaukset, 31.12.2027 asti
- PHP 8.4 Aktiivinen tuki, 31.12.2026 asti
- PHP 8.5 Aktiivinen tuki, 31.12.2027 asti
Laravelin ja Symfonyn tuotantoasetukset
-
Debug pois päältä.
APP_DEBUG=falseLaravelissa,APP_ENV=prodjaAPP_DEBUG=0Symfonyssa. Tuotannossa näkyvä debug-sivu paljastaa konfiguraation ja ympäristömuuttujien arvot. -
Avaimet ja salaisuudet. Pidä
APP_KEY(Laravel) jaAPP_SECRET(Symfony) poissa repositoriosta ja vaihda ne, jos ne vuotavat. LaravelinAPP_PREVIOUS_KEYSantaa vaihtaa avaimen kirjaamatta jokaista käyttäjää ulos. -
Luotetut välityspalvelimet. Kuormantasaajan takana luettele luottamasi välityspalvelimet (Laravelin
trusted proxies -middleware, Symfonyn
framework.trusted_proxies), jottei asiakas voi väärentää IP-osoitettaan tai HTTPS:ääX-Forwarded-*-otsakkeilla.
PHP:n staattinen analyysi (SAST)
Staattinen analyysi lukee koodiasi ajamatta sitä. Kolme avoimen lähdekoodin työkalua riittää useimmille PHP-tiimeille:
-
PHPStan löytää tyyppivirheitä ja bugeja kasvavilla tiukkuustasoilla. Se ei ole tietoturvaskanneri,
mutta moni tietoturvabugi alkaa tyyppisekaannuksena tai tarkistamattomana
null-arvona. -
Psalm lisää taint-analyysin:
se seuraa dataa lähteistä, kuten
$_GETja$_COOKIE, kohteisiin, kutenecho,includeja SQL-kyselyt, ja raportoi ongelmia, kutenTaintedSqljaTaintedHtml. Nykyinen Psalm ajaa sen tyyppitarkistuksen yhteydessä;--taint-analysisottaa sen käyttöön yksittäiselle ajolle, ja--report=results.sarifkirjoittaa SARIF-tiedoston GitHubin code scanningia varten. - Semgrep tukee PHP:tä yleisesti saatavilla olevana kielenä, ja siinä on yhteisön säännöt yleisille PHP-virheille sekä funktiorajat ylittävä datavirta-analyysi kaupallisessa versiossa.
SAST tuottaa vanhassa PHP:ssä paljon kohinaa rakenteellisista syistä: dynaamiset include-lauseet, muuttujamuuttujat,
maagiset metodit, kuten __get() ja __call(), sekä kehysten käytännöt, jotka kuljettavat dataa
säiliöiden ja fasadien läpi, joita analysaattori ei pysty seuraamaan. Työkalu joko arvaa ja raportoi vääriä
positiivisia tai luovuttaa ja ohittaa todellisia datavirtoja.
Vanhaan koodikantaan sen käyttöönotto toimii parhaiten räikkänä. Tallenna nykyiset löydökset lähtötasoksi
(vendor/bin/phpstan analyse --generate-baseline kirjoittaa phpstan-baseline.neon-tiedoston;
Psalmissa on --set-baseline), kaada buildi vain uusista löydöksistä ja nosta tasoa sitä mukaa kuin
lähtötaso pienenee. Kuulemme pitkäikäisiä PHP-koodikantoja ylläpitäviltä tiimeiltä, että tekoälyn autofix toimii hyvin
riippuvuuksien versionnostoissa ja epätasaisesti koodilöydöksissä, joten tarkista SAST-löydösten generoidut korjaukset
yhtä huolellisesti kuin minkä tahansa muun muutoksen. SAST ja SCA vastaavat eri kysymyksiin; katso
SAST vs. SCA.
Composer-riippuvuuksien tietoturva
Laravel- tai Symfony-sovellus asentaa kymmeniä Composer-paketteja, joista useimmat ovat transitiivisia. Composer on saanut viime julkaisuissa todellisia tietoturvaominaisuuksia; tämä osio vastaa Composer 2.11:tä, nykyistä julkaisua.
composer audit
composer audit tarkistaa asentamasi paketit tietoturvatiedotteita vasten, jotka tulevat
Packagist.orgin API:sta. Se kerää
tiedotteita muun muassa GitHub Advisory Databasesta ja FriendsOfPHP/security-advisoriesista. Lisäksi se raportoi hylätyt
paketit ja Composer 2.10:stä alkaen haittaohjelmiksi merkityt pakettiversiot. Kaikki valinnat löydät
Composerin dokumentaatiosta.
composer audit # audit what is installed in vendor/
composer audit --locked # audit composer.lock, whatever is in vendor/
composer audit --no-dev # skip require-dev packages
composer audit --direct # direct dependencies only (Composer 2.11)
composer audit --format=json # table, plain, json or summary
composer audit --abandoned=report # ignore, report or fail on abandoned packages
composer audit --ignore-severity=low
Composer 2.10:stä alkaen paluukoodi on 0, kun ongelmia ei ole, ja 1, kun jokin ei läpäissyt
tarkistusta, joten se toimii CI-porttina ilman tulosteen jäsentämistä. Hylätyt paketit kaatavat tarkistuksen
oletuksena.
Hyväksytyt löydökset kuuluvat composer.json-tiedostoon perusteluineen, ei CI-lippuun, jota kukaan ei
muista. Composer 2.10 korvasi suurimman osan vanhasta audit-konfiguraatiosta policy-lohkolla:
{
"config": {
"policy": {
"advisories": {
"ignore-id": {
"CVE-1234": "Not affected: we never call the vulnerable parser."
}
},
"abandoned": {
"audit": "report"
}
}
}
}
Vanhat avaimet (audit.ignore, audit.abandoned, audit.block-insecure) toimivat
yhä varavaihtoehtona niin kauan kuin vastaava policy-osio puuttuu, mutta ne ovat vanhentuneita. Siirrä
kaikki tiedoteasetukset kerralla: kun policy.advisories on olemassa, jokainen tiedotteisiin liittyvä
audit.*-avain ohitetaan.
Turvattomien versioiden estäminen
Composer 2.9:stä (marraskuu 2025) alkaen composer update ja composer require kieltäytyvät
pakettiversioista, joista on tietoturvatiedote. Asetus oli ennen audit.block-insecure ja on nyt
policy.advisories.block, oletuksena päällä. Composer 2.10 lisäsi haittaohjelmien eston Packagistin
haittaohjelmasyötteestä, ja se koskee myös composer install -komentoa. Composer 2.11 lisäsi valinnaisen
cooldown-käytännön, joka pidättää juuri julkaistuja versioita valitsemasi ajan, kuten "7 days",
koska monet haitalliset versiot löydetään ja poistetaan tuntien tai päivien kuluessa.
CI:ssä kaksi yksityiskohtaa on tärkeää. Ensinnäkin tiedotteisiin perustuva esto koskee versioiden ratkaisemista:
composer.lock-tiedostossa jo oleva versio asentuu edelleen composer install -komennolla
tiedotteen julkaisun jälkeen, joten tarvitset yhä composer audit -tarkistuksen porttina. Toiseksi
--no-blocking poistaa kaikki käytännöt käytöstä yhdeltä komennolta, joten sitä kannattaa etsiä
putkistasi.
Ennen Composer 2.9:ää tavallinen tapa estää turvattomat versiot oli
roave/security-advisories, paketti, joka
ilmoittaa conflict-ristiriidan jokaisen tunnetun haavoittuvan version kanssa ja on koottu GitHub Advisory
Databasesta ja FriendsOfPHP:stä. Se toimii yhä ja auttaa edelleen vanhemmissa Composer-versioissa; Composer 2.9:ssä ja
uudemmissa sisäänrakennettu esto kattaa samat tiedotteet ilman lisäpakettia.
Lukitustiedoston kurinalaisuus
- Tallenna sovellusten
composer.lockversionhallintaan, jotta jokainen ympäristö asentaa samat versiot. -
Käytä CI:ssä ja käyttöönotoissa
composer install-komentoa, älä koskaancomposer update-komentoa, joka ratkaisee uudet versiot lennossa. - Asenna tuotantobuildit valinnalla
--no-dev, jotta testityökalut eivät koskaan päädy toimitukseen. -
Aja
composer validate --strict: se epäonnistuu, kuncomposer.lockei ole ajan tasallacomposer.json-tiedoston kanssa.
Lukitustiedosto (lockfile) kirjaa, mitä Composer ratkaisi. Katso mitä lukitustiedosto ei kirjaa.
Asennuksenaikainen koodi: lisäosat, skriptit ja automaattisesti ladattavat tiedostot
Composer ajaa riippuvuuksiesi koodia useammassa kohdassa kuin useimmat tiimit odottavat, mikä tekee siitä PHP:n vastineen npm:n asennusskripteille:
-
Lisäosat (plugins) ajetaan itse Composerin sisällä. Composer 2.2:sta alkaen
allow-plugins-asetuksessa on lueteltava jokainen lisäosa, jonka ajaminen sallitaan, ja oletuksena se ei salli yhtäkään. Kohtele uutta merkintää siellä kuin uutta riippuvuutta, jolla on shell-pääsy. -
Skriptit ajetaan vain juuritason
composer.json-tiedostosta, eivät riippuvuuksista. Usein ne kuitenkin käynnistävät sovelluksesi. -
Automaattisesti ladattavat tiedostot ajetaan aina, kun autoloader ladataan. Tavallisessa
Laravel-sovelluksessa
composer installpäättyy komentoonphp artisan package:discover, joka lataa autoloaderin, joten riippuvuuden automaattisesti ladattavatfiles-tiedostot suoritetaan CI-jobissasi asennuksen aikana.
Jos jobi ei tarvitse lisäosia tai skriptejä, --no-plugins --no-scripts estää riippuvuuksien koodin
ajamisen asennuksen aikana; aja tarvitsemasi vaiheet erikseen sen jälkeen.
Repositoriolähteet ja dependency confusion
Composer voi asentaa Packagist.orgista, yksityisistä Composer-repositorioista, kuten Private Packagistista,
VCS-repositorioista (Git-URL-osoitteet) ja paikallisista poluista. Yksityisten ja julkisten lähteiden sekoittaminen on
dependency confusionin ydin: jos yksityisen pakettisi nimi on myös Packagist.orgissa
suuremmalla versiolla, kumman saat? Composer 2:ssa
jokainen repositorio on
kanoninen oletuksena: kun Composer löytää paketin repositoriosta, se ei etsi sitä listalta alempaa, ja Packagist.org
on aina viimeisenä. "canonical": false repositoriossa poistaa tämän suojan, joten tee se vain tarkoituksella.
VCS-repositoriot hakevat sen, mitä Git-palvelin tarjoaa tagille, joten kiinnitä ne lukitustiedostossa committiin ja
tarkista niiden muutokset.
Hylätyt ja ylläpitämättömät paketit
Vanhoissa PHP-ympäristöissä on paketteja, joiden ylläpitäjät ovat siirtyneet muualle. Packagist merkitsee paketin
hylätyksi, kun sen ylläpitäjä niin ilmoittaa, usein korvaajan nimeten, ja composer audit kaatuu siihen
oletuksena. Monia ylläpitämättömiä paketteja ei kuitenkaan koskaan virallisesti hylätä. Katso viimeisimmän julkaisun
päivämäärä kaikesta, mikä käsittelee syötteitä, tunnistautumista tai kryptografiaa, ja suunnittele korvaajat ennen kuin
haavoittuvuus pakottaa siihen.
Turvallinen päivittäminen, kun testejä on vähän
Vaikein tapaus on suuri vanha sovellus, jossa on haavoittuva riippuvuus ja vähän testejä. Mikä toimii:
-
Päivitä paketti, joka tuo haavoittuvan mukanaan. Useimmat löydökset ovat transitiivisia; aja
composer why vendor/packagenähdäksesi ketjun ja päivitä ensin ylävirran paketti sen rajoitteen sisällä. -
Päivitä yksi asia kerrallaan.
composer update vendor/package --with-dependenciespitää muutoksen niin pienenä, että sen voi tarkistaacomposer.lock-tiedoston diffistä. - Savutestaa kriittiset polut ensin. Kirjautuminen, kassa, tärkeimmät API-päätepisteet: kourallinen päästä päähän -tarkistuksia löytää useimmat päivitysten rikkoutumiset.
- Lisää karakterisointitestejä sen ympärille, mihin päivitys koskee. Kirjaa, mitä koodi tekee nyt, jotta huomaat, kun päivitys muuttaa sitä.
- Ota käyttöön vaiheittain ja seuraa virhemääriä ennen kuin muutos tavoittaa jokaisen asiakkaan.
Mitä composer audit ei näe
Lukitustiedosto on suunnitelma, ei kirjaus buildista. composer audit lukee vendor/-hakemiston
tai composer.lock-tiedoston, joten se ei näe:
- PHP:tä itseään ja sen laajennuksia, jotka tulevat peruskuvastasi tai käyttöjärjestelmän paketeista (PHP2Shell on yksi esimerkki).
- Docker-kuvaan asennettuja PECL-laajennuksia ja järjestelmäkirjastoja.
- Työkaluja, jotka CI hakee
curl-komennolla, actioneita, joita se ajaa, ja binäärejä, joita lisäosa tai skripti lataa. - Haitallista koodia, jolla ei vielä ole tietoturvatiedotetta tai haittaohjelmamerkintää.
Samat rajoitukset koskevat npm auditia ja pip-auditia niiden ekosysteemeissä.
PHP:n toimitusketjuhyökkäykset
Neljä tapausta näyttää, mihin PHP:hen kohdistuvat toimitusketjuhyökkäykset todella osuvat ja mikä olisi havainnut kunkin.
Maaliskuu 2021: haitalliset commitit php-srciin
28.3.2021 php-src-repositorioon pushattiin kaksi haitallista committia kahden ydinkehittäjän nimissä. PHP-tiimi ilmoitti lopettavansa oman Git-palvelimensa ylläpidon ja tekevänsä GitHub-repositorioista kanonisia. Sen jatkoselvitys totesi, että commitit oli pushattu HTTPS:n yli salasanatunnistautumisella, mahdollisesti vuotaneesta käyttäjätietokannasta peräisin olevilla tunnuksilla, eikä vaarantuneen palvelimen kautta. Mikä olisi havainnut sen: ei mikään omassa putkessasi. Hyökkäys kohdistui itse kieleen, mikä muistuttaa, että PHP-ajoympäristökin on riippuvuus.
Huhtikuu 2021: Composerin komentoinjektio (CVE-2021-29472)
Virhe siinä, miten Composer käsitteli Mercurial-repositorioiden URL-osoitteita, mahdollisti komentoinjektion. Packagist kertoi, että Packagist.org ja Private Packagist olivat teoriassa alttiina muokattujen paketti-URL-osoitteiden kautta, ettei se uskonut haavoittuvuutta hyödynnetyn ja että se korjattiin versioissa Composer 2.0.13 ja 1.10.22. Mikä olisi havainnut sen: itse Composerin pitäminen ajan tasalla kehittäjien koneilla ja CI-kuvissa. Paketinhallinta on osa toimitusketjuasi.
Toukokuu 2023: Packagist-tilien kaappaukset
1.5.2023 hyökkääjä kirjautui neljälle passiiviselle Packagist.org-tilille muualla vuotaneilla salasanoilla. Tilit
hallitsivat yhteensä 14 pakettia, ja hyökkääjä ohjasi paketit forkattuihin repositorioihin. Forkeissa oli muutettu kuvaus
eikä haitallista koodia.
Packagist
palautti ne seuraavana aamuna. Mikä olisi havainnut sen: versionhallintaan tallennettu
composer.lock, koska composer install käyttää lukitustiedostoon kirjattuja lähteitä, sekä
lukitustiedoston diffien tarkistaminen odottamattomien URL-osoitteiden varalta, kuten Packagist itse suosittelee.
Ylläpitäjille: yksilölliset salasanat ja kaksivaiheinen tunnistautuminen.
2026: kaapatut tagit laravel-langissa ja intercom-php:ssä
Vuonna 2026 hyökkääjät julkaisivat varastetuilla käyttöoikeuksilla haitallisia julkaisuja olemassa olevista paketeista.
Packagist mainitsee intercom/intercom-php-paketin (30. huhtikuuta) ja laravel-lang-paketit
(22. toukokuuta) toimitusketjun
tietoturvaa koskevassa päivityksessään. Laravel-langin tapauksessa versiotagit osoittivat haitallisen forkin
committeihin, ja hyötykuorma, src/helpers.php-tiedosto, jonka
Composerin
autoloader latasi, haki tunnusvarastajan, joka kohdistui pilviavaimiin, SSH-avaimiin ja .env-tiedostoihin.
Aikido laski 233 vaarantunutta versiota. Mikä olisi havainnut sen: kun versiot on merkitty, Composer
2.10:n haittaohjelmakäytäntö estää ne jopa composer install -komennossa; sitä ennen cooldown-jakso pitää
upouudet versiot poissa, egress-käytäntö estää hyötykuormaa tavoittamasta palvelintaan, ja kunkin buildin kirjaus
näyttää, mitkä buildit asensivat huonon version.
Sittemmin Packagist on ilmoittanut hylkäävänsä uudelleen tagatut vakaat versiot, julkaisevansa läpinäkyvyyslokia omistajuus- ja tagimuutoksista ja merkinneensä haittaohjelmia Packagist.orgissa maaliskuusta 2026 alkaen.
PHP-buildin suojaaminen CI:ssä
Buildissa koodisi, riippuvuutesi ja salaisuutesi kohtaavat, joten siellä haitallinen paketti tekee eniten vahinkoa. GitHub Actions -työnkulku tuotantobuildille:
name: build
on: [push, pull_request]
permissions:
contents: read # least privilege for GITHUB_TOKEN
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: "8.4"
tools: composer:v2
coverage: none
- run: composer validate --strict
- id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-${{ hashFiles('composer.lock') }}
- run: composer install --no-dev --no-interaction --no-progress --optimize-autoloader
- run: composer audit --no-dev
-
Vähimpien oikeuksien token.
permissions: contents: readestää vaarantunutta vaihetta pushaamasta koodia tai julkaisujaGITHUB_TOKEN-tokenilla. Katso GitHub-tokenin oikeudet. - Kiinnitetyt actionit. Täyttä commit-SHA:ta ei voi siirtää niin kuin tagin; kommentti pitää version luettavana Dependabotille tai Renovatelle.
-
Tallenna välimuistiin lataukset, älä
vendor/-hakemistoa. Composerin latausvälimuistin tallentaminen pitääcomposer install-komennon ajossa lukitustiedostoa vasten jokaisessa jobissa. Palautettuunvendor/-hakemistoon luotetaan sellaisenaan, ja välimuisteja voidaan myrkyttää työnkulkujen yli; katso GitHub Actionsin välimuisti. - Tallenna, mitä asennettiin. Lukitustiedosto kertoo, mitä pitäisi asentaa. Buildista tallennettu SBOM kertoo, mitä asennettiin, mukaan lukien PHP-ajoympäristö, käyttöjärjestelmän paketit ja Composerin ulkopuoliset lataukset. Katso buildin aikana tallennetut SBOM:t.
- Rajoita, mihin buildi voi yhdistää. Composer-buildi tarvitsee Packagistin, GitHubin ja yksityisen repositoriosi. Egress-käytäntö, joka sallii vain ne, estää tunnusvarastajaa lähettämästä löytämäänsä.
SBOM:n generointi PHP-projektille
Tavallinen avoimen lähdekoodin vaihtoehto on
CycloneDX Composer -lisäosa.
Lisäosana se tarvitsee allow-plugins-merkinnän:
composer require --dev cyclonedx/cyclonedx-php-composer
composer CycloneDX:make-sbom --output-format=JSON --output-file=sbom.json
Se lukee composer.json-tiedoston yhdessä asennettujen pakettien tai composer.lock-tiedoston
kanssa, joten se luettelee Composer-riippuvuutesi tarkasti eikä mitään muuta. PHP-ajoympäristö, sen laajennukset,
peruskuvan apt- tai apk-paketit ja jobin lataamat työkalut eivät ole siinä. Buildin aikana tallennettu SBOM kirjaa nekin,
koska se muodostetaan siitä, mitä jobi haki, eikä Composerin tiedostoista. Formaateista ja siitä, mitä SBOM:n pitäisi
sisältää: mikä on SBOM.
PHP-tietoturvatyökalut kerroksittain
| Kerros | Työkalut | Mitä ne kattavat | Mitä ne ohittavat |
|---|---|---|---|
| Koodi | PHPStan, Psalm, Semgrep | Bugit ja tyyppivirheet, saastuneet datavirrat, tunnetut turvattomat mallit | Haavoittuvat riippuvuudet, ajoympäristö, buildi |
| Riippuvuudet | composer audit, Composerin käytännöt, roave/security-advisories, Dependabot tai Renovate |
Julkaistut tietoturvatiedotteet, hylätyt ja merkityt paketit, päivitys-PR:t | Oma koodisi ja kaikki composer.lock-tiedoston ulkopuolinen |
| Kuva | Konttiskannerit | Käyttöjärjestelmän ja kielten paketit buildatussa kuvassa | Se, mitä buildi haki eikä säilyttänyt, ja kuvat, joita kukaan ei skannannut |
| SBOM | CycloneDX Composer -lisäosa | Composer-paketit CycloneDX-SBOM:na | Kaikki Composerin ulkopuolinen |
| Buildi | CRACI | Mitä kukin GitHub Actions -jobi todella asensi Composerista ja kaikista muista lähteistä, seurattuna ajan mittaan | PHP-koodisi (ei SAST:ia); muut CI-järjestelmät kuin GitHub Actions |
Lisää kuvista: konttiskannaus. Vähemmän ja parempia löydöksiä kaikissa kerroksissa: miten vähentää haavoittuvuuskohinaa.
PHP-tietoturva ja CRACI
CRACI on GitHub Actions -runneri, joka tallentaa, mitä kukin jobi todella asentaa. Vaihda runs-on-arvoksi
craci, niin jokainen jobin lataama Composer-paketti tallennetaan paketti-URL-osoitteineen
(pkg:composer/vendor/package) yhdessä käyttöjärjestelmän paketinhallinnan pakettien, konttikuvien,
Git-lähteiden ja muiden saman jobin latausten kanssa, välimuistista palautetut paketit mukaan lukien. Jokainen SBOM:n
komponentti oli buildissa.
CRACI täsmää nämä komponentit tunnettuihin haavoittuvuuksiin ja tarkistaa toimittamiesi ohjelmistojen SBOM:t uudelleen aina, kun uusia tietoturvatiedotteita julkaistaan, joten ensi vuonna julkistettu CVE johtaa sinut yhä buildeihin, jotka sisältävät sen. Käytäntöportit voivat pysäyttää buildin löydösten perusteella, myös buildin, joka sisältää tietyn CVE:n, ja egress-käytäntö rajaa, mihin jobi voi yhdistää.
CRACI ei skannaa PHP-koodiasi: SAST:ia ei ole, ja se on tietoinen valinta. Pidä PHPStan, Psalm tai Semgrep koodikerroksessa. Näe jokainen paketti, jonka PHP-buildisi asentaa.
Milloin CRACI ei ole oikea ratkaisu
-
Viet tuotantoon FTP:llä tai jaettuun webhotelliin ilman CI:tä. Tallennettavaa buildia ei ole. Aloita
composer audit-tarkistuksella ja versionhallintaan tallennetullacomposer.lock-tiedostolla. - Riskisi on pääasiassa omassa koodissasi, kuten vanhassa sovelluksessa, joka on täynnä raakaa SQL:ää. SAST, katselmointi ja testit ovat etusijalla.
- Buildaat GitHub Actionsin ulkopuolella, GitLab CI:ssä tai Bitbucket Pipelinesissa. CRACI tukee tällä hetkellä GitHub Actionsia; GitLab CI ja Jenkins ovat tiekartalla.
PHP-tietoturva: usein kysytyt kysymykset
Miten suojaan PHP-koodin?
Toimi kolmessa kerroksessa. Koodissasi validoi syötteet, käytä valmisteltuja lauseita, luota kehyksen tulosteen escapointiin äläkä koskaan deserialisoi epäluotettavaa dataa. Riippuvuuksissasi tallenna composer.lock versionhallintaan, aja composer audit CI:ssä ja pidä Composerin turvattomien versioiden esto päällä. Buildissasi kiinnitä actionit, rajaa tokenin oikeudet, rajoita verkkoyhteyksiä ja tallenna, mitä jobi asensi.
Mitkä ovat yleisimmät PHP-haavoittuvuudet?
SQL-injektio, cross-site scripting, cross-site request forgery, koodi-injektio eval()-kutsun kautta, objekti-injektio unserialize()-kutsun kautta, tiedostojen sisällyttäminen ja turvattomat lataukset, tyyppimuunnokset ==-vertailussa, heikot istuntoasetukset, SSRF URL-wrappereiden kautta, komentoinjektio ja massasijoitus. Haavoittuvat Composer-riippuvuudet ja paikkaamattomat PHP-versiot, kuten PHP2Shell-bugit, ovat yhtä yleinen tietoturvapoikkeamien syy.
Mikä on paras PHP-tietoturvaskanneri?
Mikään yksittäinen työkalu ei kata PHP-tietoturvaa. Käytä koodillesi staattista analysaattoria, kuten PHPStan, Psalm tai Semgrep, Composer-riippuvuuksillesi composer auditia, kuvalle konttiskanneria ja sille, mitä CI todella asensi, buildin aikaista kirjausta.
Miten composer audit toimii, ja mitä se ei näe?
Se tarkistaa asennetut paketit, tai --locked-valinnalla composer.lock-tiedoston, Packagist.orgin API:n tietoturvatiedotteita vasten ja raportoi hylätyt ja haittaohjelmiksi merkityt paketit. Se palauttaa paluukoodin 1, kun jokin ei läpäise tarkistusta. Se ei näe PHP:tä itseään, PECL-laajennuksia, kuvasi käyttöjärjestelmäpaketteja eikä mitään, mitä CI lataa Composerin ulkopuolelta.
Miten estän turvattomat Composer-paketit CI:ssä?
Käytä Composer 2.9:ää tai uudempaa, joka estää oletuksena versiot, joista on tietoturvatiedote, update- ja require-komennoissa (policy.advisories.block). Koska lukittu haavoittuva versio asentuu silti, aja myös composer audit composer installin jälkeen ja kaada jobi sen paluukoodin perusteella. Älä koskaan käytä --no-blocking-valintaa CI:ssä.
Onko Laravel oletuksena turvallinen?
Enimmäkseen. Blade escapoi tulosteen, Eloquent sitoo kyselyparametrit, mallit on suojattu massasijoitukselta ja istuntotunniste luodaan uudelleen kirjautumisessa. Nämä oletukset lakkaavat toimimasta, kun koodi käyttää {!! !!}-syntaksia, whereRaw()-kutsua syötteen kanssa, $guarded = [] -asetusta tai APP_DEBUG=true-asetusta tuotannossa, tai kun APP_KEY vuotaa.
Miten päivitän riippuvuudet vanhassa PHP-sovelluksessa, jossa on vähän testejä?
Päivitä ylävirran paketti, joka tuo haavoittuvan mukanaan, yksi paketti kerrallaan komennolla composer update vendor/package --with-dependencies. Savutestaa kriittiset polut, lisää karakterisointitestejä sen ympärille, mihin päivitys koskee, ja ota muutos käyttöön vaiheittain.
Miten generoin SBOM:n PHP-projektille?
CycloneDX Composer -lisäosa (cyclonedx/cyclonedx-php-composer) generoi sen Composer-paketeistasi komennolla composer CycloneDX:make-sbom. Se luettelee vain Composer-paketit. Buildin aikana tallennettu SBOM kirjaa myös PHP-ajoympäristön, käyttöjärjestelmän paketit ja muut buildin lataukset.
Mikä on PHP2Shell?
PHP2Shell on nimitys haavoittuvuuksille CVE-2026-109618 ja CVE-2026-109619. Ne ovat kaksi kriittistä haavoittuvuutta itse PHP:ssä, ja ne voivat mahdollistaa etäkoodin suorittamisen ilman tunnistautumista. Ne on korjattu versioissa PHP 8.5.12, 8.4.27, 8.3.36 ja 8.2.35, jotka julkaistiin 21.10.2026. Katso PHP2Shell.
Näe jokainen paketti, jonka PHP-buildisi asentaa
Aja yksi GitHub Actions -työnkulku CRACI:lla ja vertaa sen tallennettua SBOM:ia composer.lock-tiedostoosi.
Varaa demo