Skip to content

Kaikki WordPressistä, web-kehityksestä — ja paljon muuta

🔒 Tiedostojen salaaminen ja salauksen purkaminen PHP:ssä: OpenSSL ja Sodium mcryptin sijaan

🔒 Tiedostojen salaaminen ja salauksen purkaminen PHP:ssä: OpenSSL ja Sodium mcryptin sijaan

Salaa tiedosto ennen kuin tallennat sen palvelimelle ja ole varma, ettei kukaan, ei edes palvelimen ylläpitäjä, voi lukea sitä ilman avainta. Kuulostaa perustarpeelta, mutta mcryptin poistuttua PHP:stä tuttu lähestymistapa lakkasi toimimasta.

Mcrypt-laajennus merkittiin vanhentuneeksi PHP 7.1:ssä ja poistettiin kokonaan PHP 7.2:ssa vuonna 2017. Nykyään PHP 8:lla yritys kutsua mcrypt_encrypt()-funktiota päättyy vakavaan virheeseen. Samaan aikaan palvelimilla lojuu yhä luottamuksellisia tiedostoja: tietokantavarmuuskopioita, henkilötietoja sisältäviä CSV-tiedostoja, PDF-sopimuksia, tilausvientejä.

Hyviä uutisia: PHP tarjoaa kaksi toimivaa mekanismia suoraan paketista, OpenSSL ja Sodium. Kumpikaan ei vaadi ylimääräisten laajennusten asentamista nykyaikaisilla hosting-palveluilla, ja molemmat ovat nopeampia ja turvallisempia kuin mcrypt. Alla on käytännön opas tiedostojen salaamiseen ja salauksen purkamiseen PHP 8:lla koodeineen, jotka voit kopioida ja ajaa.

💡 Pikaopas:

  • Miksi mcrypt on kuollut ja mitä PHP-versioita asia koskee
  • Tiedoston salaus vaihe vaiheelta OpenSSL:llä käyttäen AES-256-CBC:tä
  • Salauksen purku ja tietojen eheyden suojaus HMAC:lla
  • Sodium-vaihtoehto PHP 7.2:lle ja uudemmille
  • Milloin valita OpenSSL ja milloin Sodium

Miksi mcrypt ei ole enää vaihtoehto

Mcrypt-kirjastoa ei ole päivitetty vuoden 2007 jälkeen. Sen koodista löydettiin kriittisiä haavoittuvuuksia, eikä ylläpitäjiä ollut jäljellä. PHP-tiimi teki päätöksen: PHP 7.1:ssä laajennus merkittiin vanhentuneeksi, ja PHP 7.2:ssa, joka julkaistiin marraskuussa 2017, se suljettiin kokonaan pois ytimestä.

Jos olet siirtämässä vanhaa projektia, jossa on mcrypt, tarkista phpinfo(). PHP 7.2+:ssa ei ole riviä "mcrypt support: enabled". mcrypt_encrypt(), mcrypt_decrypt() tai stream-suodattimien mcrypt.tripledes / mdecrypt.tripledes kutsuminen palauttaa "Call to undefined function" -virheen.

Teknisesti mcrypt on saatavilla PECL:n kautta pecl install mcrypt -komennolla. Mutta tuottamattoman ja tunnetusti haavoittuvan laajennuksen asentaminen tuotantopalvelimelle yhden vanhan skriptin takia on huono idea. Kirjoita salaus uusiksi OpenSSL:llä: se on sisäänrakennettu PHP:ssä versiosta 5.3 lähtien eikä ole katoamassa minnekään.

Tiedoston salaus OpenSSL:llä, vaihe vaiheelta

OpenSSL PHP:ssä koostuu openssl_encrypt()- ja openssl_decrypt()-funktioista. Ne toimivat raakadatan kanssa ja tukevat kymmeniä algoritmeja AES-128-CBC:stä AES-256-GCM:ään. Tiedostoille käytämme AES-256-CBC:tä: se on kryptografisesti vahva eikä vaadi PHP 7.1:tä toisin kuin GCM, jossa on ylimääräisiä tag-parametreja.

Vaihe 1: luo salausavain

Avain on koko järjestelmän tärkein salaisuus. Sen on oltava kryptografisesti satunnainen, ei käsin keksitty. Ei "secret-password"-tyyppisiä esimerkkejä, ainoastaan openssl_random_pseudo_bytes().

Alla oleva skripti generoi 256-bittisen avaimen ja tulostaa sen muodossa, jossa se voidaan lisätä wp-config.php-tiedostoon. Aja kerran komentoriviltä ja tallenna tulos:

1<?php
2// Generating a random 256-bit key (32 bytes)
3$encryption_key = base64_encode(openssl_random_pseudo_bytes(32));
4echo "define('FILE_ENCRYPTION_KEY', '" . $encryption_key . "');\n";

openssl_random_pseudo_bytes(32)-funktio palauttaa 32 tavua kryptografisesti laadukasta satunnaisuutta. base64_encode muuntaa binääridatan merkkijonoksi, jota on kätevä säilyttää asetustiedostoissa. Avaimen on sijaittava dokumenttijuuren ulkopuolella, wp-config.php- tai .env-tiedostossa, mutta ei teeman koodissa.

Vaihe 2: tiedoston salausfunktio

Skripti lukee tiedoston levyltä, salaa sen AES-256-CBC-algoritmilla, lisää satunnaisen IV:n alkuun ja HMAC-allekirjoituksen eheyden tarkistusta varten, ja tallentaa tuloksen. Lisää koodi lapsiteeman functions.php-tiedostoon tai omaan lisäosaan.

Varoitus: ennen kuin ajat tätä tuotantopalvelimella, tee täydellinen varmuuskopio. Testaa salaus ja salauksen purku tiedoston kopiolla testihakemistossa. Jos avain katoaa, tietojen purkaminen on mahdotonta, AES-256:ta ei voi murtaa raa'alla voimalla.

1<?php
2function encrypt_file(string $sourcePath, string $destPath, string $key): bool
3{
4 if (!file_exists($sourcePath)) {
5 throw new RuntimeException('Source file not found: ' . $sourcePath);
6 }
7
8 $plaintext = file_get_contents($sourcePath);
9 if ($plaintext === false) {
10 throw new RuntimeException('Failed to read file');
11 }
12
13 $cipher = 'aes-256-cbc';
14 $ivLength = openssl_cipher_iv_length($cipher);
15 $iv = openssl_random_pseudo_bytes($ivLength);
16
17 $ciphertext = openssl_encrypt(
18 $plaintext,
19 $cipher,
20 base64_decode($key),
21 OPENSSL_RAW_DATA,
22 $iv
23 );
24
25 if ($ciphertext === false) {
26 throw new RuntimeException('Encryption error');
27 }
28
29 // HMAC signature for integrity verification during decryption
30 $hmac = hash_hmac('sha256', $iv . $ciphertext, base64_decode($key), true);
31
32 // File format: IV (16 bytes) + HMAC (32 bytes) + ciphertext
33 $result = file_put_contents($destPath, $iv . $hmac . $ciphertext);
34
35 return $result !== false;
36}

Mitä tässä tapahtuu rivi riviltä:

  • openssl_cipher_iv_length('aes-256-cbc') palauttaa 16, alustusvektorin pituuden tälle algoritmille.
  • openssl_random_pseudo_bytes($ivLength) luo satunnaisen IV:n. Se saa samalla avaimella salatun identtisen datan tuottamaan eri salatekstin jokaisella ajokerralla.
  • OPENSSL_RAW_DATA käskee funktiota palauttamaan binääridataa, ei base64-muotoa. Tallennamme raa'an salatekstin tiiviyden vuoksi.
  • hash_hmac('sha256', ...) laskee tarkistussumman IV- ja salatekstipaketista. Salauksen purun yhteydessä laskemme HMAC:n uudelleen ja vertaamme: jos dataa on muokattu tai se on vioittunut, vertailu ei täsmää.
  • Tiedosto tallennetaan muodossa: [IV 16 bytes][HMAC 32 bytes][ciphertext]. Ei erottimia, paikat määräytyvät pituuksien mukaan.

Vaihe 3: salauksen purkufunktio

Käänteinen prosessi: lue IV, lue HMAC, lue salateksti, laske HMAC uudelleen ja vertaa hash_equals()-funktiolla, pura salaus. Koodi lisätään samaan tiedostoon:

1<?php
2function decrypt_file(string $sourcePath, string $key): string|false
3{
4 if (!file_exists($sourcePath)) {
5 throw new RuntimeException('Encrypted file not found: ' . $sourcePath);
6 }
7
8 $data = file_get_contents($sourcePath);
9 if ($data === false) {
10 throw new RuntimeException('Failed to read file');
11 }
12
13 $cipher = 'aes-256-cbc';
14 $ivLength = openssl_cipher_iv_length($cipher);
15 $hmacLength = 32; // sha256 = 32 bytes
16
17 $iv = substr($data, 0, $ivLength);
18 $hmac = substr($data, $ivLength, $hmacLength);
19 $ciphertext = substr($data, $ivLength + $hmacLength);
20
21 // Integrity check: recompute HMAC and compare
22 $calculatedHmac = hash_hmac(
23 'sha256',
24 $iv . $ciphertext,
25 base64_decode($key),
26 true
27 );
28
29 if (!hash_equals($hmac, $calculatedHmac)) {
30 throw new RuntimeException('File corrupted or key invalid');
31 }
32
33 $plaintext = openssl_decrypt(
34 $ciphertext,
35 $cipher,
36 base64_decode($key),
37 OPENSSL_RAW_DATA,
38 $iv
39 );
40
41 return $plaintext;
42}

Avainkohta: hash_equals() eikä ===. Tavallinen merkkijonojen vertailu on altis ajoitushyökkäyksille, hyökkääjä voi arvata HMAC:n tavu kerrallaan mittaamalla palvelimen vasteaikaa. hash_equals() vertaa merkkijonoja vakioajassa riippumatta siitä, mistä kohdasta ne eroavat.

Käyttöesimerkki oikeilla poluilla

1<?php
2$key = FILE_ENCRYPTION_KEY; // from wp-config.php
3
4// Encrypting the database backup
5encrypt_file(
6 __DIR__ . '/backup.sql',
7 __DIR__ . '/backup.sql.enc',
8 $key
9);
10
11// Decrypting and serving for download
12$decrypted = decrypt_file(__DIR__ . '/backup.sql.enc', $key);
13header('Content-Type: application/octet-stream');
14header('Content-Disposition: attachment; filename="backup.sql"');
15echo $decrypted;

Funktiot ovat yleispäteviä: ne toimivat minkä tahansa tiedostotyypin, kuvien, PDF:ien, CSV-tiedostojen, SQL-dumppien kanssa. Kokoa rajoittaa vain käytettävissä oleva RAM, koska tiedosto luetaan muistiin kokonaan. Gigatavun kokoisille tiedostoille tarvitaan suoratoistokäsittelyä puskuroimalla paloissa, mutta valtaosaan käytännön tehtävistä tämä koodi riittää.

Vaihtoehto: Sodium (libsodium)

Sodium-laajennus on sisäänrakennettu PHP:ssä versiosta 7.2 alkaen ja tuli osaksi ydintä PHP 8.1:ssä. Se tarjoaa yksinkertaisemman API:n verrattuna OpenSSL:ään: IV:tä ja HMAC:ia ei tarvitse hallita manuaalisesti, todennettu salaus toimii suoraan paketista.

1<?php
2function sodium_encrypt_file(string $sourcePath, string $destPath, string $key): bool
3{
4 $plaintext = file_get_contents($sourcePath);
5 $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
6 $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, base64_decode($key));
7 return file_put_contents($destPath, $nonce . $ciphertext) !== false;
8}
9
10function sodium_decrypt_file(string $sourcePath, string $key): string|false
11{
12 $data = file_get_contents($sourcePath);
13 $nonce = substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
14 $ciphertext = substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
15 $result = sodium_crypto_secretbox_open($ciphertext, $nonce, base64_decode($key));
16 return $result !== false ? $result : false;
17}

Huomaa: Sodium varmistaa eheyden itse. Jos dataa on muokattu tai avain on väärä, sodium_crypto_secretbox_open() yksinkertaisesti palauttaa false, erillisiä HMAC-tarkistuksia ei tarvita. Koodi on puolet lyhyempi.

Yksi haittapuoli: Sodium vaatii PHP 7.2:n tai uudemman. Jos projekti toimii PHP 7.0-7.1:llä, ainoa vaihtoehto on OpenSSL. Mutta käytännössä vuonna 2026 hosting-palvelun löytäminen PHP:llä, joka on alle 7.4, on jo vaikeaa: WordPress.org:n kesäkuun 2026 tietojen mukaan PHP 7.0-7.1:n osuus on alle 0,3% kaikista asennuksista.

OpenSSL vai Sodium: kaksi valintakriteeriä

Valinta näiden kahden lähestymistavan välillä riippuu kahdesta tekijästä:

  • PHP-versio. PHP 7.2+, ota Sodium, se on oletuksena turvallisempi eikä anna sinun sotkea HMAC-toteutusta. PHP 7.0-7.1, vain OpenSSL. Alle PHP 7.0, on aika päivittää palvelin, ei keksiä kiertoteitä PECL-mcryptillä.
  • Datan siirrettävyys ympäristöjen välillä. OpenSSL on saatavilla kaikkialla, mukaan lukien PHP 5.3+. Jos salattujen tiedostojen on oltava luettavissa dev-palvelimella, tuotannossa ja asiakkaalla, OpenSSL on yhteensopivuuden kannalta luotettavampi. Sodium-salatekstin purku onnistuu vain siellä, missä Sodium on olemassa.

Käytännössä käytämme OpenSSL:ää projekteissa, joissa yhteensopivuus eri ympäristöjen välillä on tärkeää. Sodiumia siellä, missä koko pino on päivitetty PHP 8:aan ja turvallisuus on etusijalla.

Videolla Dave Hollingworth käy läpi molemmat lähestymistavat yksityiskohtaisesti koodiesittelyn ja kunkin taustalla olevien kryptografisten primitiivien selityksen kera. Materiaali täydentää artikkelia: siinä näytetään reunatapaukset, defuse/php-encryption-kirjaston asennus Composerin kautta ja OpenSSL:n ja Sodiumin suorituskyvyn vertailu oikealla datalla.

⁉️🤔 Usein kysytyt kysymykset

Voinko käyttää md5()- tai sha1()-funktiota tiedostojen salaamiseen?

Et. md5() ja sha1() ovat tiivistefunktioita, ne ovat määritelmällisesti peruuttamattomia. Salatun tiedoston on purettava takaisin, eikä tiivistettä voi purkaa. Tiivisteitä käytetään eheyden tarkistamiseen (kuten HMAC yllä olevassa koodissa) ja salasanojen tallentamiseen password_hash()-funktiolla, mutta ei sisällön salaamiseen.

Mitä tehdä, jos salausavain katoaa?

Tietojen purkaminen ilman avainta on mahdotonta. Säilytä avain wp-config.php-tiedostossa dokumenttijuuren ulkopuolella ja varmuuskopioi se erillään tiedosto- ja tietokantavarmuuskopioista. Älä laita avainta Git-repositorioon, lisää wp-config.php .gitignore-tiedostoon tai käytä ympäristömuuttujia.

Miksi IV:tä tarvitaan, jos avain on jo salainen?

Ilman satunnaista IV:tä identtinen data samalla avaimella salattuna tuottaa identtisen salatekstin. Hyökkääjä, joka näkee toistuvia lohkoja, saa tietoa tiedoston rakenteesta. IV tekee jokaisesta salauskerrasta ainutlaatuisen: sama tiedosto kahdesti samalla avaimella salattuna tuottaa kaksi erilaista salatekstiä.

Voinko salata suuria, usean gigatavun tiedostoja?

Yllä olevat funktiot lukevat tiedoston muistiin kokonaan, gigatavun datalla tämä johtaa muistin loppumiseen. Suoratoistosalausta varten käytä openssl_encrypt()-funktiota silmukassa puskuroimalla paloissa (esimerkiksi 1 Mt kerrallaan) tai defuse/php-encryption-kirjastoa, joka tukee suoratoistotilaa suoraan paketista.

Toimiiko tämä koodi PHP 8.3:lla?

Kyllä. Sekä OpenSSL että Sodium ovat täysin tuettuja PHP 8:ssa. Koodi on testattu nykyisillä PHP-versioilla eikä käytä vanhentuneita funktioita. PHP 8.3:ssa yhteensopivuus säilyy, OpenSSL-laajennuksessa ei ole taaksepäin yhteensopimattomia muutoksia.

Tiedostojen salaus vuonna 2026: käytännön tuomio

Mcrypt poistui PHP:stä, ja se on vain hyvä asia. Kaksi sisäänrakennettua korvaavaa työkalua ovat sekä turvallisempia että nopeampia eivätkä vaadi PECL-tanssia tamburiinin kanssa. OpenSSL toimii kaikkialla, Sodium on oletuksena yksinkertaisempi ja luotettavampi.

Lyhyesti: uusi projekti PHP 8:lla, aloita Sodiumilla, koodista tulee selkeämpää. Vanhan koodin siirtäminen mcryptistä, kirjoita uusiksi OpenSSL:lle, se on saatavilla jopa PHP 7.0:ssa. Ja kryptografian pääsääntö: säilytä avaimet erillään salatusta datasta. Avaimen menetys tarkoittaa datan menetystä, eikä raaka voima auta tässä.

Aloita testiskriptillä tiedoston kopiolla: varmista, että sykli "salattu → purettu → tavu tavulta täsmää" toimii virheittä. Ja mitä menetelmää sinä käytät projekteissasi, OpenSSL:ää, Sodiumia vai jotain muuta? Kirjoita kommentteihin.