Ajankohtainen tilanne: PHP2Shell. Kaksi kriittistä PHP-haavoittuvuutta (CVSS 9.2) sallii haittakoodin suorittamisen ilman kirjautumista. Korjaukset 21.10.2026 alkaen.Mitä nyt on tehtävä →

Tiedostolataukset turvallisesti PHP:ssä: PHP-latausten tietoturva

Latauslomakkeet ovat yleinen reitti web shelleille. Näin tarkistatte tiedostotyypit oikein, tallennatte tiedostot turvallisesti ja estätte palvelinta suorittamasta ladattuja skriptejä.

Tilanne Lukuaika 4 minOppaat

Lyhyesti: PHP-latausten tietoturvassa ratkaisee yksittäisen tarkistuksen sijaan yhdistelmä: tarkista tiedostotyyppi palvelimella, nimeä tiedostot uudelleen, sijoita ne web-juuren ulkopuolelle äläkä salli skriptien suoritusta latauskansiossa. Tarkistamaton latauslomake on yksi klassisista reiteistä, joilla hyökkääjät tuovat takaoven verkkopalvelimelle.

Miksi latauslomakkeet ovat suosittu sisääntuloreitti

Jokainen lomake, joka ottaa vastaan tiedostoja, kirjoittaa vierasta dataa palvelimellenne. Näitä ovat työhakemuslomakkeet, asiakasportaalit, kuvagalleriat, tukialueet ja monet CMS- ja verkkokauppalaajennukset. Jos hyökkääjä onnistuu tallentamaan sinne tiedoston, jonka verkkopalvelin sen jälkeen suorittaa PHP:nä, hänellä on pysyvä takaovi.

Johdolle tämä tarkoittaa: harmittomalta vaikuttava ansioluettelojen lataus voi pahimmillaan johtaa koko verkkopalvelimen haltuunottoon. Lisäksi latauskansioissa on usein henkilötietoja, jotka väärin määritettynä ovat julkisesti haettavissa.

Ylläpitäjille ja kehittäjille se tarkoittaa: sovelluskoodin ja palvelinasetusten on sovittava yhteen. Toisen tason heikkouden kompensoi usein toinen, kun molemmat on toteutettu siististi. Miten jo lisätyt tiedostot löydetään, kuvataan oppaassa Web shellien tunnistaminen ja poistaminen.

PHP-latausten tietoturvan perussäännöt

Seuraavat säännöt vastaavat OWASP Cheat Sheet -ohjeiden suosituksia, ja ne ovat osoittautuneet toimiviksi käytössä:

  • Sallittujen luettelo eikä kiellettyjen luettelo: Ottakaa vastaan vain nimenomaisesti sallitut tiedostotyypit, esimerkiksi PDF, JPG ja PNG. Yksittäisten päätteiden kieltolistat voi kiertää muunnelmilla ja kaksoispäätteillä.
  • Tarkistakaa sisältö, ei vain pääte: Selvittäkää todellinen tyyppi palvelimella finfo:lla. $_FILES-taulukon MIME-tyyppi on lähettäjän ilmoitus, eikä se ole luotettava.
  • Uusi tiedostonimi: Älkää koskaan käyttäkö alkuperäistä nimeä tiedostonimenä. Luokaa satunnainen nimi ja liittäkää siihen sallittujen luettelon pääte.
  • Tallennus web-juuren ulkopuolelle: Tallentakaa tiedostot niin, ettei niihin pääse suoraan URL-osoitteella. Toimitus tehdään komentosarjalla, joka tarkistaa oikeudet.
  • Rajatkaa koko: Sovelluksessa, PHP:ssä ja verkkopalvelimessa.
  • Ei suoritusta: Jos lataukset on pakko pitää web-juuressa, kytkekää skriptien suoritus pois kyseisestä hakemistosta.
  • Tarkistus vain palvelimella: Selaimen JavaScript-tarkistukset parantavat käytettävyyttä, mutta eivät suojaa.

Turvallinen esimerkki finfolla ja satunnaisella nimellä

Seuraava esimerkki näyttää turvallisen käsittelyn ytimen. Se on tietoisesti tiivis, ja se on upotettava sovellukseenne, siis kirjautumisen, oikeuksien tarkistuksen, CSRF-suojan ja lokituksen kanssa.

<?php
declare(strict_types=1);

const MAX_BYTES  = 5 * 1024 * 1024; // 5 MB
const ERLAUBT    = [
    'application/pdf' => 'pdf',
    'image/jpeg'      => 'jpg',
    'image/png'       => 'png',
];
const ZIELORDNER = '/var/www/daten/uploads'; // web-juuren ulkopuolella

$datei = $_FILES['dokument'] ?? null;

if (!is_array($datei) || $datei['error'] !== UPLOAD_ERR_OK) {
    http_response_code(400);
    exit('Upload fehlgeschlagen.');
}

if ($datei['size'] > MAX_BYTES || !is_uploaded_file($datei['tmp_name'])) {
    http_response_code(400);
    exit('Datei ungültig.');
}

$finfo = new finfo(FILEINFO_MIME_TYPE);
$typ   = $finfo->file($datei['tmp_name']);

if (!isset(ERLAUBT[$typ])) {
    http_response_code(415);
    exit('Dateityp nicht erlaubt.');
}

$name = bin2hex(random_bytes(16)) . '.' . ERLAUBT[$typ];
$ziel = ZIELORDNER . '/' . $name;

if (!move_uploaded_file($datei['tmp_name'], $ziel)) {
    http_response_code(500);
    exit('Speichern fehlgeschlagen.');
}

chmod($ziel, 0640);
// Tallenna alkuperäinen nimi tietokantaan vain tekstinä
// ja escapoi se htmlspecialchars()-funktiolla jokaisessa tulosteessa.

Kuvien kohdalla kannattaa tehdä vielä yksi vaihe: koodata kuva uudelleen GD:llä tai Imagickilla ja hylätä samalla metatiedot. Se ei korvaa muita tarkistuksia, mutta pienentää jäännösriskiä upotetun vieraan datan vuoksi.

Tuntemattomista lähteistä tuleville asiakirjoille, esimerkiksi hakemuksille tai reklamaatiokuville, virustarkistus ennen välittämistä on lisäksi järkevä. Se suojaa vähemmän verkkopalvelinta kuin työntekijöitä, jotka avaavat tiedostot myöhemmin työasemillaan. Kirjatkaa lisäksi jokainen lataus lokiin ajan, tilin, tiedostokoon ja tunnistetun tyypin kera, jotta epätavalliset kuviot voi myöhemmin selvittää.

CMS-järjestelmissä ja verkkokaupoissa sisäänrakennetut mediakirjastot tuovat yleensä mukanaan omat tarkistuksensa. Riskialttiimpia ovat lomakelaajennukset, tuontitoiminnot ja itse ohjelmoidut lisäsivut, jotka tallentavat tiedostoja kirjaston ohi. Juuri nämä kohdat kannattaa katsoa tarkistuksessa ensin.

Vinkki

Toimittakaa tiedostot suojatusta kansiosta otsakkeilla Content-Disposition: attachment ja X-Content-Type-Options: nosniff. Silloin selain ei näytä ongelmallista sisältöä suoraan vaan tarjoaa sen latauksena.

PHP:n suorituksen kytkeminen pois latauskansiossa

Vaikka väärän sisällön sisältävä tiedosto livahtaisi läpi, verkkopalvelimen ei saa suorittaa sitä komentosarjana. Tämä toinen suojakerros kuuluu jokaiselle palvelimelle, joka tallentaa lataukset web-juureen.

Nginxille ja PHP-FPM:lle:

location ^~ /uploads/ {
    location ~* \.(php|phtml|phar)$ {
        deny all;
    }
}

Apachelle, mieluiten suoraan VirtualHostissa .htaccess-tiedoston sijaan, jotta sovellus ei voi ylikirjoittaa sääntöä:

<Directory "/var/www/html/uploads">
    AllowOverride None
    <FilesMatch "\.(php|phtml|phar)$">
        Require all denied
    </FilesMatch>
</Directory>

Jos PHP toimii Apache-moduulina (mod_php), voitte samassa lohkossa asettaa lisäksi php_admin_flag engine off. Muutoksen jälkeen varmistakaa harmittomalla testitiedostolla, joka tulostaa vain tekstin, että palvelin vastaa siihen kohdistuvaan pyyntöön koodilla 403.

Kokorajat ja yhteys PHP2Shelliin

Lataukset liittyvät läheisesti PHP-asetuksiin post_max_size ja upload_max_filesize. PHP2Shellin yhteydessä php.net suosittelee väliaikaiseksi toimeksi asettamaan post_max_size-arvon 8M, jos latauksia ei tarvita, tai 32M latausten kanssa, ja pitämään upload_max_filesize sen alapuolella. Se ei ole täydellinen suoja eikä korvaa päivitystä korjattuun PHP-versioon.

; php.ini tai FPM-pooli (php_admin_value), käynnistä sen jälkeen kaikki worker-prosessit uudelleen
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Jos verkkosivusto ei tarvitse lainkaan latauksia, file_uploads = Off on siistein ratkaisu. Mitkä asetukset ovat järkeviä korjaukseen asti, kerrotaan sivulla Pikatoimet ilman korjausta. Latauskansioiden sopivat oikeudet kuvataan oppaassa Verkkopalvelimen tiedosto-oikeudet.

Tyypilliset virheet latausominaisuuksissa

Olemassa olevia sovelluksia läpikäydessä samat mallit toistuvat. Tarkistakaa lomakkeenne kohdennetusti näiden kohtien varalta:

  • Päätettä tarkistetaan vain osittain, jolloin useita päätteitä sisältävät tiedostonimet livahtavat läpi.
  • Lomakkeen alkuperäinen nimi päätyy tallennuspolkuun. Se avaa reittejä polkujen manipulointiin ja olemassa olevien tiedostojen ylikirjoittamiseen.
  • Lataukset ovat julkisia ennustettavilla nimillä. Hakemuksissa tai sopimuksissa se on myös tietosuojaongelma.
  • Latauskansio on verkkopalvelimen kirjoitettavissa ja samalla suoritettavissa.
  • Vanhoja latauslaajennuksia tai lomakelisäosia ei päivitetä, vaikka valmistaja on julkaissut tietoturvapäivityksiä.
  • Virheitä ei kirjata lokiin. Hylätyt lataukset ovat kuitenkin varhainen merkki kohdennetuista yrityksistä.

Miten PHP SOS auttaa konkreettisesti?

  • Tarkistamme latausominaisuutenne koodissa ja palvelinasetuksissa ja nimeämme konkreettiset heikkoudet prioriteetin kera.
  • Otamme käyttöön suoritusesteen latauskansiossa, sopivat oikeudet ja kokorajat osana palvelimen koventamista.
  • Epäilyn tapauksessa käymme latauskansiot läpi lisättyjen komentosarjojen varalta ja puhdistamme ne haittaohjelmien poiston kautta.
  • Halutessanne annamme penetraatiotestin varmistaa toteutuksen riippumattomasti.

Usein kysytyt kysymykset

Riittääkö, että tiedostopääte tarkistetaan latauksessa?

Ei. Pääte ja selaimen ilmoittama MIME-tyyppi tulevat lähettäjältä, ja ne voi valita vapaasti. Tarkistakaa sisältö palvelimella, antakaa uusi tiedostonimi ja estäkää skriptien suoritus latauskansiossa.

Minne ladatut tiedostot kannattaa tallentaa?

Mieluiten julkisen verkkohakemiston ulkopuolelle. Toimituksen hoitaa silloin komentosarja, joka tarkistaa ensin, saako käyttäjä nähdä tiedoston. Näin tiedostoja ei voi avata suoraan eikä suorittaa.

Ovatko SVG-tiedostot kuvina vaarattomia?

Eivät automaattisesti. SVG on XML, ja se voi sisältää aktiivista sisältöä. Sallikaa SVG vain, jos puhdistatte tiedostot tai toimitatte ne ainoastaan latauksena sopivalla Content-Disposition-otsakkeella.

Onko latauksen koolla jotain tekemistä PHP2Shellin kanssa?

On, välillisesti. php.net suosittelee väliaikaiseksi toimeksi pienempiä arvoja asetuksille post_max_size ja upload_max_filesize. Se pienentää riskiä, mutta ei korvaa päivitystä korjattuun PHP-versioon eikä kaikkien worker-prosessien uudelleenkäynnistystä.

Lähteet

Pikayhteys

Jokainen tunti on tärkeä. Kirjoittakaa meille nyt.

Kuvatkaa lyhyesti järjestelmä, PHP-versio ja havainnot. Otamme yhteyttä, selvitämme tilanteen ja työskentelemme etänä, kunnes tapaus on käsitelty loppuun ja dokumentoitu.

Pikaapu