Jak szyfrujesz i odszyfrowujesz ciąg PHP?


225

Chodzi mi o to że:

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

Może coś takiego:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • Jak to zrobić w PHP?

Próbowałem użyć Crypt_Blowfish, ale nie działało to dla mnie.


35
@Rogue Nie chce skrótu, chce szyfrowania symetrycznego (jak AES), po prostu nie wie, jak się nazywa. (A teraz robi :))
Patashu

jak bezpieczne musi być?

3
@ 夏 期 劇場, nie „szyfrujesz” szyfrowania symetrycznego, używasz klucza. Klucz należy utrzymywać w tajemnicy. Sól może być publiczna bez uszczerbku dla bezpieczeństwa (o ile sól każdego jest inna) i jest to termin używany do mieszania haseł.
Patashu

2
Potrzebujesz Soli (klucza prywatnego), klucza publicznego i algorytmu szyfrowania, takiego jak AES-256: wpy.me/blog/15-encrypt-and-decrypt-data-in-php-using-aes-256
wappy

8
@CristianFlorea Autor tego postu na blogu używa terminów, które po prostu nie mają najmniejszego sensu w kontekście szyfrowania symetrycznego. W AES nie ma klucza publicznego ani soli. Jest jeden klucz; to musi być trzymane w tajemnicy. W niektórych trybach działania istnieje IV, który nie musi być tajny, ale IV nie jest solą (w zależności od trybu może mieć zupełnie inne wymagania) i nie musi być tajny, podczas gdy rzeczywisty klucz szyfrowania absolutnie nie może być publiczny. Klucz publiczny / prywatny dotyczy szyfrowania asymetrycznego, ale nie ma nic wspólnego z AES.
cpast

Odpowiedzi:


410

Zanim zrobisz cokolwiek dalej, spróbuj zrozumieć różnicę między szyfrowaniem a uwierzytelnianiem i dlaczego prawdopodobnie chcesz szyfrowania uwierzytelnionego, a nie tylko szyfrowania .

Aby zaimplementować uwierzytelnianie szyfrowane, chcesz zaszyfrować, a następnie MAC. Kolejność szyfrowania i uwierzytelniania jest bardzo ważna! Jedna z istniejących odpowiedzi na to pytanie popełniła ten błąd; podobnie jak wiele bibliotek kryptograficznych napisanych w PHP.

Należy unikać wdrażania własnej kryptografii , a zamiast tego korzystać z bezpiecznej biblioteki napisanej i sprawdzonej przez ekspertów w dziedzinie kryptografii.

Aktualizacja: PHP 7.2 zapewnia teraz libsodium ! Aby zapewnić najwyższy poziom bezpieczeństwa, zaktualizuj system, aby korzystał z PHP 7.2 lub nowszego i postępuj zgodnie z zaleceniami libsodium zawartymi w tej odpowiedzi.

Użyj libsodium, jeśli masz dostęp do PECL (lub sodu_compat, jeśli chcesz libsodium bez PECL); w przeciwnym razie ...
Użyj szyfrowania / defuse / php ; nie rzucaj własną kryptografią!

Obie wyżej wymienione biblioteki ułatwiają i bezbolesne wdrożenie uwierzytelnionego szyfrowania we własnych bibliotekach.

Jeśli nadal chcesz pisać i wdrażać własną bibliotekę kryptograficzną, wbrew konwencjonalnej wiedzy każdego eksperta w dziedzinie kryptografii w Internecie, musisz wykonać te kroki.

Szyfrowanie:

  1. Szyfruj za pomocą AES w trybie CTR. Możesz także użyć GCM (co eliminuje potrzebę oddzielnego MAC). Dodatkowo ChaCha20 i Salsa20 (dostarczone przez libsodium ) są szyframi strumieniowymi i nie wymagają specjalnych trybów.
  2. O ile nie wybrałeś GCM powyżej, powinieneś uwierzytelnić tekst zaszyfrowany za pomocą HMAC-SHA-256 (lub, w przypadku szyfrów strumieniowych, Poly1305 - większość interfejsów API libsodium robi to za Ciebie). MAC powinien obejmować IV, a także tekst zaszyfrowany!

Deszyfrowanie:

  1. O ile nie zostanie użyty Poly1305 lub GCM, ponownie oblicz MAC zaszyfrowanego tekstu i porównaj go z MAC, który został wysłany hash_equals(). Jeśli to się nie powiedzie, przerwij.
  2. Odszyfruj wiadomość.

Inne uwagi dotyczące projektu:

  1. Nigdy nie kompresuj niczego. Tekst zaszyfrowany nie podlega kompresji; kompresowanie tekstu jawnego przed szyfrowaniem może prowadzić do wycieku informacji (np. PRZESTĘPSTWA i NARUSZENIE w TLS).
  2. Upewnij się, że używasz mb_strlen()i mb_substr(), używając '8bit'trybu zestawu znaków, aby zapobiec mbstring.func_overloadproblemom.
  3. IV powinny generować przy użyciu CSPRNG ; Jeśli używasz mcrypt_create_iv(), NIE UŻYWAJMCRYPT_RAND !
  4. O ile nie używasz konstrukcji AEAD, ZAWSZE szyfruj, a następnie MAC!
  5. bin2hex(), base64_encode()itp. mogą wyciekać informacje o twoich kluczach szyfrujących poprzez synchronizację pamięci podręcznej. Unikaj ich, jeśli to możliwe.

Nawet jeśli zastosujesz się do podanych tu wskazówek, wiele może pójść nie tak z kryptografią. Zawsze poproś eksperta od kryptografii o sprawdzenie Twojej implementacji. Jeśli nie masz szczęścia, aby być osobistym przyjacielem studenta kryptografii na lokalnym uniwersytecie, zawsze możesz skorzystać z forum Cryptography Stack Exchange w celu uzyskania porady.

Jeśli potrzebujesz profesjonalnej analizy swojego wdrożenia, zawsze możesz zatrudnić renomowany zespół konsultantów ds. Bezpieczeństwa, aby sprawdzili Twój kod kryptograficzny PHP (ujawnienie: mój pracodawca).

Ważne: kiedy nie używać szyfrowania

Nie szyfruj haseł . Chcesz hash nich zamiast, korzystając z jednej z tych algorytmów mieszania hasłem:

Nigdy nie używaj funkcji skrótu ogólnego przeznaczenia (MD5, SHA256) do przechowywania haseł.

Nie szyfruj parametrów adresów URL . To nieodpowiednie narzędzie do pracy.

Przykład szyfrowania łańcucha PHP za pomocą Libsodium

Jeśli korzystasz z PHP <7.2 lub w inny sposób nie masz zainstalowanego libsodium, możesz użyć sodium_compat, aby osiągnąć ten sam wynik (choć wolniej).

<?php
declare(strict_types=1);

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 * @throws RangeException
 */
function safeEncrypt(string $message, string $key): string
{
    if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new RangeException('Key is not the correct size (must be 32 bytes).');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

    $cipher = base64_encode(
        $nonce.
        sodium_crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
    sodium_memzero($message);
    sodium_memzero($key);
    return $cipher;
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 * @throws Exception
 */
function safeDecrypt(string $encrypted, string $key): string
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    $plain = sodium_crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
    if (!is_string($plain)) {
        throw new Exception('Invalid MAC');
    }
    sodium_memzero($ciphertext);
    sodium_memzero($key);
    return $plain;
}

Następnie, aby to przetestować:

<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Halite - Libsodium Made Easy

Jednym z projektów, nad którymi pracuję, jest biblioteka szyfrowania o nazwie Halite , która ma na celu uczynienie libsodium łatwiejszym i bardziej intuicyjnym.

<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;

// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');

$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Cała kryptografia jest obsługiwana przez libsodium.

Przykład z defuse / php-encryption

<?php
/**
 * This requires https://github.com/defuse/php-encryption
 * php composer.phar require defuse/php-encryption
 */

use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

require "vendor/autoload.php";

// Do this once then store it somehow:
$key = Key::createNewRandomKey();

$message = 'We are all living in a yellow submarine';

$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Uwaga : Crypto::encrypt()zwraca dane wyjściowe w formacie szesnastkowym.

Zarządzanie kluczami szyfrującymi

Jeśli masz ochotę użyć „hasła”, przestań już teraz. Potrzebujesz losowego 128-bitowego klucza szyfrowania, a nie hasła, które można zapamiętać.

Możesz przechowywać klucz szyfrujący do długoterminowego użytku, tak jak:

$storeMe = bin2hex($key);

I na żądanie możesz go pobrać w następujący sposób:

$key = hex2bin($storeMe);

I zdecydowanie zalecamy tylko przechowywanie losowo wygenerowanego klucza do długotrwałego stosowania zamiast jakichkolwiek hasła jako klucza (lub w celu uzyskania klucza).

Jeśli korzystasz z biblioteki Defuse:

„Ale naprawdę chcę użyć hasła”.

To zły pomysł, ale dobrze, oto jak to zrobić bezpiecznie.

Najpierw wygeneruj losowy klucz i zapisz go w stałej.

/**
 * Replace this with your own salt! 
 * Use bin2hex() then add \x before every 2 hex characters, like so:
 */
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");

Pamiętaj, że dodajesz dodatkową pracę i możesz użyć tej stałej jako klucza i zaoszczędzić sobie dużo bólu serca!

Następnie użyj PBKDF2 (podobnie jak ten), aby uzyskać odpowiedni klucz szyfrowania na podstawie hasła zamiast szyfrować go bezpośrednio.

/**
 * Get an AES key from a static password and a secret salt
 * 
 * @param string $password Your weak password here
 * @param int $keysize Number of bytes in encryption key
 */
function getKeyFromPassword($password, $keysize = 16)
{
    return hash_pbkdf2(
        'sha256',
        $password,
        MY_PBKDF2_SALT,
        100000, // Number of iterations
        $keysize,
        true
    );
}

Nie używaj tylko 16-znakowego hasła. Twój klucz szyfrujący zostanie komicznie zepsuty.


3
Nie szyfruj haseł , mieszaj je password_hash()i sprawdzaj za pomocą password_verify().
Scott Arciszewski,

2
„Nigdy nie kompresuj niczego”. Masz na myśli to, co HTTP, Spdy i inne protokoły? Przed TLS? Porada absolutystyczna?
Tiberiu-Ionuț Stan

1
@ScottArciszewski Podoba mi się twój komentarz na temat klucza „// Zrób to raz, a następnie zapisz jakoś:„ .. jakoś, lol :)) a co powiesz na przechowywanie tego „klucza” (który jest obiektem) jako zwykłego ciągu znaków, na stałe? Potrzebuję samego klucza, jako łańcucha. Czy mogę jakoś go zdobyć z tego obiektu? Dzięki
Andrew,

2
Dzięki, musiałem tylko zmodyfikować jedną linię, aby twoje przykłady sodu działały:function getKeyFromPassword($password, $keysize = \Sodium\CRYPTO_SECRETBOX_KEYBYTES)
Alexey Ozerov

1
Łał! +1 za wzmiankę o nieszyfrowaniu parametrów adresu URL. Naprawdę podobał mi się artykuł, który udostępniasz: paragonie.com/blog/2015/09/…
Lynnell Emmanuel Neri

73

Jestem spóźniony na imprezę, ale szukając właściwego sposobu, by to zrobić, natknąłem się na tę stronę, która była jednym z najlepszych wyników wyszukiwania w Google, więc chciałbym podzielić się swoją opinią na temat problemu, który uważam za aktualne w momencie pisania tego postu (początek 2017 r.). Od PHP 7.1.0 mcrypt_decrypti mcrypt_encryptbędzie przestarzałe, więc przy tworzeniu przyszłego kodu sprawdzającego należy użyć openssl_encrypt i openssl_decrypt

Możesz zrobić coś takiego:

$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

Ważne : używa trybu EBC , który nie jest bezpieczny. Jeśli chcesz prostego rozwiązania bez udziału w awaryjnym kursie inżynierii kryptograficznej, nie pisz tego sam, po prostu skorzystaj z biblioteki.

Możesz również użyć innych metod rozdrabniacza, w zależności od potrzeb bezpieczeństwa. Aby dowiedzieć się o dostępnych metodach rębaka, zobacz funkcję openssl_get_cipher_methods .


10
Dziękuję, jestem zaskoczony, że ta prosta i jasna odpowiedź nie jest już tak pozytywna. Wolałbym nie czytać 10-stronicowej dyskusji na ten temat, takiej jak najlepsza odpowiedź, gdy wszystko, czego chcę, to szyfrować / odszyfrowywać prosty ciąg znaków.
laurent

4
To nie jest bezpieczna odpowiedź. Tryb EBC nie powinien być używany. Jeśli chcesz „prostej i jasnej odpowiedzi”, po prostu skorzystaj z biblioteki .
Scott Arciszewski

2
@ScottArciszewski, tak Przyznaję, że mówiłem zbyt szybko, szukając prostego kodu. Od tego czasu dodałem IV i używam CBC we własnym kodzie, co jest wystarczające do mojego użytku.
laurent

Przeczytaj to i ponownie rozważ . W trybie CBC atakujący może całkowicie zmienić wiadomość. Jeśli wiadomość jest plikiem, atakujący może przerzucić bity i wyskoczyć z pliku calc.exe. Ryzyko nieuwierzytelnionego szyfrowania jest poważne.
Scott Arciszewski

7
Wszystko zależy od przypadku użycia! Zdarzają się przypadki, gdy jest to DOKŁADNIE. Na przykład chcę przekazać parametr GET ze strony do strony, powiedzmy, prod_id=123ale po prostu nie chcę, aby 123 był czytelny dla wszystkich, jednak nawet gdyby mogli go odczytać, nie będzie problemu. Atakujący, który może zamienić 123 na wartość niestandardową, nie spowoduje żadnej szkody, będzie mógł uzyskać jedynie szczegóły dotyczące jakiegokolwiek innego produktu, ale przeciętny użytkownik Joe nie będzie miał pojęcia, jak uzyskać szczegółowe informacje na temat na przykład produkt 124. W takim scenariuszu jest to idealne rozwiązanie, dla bezpieczeństwa nie ma mowy!
Emil Borconi,

43

Czego nie robić

OSTRZEŻENIE:
Ta odpowiedź wykorzystuje EBC . EBC nie jest trybem szyfrowania, to tylko element składowy. Użycie EBC, jak pokazano w tej odpowiedzi, w rzeczywistości nie szyfruje bezpiecznie łańcucha. Nie używaj EBC w swoim kodzie. Zobacz odpowiedź Scotta na dobre rozwiązanie.

Mam to na sobie. Właściwie znalazłem odpowiedź na google i właśnie coś zmodyfikowałem. Rezultat jest jednak całkowicie niepewny.

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>

8
Możesz użyć użytkownika „MCRYPT_MODE_CBC” zamiast „MCRYPT_MODE_ECB”, aby zapewnić większe bezpieczeństwo.
Parixit,

10
Ramesh, to dlatego, że otrzymujesz surowe zaszyfrowane dane. Możesz uzyskać lepszą wersję zaszyfrowanych danych, używając base64, w następujący sposób: base64_encode(encrypt($string))- Aby je odszyfrować:decrypt(base64_decode($encrypted))
mendezcode

82
OSTRZEŻENIE: jest to niepewne . Trybu EBC nie należy używać w przypadku ciągów, EBC nie przyjmuje IV, nie jest uwierzytelniany, używa starego szyfru (Blowfish) zamiast AES, klucz nie jest binarny itp. Społeczność SO powinna naprawdę przestać głosować na szyfrowanie / deszyfrowanie, które po prostu „działa” i rozpoczyna głosowanie odpowiedzi, o których wiadomo, że są bezpieczne. Jeśli nie wiesz tego na pewno, nie głosuj.
Maarten Bodewes

3
Zrobiłem to już przez mcrypt_encryptzastąpienie przykładowego kodu : php.net/manual/en/function.mcrypt-encrypt.php . Zauważ, że przejrzenie go teraz powinno mieć na końcu rtrimpostać for "\0".
Maarten Bodewes

4
Prawidłowa odpowiedź to użycie czegoś takiego jak defuse / php-encryption zamiast pisania własnego kodu mcrypt.
Scott Arciszewski

18

Dla frameworka Laravel

Jeśli używasz frameworka Laravel, łatwiej jest go zaszyfrować i odszyfrować za pomocą funkcji wewnętrznych.

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

Uwaga: pamiętaj, aby ustawić losowy ciąg 16, 24 lub 32 znaków w opcji klucza pliku config / app.php. W przeciwnym razie zaszyfrowane wartości nie będą bezpieczne.


1
Jasne, może być łatwy w użyciu. Ale czy to jest bezpieczne? Jak rozwiązuje problemy w stackoverflow.com/a/30159120/781723 ? Czy używa uwierzytelnionego szyfrowania? Czy unika luk w zabezpieczeniach kanału bocznego i zapewnia kontrolę równości w czasie stałym? Czy używa naprawdę losowego klucza, a nie hasła / hasła? Czy używa odpowiedniego trybu działania? Czy prawidłowo generuje losowe IV?
DW

10

Zaktualizowano

Wersja gotowa na PHP 7. Wykorzystuje funkcję openssl_encrypt z biblioteki PHP OpenSSL .

class Openssl_EncryptDecrypt {
    function encrypt ($pure_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
        $hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        return $iv.$hmac.$ciphertext_raw;
    }
    function decrypt ($encrypted_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = substr($encrypted_string, 0, $ivlen);
        $hmac = substr($encrypted_string, $ivlen, $sha2len);
        $ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
        $calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        if(function_exists('hash_equals')) {
            if (hash_equals($hmac, $calcmac)) return $original_plaintext;
        } else {
            if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
        }
    }
    /**
     * (Optional)
     * hash_equals() function polyfilling.
     * PHP 5.6+ timing attack safe comparison
     */
    function hash_equals_custom($knownString, $userString) {
        if (function_exists('mb_strlen')) {
            $kLen = mb_strlen($knownString, '8bit');
            $uLen = mb_strlen($userString, '8bit');
        } else {
            $kLen = strlen($knownString);
            $uLen = strlen($userString);
        }
        if ($kLen !== $uLen) {
            return false;
        }
        $result = 0;
        for ($i = 0; $i < $kLen; $i++) {
            $result |= (ord($knownString[$i]) ^ ord($userString[$i]));
        }
        return 0 === $result;
    }
}

define('ENCRYPTION_KEY', '__^%&Q@$&*!@#$%^&*^__');
$string = "This is the original string!";

$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);

1
To jest lepsza i znacznie bezpieczniejsza wersja. Dzięki, działa również zgodnie z oczekiwaniami.

1
jak tworzysz i gdzie przechowujesz klucz szyfrujący?
user2800464

7

Uwaga historyczna: Zostało to napisane w czasie PHP4. Nazywamy to teraz „starszym kodem”.

Zostawiłem tę odpowiedź do celów historycznych - ale niektóre metody są obecnie przestarzałe, metoda szyfrowania DES nie jest zalecaną praktyką itp.

Nie zaktualizowałem tego kodu z dwóch powodów: 1) Nie pracuję już ręcznie metodami szyfrowania w PHP i 2) Ten kod nadal służy celowi, do którego został przeznaczony: w celu zademonstrowania minimalnej, uproszczonej koncepcji działania szyfrowania w PHP.

Jeśli znajdziesz podobnie uproszczone źródło „Szyfrowanie PHP dla manekinów”, które może zacząć od 10-20 linii kodu, daj mi znać w komentarzach.

Ponadto zapraszamy do obejrzenia klasycznego odcinka minimalistycznej odpowiedzi na szyfrowanie we wczesnej epoce PHP4.


Idealnie masz - lub możesz uzyskać - dostęp do biblioteki PHP mcrypt, ponieważ jest to z pewnością popularna i bardzo przydatna różnorodność zadań. Oto przegląd różnych rodzajów szyfrowania i przykładowy kod: Techniki szyfrowania w PHP

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

Kilka ostrzeżeń:

1) Nigdy nie używaj szyfrowania odwracalnego lub „symetrycznego”, gdy działa skrót jednokierunkowy.

2) Jeśli dane są naprawdę wrażliwe, takie jak numer karty kredytowej lub numer ubezpieczenia społecznego, przestań; potrzebujesz więcej niż zapewnia zwykła porcja kodu, ale potrzebujesz biblioteki kryptograficznej zaprojektowanej do tego celu i znacznej ilości czasu na zbadanie niezbędnych metod. Co więcej, oprogramowanie szyfrujące stanowi prawdopodobnie <10% bezpieczeństwa wrażliwych danych. To jak przeprojektowanie elektrowni jądrowej - zaakceptuj, że zadanie jest niebezpieczne i trudne i poza twoją wiedzą, jeśli tak jest. Kary finansowe mogą być ogromne, więc lepiej skorzystać z usługi i wysłać na nie odpowiedzialność.

3) Każdy rodzaj łatwego do wdrożenia szyfrowania, wymienionego tutaj, może w rozsądny sposób chronić mało ważne informacje, które chcesz chronić przed wścibskimi oczami lub ograniczyć narażenie w przypadku przypadkowego / umyślnego wycieku. Ale widząc, jak klucz jest przechowywany w postaci zwykłego tekstu na serwerze sieciowym, jeśli mogą uzyskać dane, mogą uzyskać klucz odszyfrowujący.

Tak czy inaczej, baw się dobrze :)


9
Eww, DES. Gdzie jest AES?
Patashu

2
Dzięki! Ale niektóre problemy. Dostaję M�������f=�_=dziwne postacie jako zaszyfrowane. Czy nie mogę uzyskać prostych znaków? Jak: 2a2ffa8f13220befbe30819047e23b2c. Czy nie mogę także zmienić DŁUGOŚCI $key_value(ustalonej na 8 ???) i DŁUGOŚCI wyjścia $encrypted_text? (czy nie może być 32 lub 64 długości, czy cokolwiek dłuższego ??)
期 劇場

3
@ 夏 期 劇場 Rezultatem szyfrowania są dane binarne. Jeśli chcesz, aby był czytelny dla ludzi, użyj na nim kodowania base64 lub hex. „Czy nie mogę zmienić długości wartości klucza?” Różne algorytmy szyfrowania symetrycznego mają różne wymagania dotyczące wartości klucza. „i DŁUGOŚĆ wyniku ...” Długość zaszyfrowanego tekstu musi być co najmniej tak długa jak oryginalny tekst, w przeciwnym razie nie ma wystarczającej ilości informacji, aby odtworzyć oryginalny tekst. (Jest to zastosowanie zasady Pigeonhole.) BTW, powinieneś użyć AES zamiast DES. DES jest łatwo łamliwy i nie jest już bezpieczny.
Patashu

8
mcrypt_ecb został DEPRECATED od PHP 5.5.0 Poleganie na tej funkcji jest wysoce odradzane. php.net/manual/en/function.mcrypt-ecb.php
Hafez Divandari

1
@BrianDHall Powodem, dla którego wciąż spadają głosy, jest to, że tryb EBC nie jest bezpieczny (użyj CBC, CTR, GCM lub Poly1305), DES jest słaby (chcesz AES, czyli MCRYPT_RIJNDAEL_128), a zaszyfrowane teksty powinny być uwierzytelnione ( hash_hmac(), zweryfikowane z hash_equals()).
Scott Arciszewski,

7

Jeśli nie chcesz używać biblioteki (co powinieneś), użyj czegoś takiego (PHP 7):

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);

Czy może to być zamiennik stackoverflow.com/a/16606352/126833 ? Używam tego pierwszego, dopóki mój host nie zaktualizował się do PHP 7.2.
anjanesh

@anjanesh nie będziesz w stanie odszyfrować starych danych za pomocą tego (różne algorytmy + ten również sprawdza podpis)
Ascon

2
W twoim przypadku prawdopodobnie powinno to zrobić:define("ENCRYPTION_KEY", "123456*"); $string = "This is the original data string!"; $encrypted = openssl_encrypt($string, 'BF-ECB', ENCRYPTION_KEY); $decrypted = openssl_decrypt($encrypted,'BF-ECB',ENCRYPTION_KEY);
Ascon

To jest super!
anjanesh

2

Są to kompaktowe metody szyfrowania / deszyfrowania ciągów za pomocą PHP przy użyciu AES256 CBC :

function encryptString($plaintext, $password, $encoding = null) {
    $iv = openssl_random_pseudo_bytes(16);
    $ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
    return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}

function decryptString($ciphertext, $password, $encoding = null) {
    $ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
    if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
    return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}

Stosowanie:

$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");

0

Poniższy kod działa w php dla wszystkich ciągów znaków specjalnych

   // Encrypt text --

    $token = "9611222007552";

      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);  
      $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));  
      $crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
    echo    $crypted_token;
    //unset($token, $cipher_method, $enc_key, $enc_iv);

    // Decrypt text  -- 

    list($crypted_token, $enc_iv) = explode("::", $crypted_token);  
      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
      $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
    echo   $token;
    //unset($crypted_token, $cipher_method, $enc_key, $enc_iv);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.