Skip to content

Alt om WordPress, webutvikling — og mer til

🔒 Kryptering og dekryptering av filer i PHP: OpenSSL og Sodium i stedet for mcrypt

🔒 Kryptering og dekryptering av filer i PHP: OpenSSL og Sodium i stedet for mcrypt

Krypter en fil før du lagrer den på serveren, og vær trygg på at ingen, ikke engang serveradministratoren, kan lese den uten nøkkelen. Høres ut som et grunnleggende behov, men da mcrypt forsvant fra PHP, sluttet den velkjente tilnærmingen å fungere.

Utvidelsen mcrypt ble merket som utdatert i PHP 7.1 og fullstendig fjernet i PHP 7.2 tilbake i 2017. I dag, på PHP 8, ender et forsøk på å kalle mcrypt_encrypt() i en fatal feil. Samtidig ligger konfidensielle filer fortsatt på servere: databasesikkerhetskopier, CSV-filer med personopplysninger, PDF-kontrakter, ordreeksporter.

Gode nyheter: PHP tilbyr to fungerende mekanismer rett ut av boksen, OpenSSL og Sodium. Ingen av dem krever installasjon av ekstra utvidelser på moderne hosting, begge er raskere og sikrere enn mcrypt. Nedenfor følger en praktisk guide til kryptering og dekryptering av filer på PHP 8 med kode du kan kopiere og kjøre.

💡 Rask oversikt:

  • Hvorfor mcrypt er dødt og hvilke PHP-versjoner som er berørt
  • Steg-for-steg filkryptering via OpenSSL med AES-256-CBC
  • Dekryptering med beskyttelse mot datamanipulasjon via HMAC
  • Sodium-alternativ for PHP 7.2 og nyere
  • Når du bør velge OpenSSL og når du bør velge Sodium

Hvorfor mcrypt ikke lenger er et alternativ

Mcrypt-biblioteket har ikke vært oppdatert siden 2007. Det ble funnet kritiske sårbarheter i koden, og ingen vedlikeholdere var igjen. PHP-teamet tok en beslutning: i PHP 7.1 ble utvidelsen merket som utdatert, og i PHP 7.2, lansert i november 2017, ble den fullstendig ekskludert fra kjernen.

Hvis du migrerer et gammelt prosjekt med mcrypt, sjekk phpinfo(). På PHP 7.2+ finnes det ingen linje med «mcrypt support: enabled». Å kalle mcrypt_encrypt(), mcrypt_decrypt() eller strømfiltrene mcrypt.tripledes / mdecrypt.tripledes returnerer feilmeldingen «Call to undefined function».

Teknisk sett er mcrypt tilgjengelig via PECL med kommandoen pecl install mcrypt. Men å installere en ustøttet utvidelse med kjente sårbarheter på en produksjonsserver for ett enkelt legacyskript er en dårlig idé. Skriv om krypteringen med OpenSSL: den er innebygd i PHP siden versjon 5.3 og kommer ikke til å forsvinne.

Filkryptering via OpenSSL, steg for steg

OpenSSL i PHP representeres av funksjonene openssl_encrypt() og openssl_decrypt(). De jobber med rådata og støtter dusinvis av algoritmer, fra AES-128-CBC til AES-256-GCM. For filer bruker vi AES-256-CBC: den er kryptografisk sterk og krever ikke PHP 7.1, i motsetning til GCM med ekstra tag-parametere.

Steg 1: generer krypteringsnøkkel

Nøkkelen er hovedhemmeligheten i hele oppsettet. Den må være kryptografisk tilfeldig, ikke manuelt oppdiktet. Ingen «secret-password» fra eksempler, kun openssl_random_pseudo_bytes().

Skriptet nedenfor genererer en 256-bits nøkkel og skriver den ut i et format for innsetting i wp-config.php. Kjør én gang via kommandolinjen og lagre resultatet:

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";

Funksjonen openssl_random_pseudo_bytes(32) returnerer 32 byte med kryptografisk kvalitetstilfeldighet. base64_encode konverterer binære data til en streng som er praktisk å lagre i konfigurasjonsfiler. Nøkkelen må ligge utenfor dokumentroten, i wp-config.php eller .env, men ikke i temakode.

Steg 2: funksjon for filkryptering

Skriptet leser en fil fra disk, krypterer med AES-256-CBC-algoritmen, legger til en tilfeldig IV i begynnelsen og en HMAC-signatur for integritetsverifisering, og lagrer resultatet. Legg til koden i functions.php i et child theme eller i en egendefinert plugin.

Advarsel: ta en fullstendig sikkerhetskopi før du kjører på en produksjonsserver. Test kryptering-dekryptering på en filkopi i en testkatalog. Hvis nøkkelen mistes, er det umulig å dekryptere data, AES-256 kan ikke knekkes med brute force.

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}

Her er hva som skjer linje for linje:

  • openssl_cipher_iv_length('aes-256-cbc') returnerer 16, lengden på initialiseringsvektoren for denne algoritmen.
  • openssl_random_pseudo_bytes($ivLength) oppretter en tilfeldig IV. Den gjør at identiske data kryptert med samme nøkkel produserer ulik chiffertekst ved hver kjøring.
  • OPENSSL_RAW_DATA ber funksjonen returnere binære data, ikke base64. Vi lagrer rå chiffertekst for kompakthet.
  • hash_hmac('sha256', ...) beregner en sjekksum fra IV-og-chiffertekst-pakken. Under dekryptering vil vi beregne HMAC på nytt og sammenligne: hvis data ble endret eller ødelagt, vil ikke sammenligningen stemme.
  • Filen lagres i formatet: [IV 16 bytes][HMAC 32 bytes][ciphertext]. Ingen skilletegn, posisjonene er fastsatt av lengdene.

Steg 3: dekrypteringsfunksjon

Den omvendte prosessen: les IV, les HMAC, les chiffertekst, beregn HMAC på nytt og sammenlign via hash_equals(), dekrypter. Kode legges til i samme fil:

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}

Nøkkelpoeng: hash_equals() i stedet for ===. Vanlig strengsammenligning er sårbar for timingangrep, en angriper kan plukke HMAC byte for byte ved å måle serverens responstid. hash_equals() sammenligner strenger i konstant tid uavhengig av hvilket tegn de divergerte ved.

Brukseksempel med reelle filstier

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;

Funksjonene er universelle: de fungerer med alle filtyper, bilder, PDF, CSV, SQL-dumper. Størrelsen begrenses kun av tilgjengelig RAM, siden filen leses inn i minnet i sin helhet. For gigabyte-store filer vil strømmende prosessering med chunk-bufring være nødvendig, men for det overveldende flertallet av praktiske oppgaver er denne koden tilstrekkelig.

Alternativ: Sodium (libsodium)

Utvidelsen Sodium er innebygd i PHP fra og med versjon 7.2 og ble en del av kjernen i PHP 8.1. Den tilbyr et enklere API sammenlignet med OpenSSL: ingen grunn til å manuelt håndtere IV og HMAC, autentisert kryptering fungerer rett ut av boksen.

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}

Merk: Sodium verifiserer integritet selv. Hvis data ble endret eller nøkkelen er feil, returnerer sodium_crypto_secretbox_open() ganske enkelt false, ingen separate HMAC-sjekker kreves. Koden er halvparten så lang.

Én ulempe: Sodium krever PHP 7.2 eller nyere. Hvis prosjektet kjører på PHP 7.0-7.1, er OpenSSL eneste alternativ. Men i praksis i 2026 er det allerede vanskelig å finne hosting med PHP under 7.4: ifølge WordPress.org-data for juni 2026 er andelen PHP 7.0-7.1 mindre enn 0,3% av alle installasjoner.

OpenSSL eller Sodium: to utvalgskriterier

Valget mellom de to tilnærmingene koker ned til to faktorer:

  • PHP-versjon. PHP 7.2+, velg Sodium, det er sikrere som standard og lar deg ikke rote til HMAC-implementeringen. PHP 7.0-7.1, kun OpenSSL. Under PHP 7.0 er det på tide å oppdatere serveren, ikke finne opp løsninger med PECL-mcrypt.
  • Dataportabilitet mellom miljøer. OpenSSL er tilgjengelig overalt, inkludert PHP 5.3+. Hvis krypterte filer må kunne leses på dev-server, i produksjon og hos klienten, er OpenSSL mer pålitelig fra et kompatibilitetsståsted. Sodium-chiffertekst vil kun dekrypteres der Sodium finnes.

I praksis bruker vi OpenSSL på prosjekter der kompatibilitet mellom ulike miljøer er viktig. Sodium der hele stakken er oppdatert til PHP 8 og sikkerhet kommer først.

I videoen bryter Dave Hollingworth ned begge tilnærmingene i detalj med kodedemonstrasjon og forklaring av de kryptografiske primitivene bak hver. Materialet utfyller artikkelen: grensetilfeller vises, installasjon av defuse/php-encryption-biblioteket via Composer og ytelses-sammenligning av OpenSSL og Sodium på reelle data.

⁉️🤔 Ofte stilte spørsmål

Kan jeg bruke md5() eller sha1() til filkryptering?

Nei. md5() og sha1() er hashfunksjoner, de er irreversible per definisjon. En kryptert fil må kunne dekrypteres tilbake, og en hash kan ikke dekrypteres. Hasher brukes til integritetsverifisering (som HMAC i koden over) og passordlagring via password_hash(), men ikke til innholdskryptering.

Hva gjør jeg hvis krypteringsnøkkelen mistes?

Å dekryptere data uten nøkkelen er umulig. Lagre nøkkelen i wp-config.php utenfor dokumentroten og sikkerhetskopier den separat fra fil- og databasesikkerhetskopier. Ikke legg nøkkelen i et Git-repository, legg til wp-config.php i .gitignore eller bruk miljøvariabler.

Hvorfor trengs IV når nøkkelen allerede er hemmelig?

Uten en tilfeldig IV produserer identiske data kryptert med samme nøkkel identisk chiffertekst. En angriper som ser gjentakende blokker får informasjon om filstrukturen. IV gjør hver krypteringskjøring unik: samme fil kryptert to ganger med én nøkkel produserer to ulike chiffertekster.

Kan jeg kryptere store filer, flere gigabyte?

Funksjonene over leser filen inn i minnet i sin helhet, for gigabyte-data vil dette føre til minneutarming. For strømmende kryptering bruk openssl_encrypt() i en løkke med chunk-bufring (for eksempel 1 MB hver) eller defuse/php-encryption-biblioteket, som støtter strømmemodus rett ut av boksen.

Fungerer denne koden på PHP 8.3?

Ja. Både OpenSSL og Sodium er fullt støttet i PHP 8. Koden er testet på gjeldende PHP-versjoner og bruker ikke utdaterte funksjoner. På PHP 8.3 opprettholdes kompatibilitet, det er ingen bakoverinkompatibiliteter i OpenSSL-utvidelsen.

Filkryptering i 2026: praktisk dom

Mcrypt forlot PHP, og det er til det bedre. To innebygde erstatningsverktøy er både sikrere og raskere og krever ikke PECL-dans med en sjaman. OpenSSL fungerer overalt, Sodium er enklere og mer pålitelig som standard.

Kort oppsummert: nytt prosjekt på PHP 8, start med Sodium, koden blir renere. Migrering av gammel kode fra mcrypt, skriv om til OpenSSL, det er tilgjengelig selv på PHP 7.0. Og kryptografiens hovedregel: lagre nøkler atskilt fra krypterte data. Nøkkeltap er lik datatap, og brute force hjelper ikke her.

Start med et testskript på en filkopi: forsikre deg om at syklusen «kryptert → dekryptert → byte-for-byte samsvarte» fungerer uten feil. Og hvilken metode bruker du i dine prosjekter, OpenSSL, Sodium eller noe annet? Skriv i kommentarene.