Jak zaszyfrować i odszyfrować Łańcuch PHP?

Chodzi mi o to, że:

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

Może coś w stylu:

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

Próba użycia Crypt_Blowfish, Ale mi nie wyszło.

Author: Scott Arciszewski, 2013-05-17

6 answers

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

Aby zaimplementować uwierzytelnione szyfrowanie, chcesz zaszyfrować 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 kryptografii biblioteki napisane w PHP.

Należy unikać implementacji własnej kryptografii[26], a zamiast tego korzystać z bezpiecznej biblioteki napisanej i recenzowanej przez ekspertów ds. kryptografii.

Aktualizacja: PHP 7.2 dostarcza teraz libsodium ! Zaktualizowano PHP do wersji 7.2 lub wyższej i postępuj zgodnie z poradami libsodium w tej odpowiedzi.

Użyj libsodium, jeśli masz dostęp do PECL (lub sodium_compat jeśli chcesz libsodium bez PECL); inaczej...
użyj defuse / PHP-encryption; nie obracaj własnej kryptografii!

Obie biblioteki połączone powyżej ułatwiają i bezbolesne implementowanie uwierzytelnionego szyfrowania do własnych bibliotek.

Jeśli nadal chcesz napisać i wdrożyć własną bibliotekę kryptograficzną, wbrew konwencjonalnej mądrości każdego eksperta w dziedzinie kryptografii w Internecie, są to kroki, które musisz wykonać weź.

Szyfrowanie:

  1. Szyfruj za pomocą AES w trybie CTR. Możesz również użyć GCM (co eliminuje potrzebę oddzielnego komputera MAC). Dodatkowo, ChaCha20 i Salsa20 (dostarczone przez libsodium) są szyframi strumieniowymi i nie wymagają specjalnych trybów.
  2. jeśli nie wybrałeś GCM powyżej, powinieneś uwierzytelnić szyfrogram za pomocą HMAC-SHA-256(lub, w przypadku szyfrów strumieniowych, Poly1305-większość API libsodium robi to za Ciebie). MAC powinien pokrywać kroplówkę jak i szyfrogram!

Odszyfrowanie:

  1. jeśli nie użyto Poly1305 lub GCM, Przelicz ponownie MAC szyfrogramu i porównaj go z komputerem MAC, który został wysłany za pomocą hash_equals(). Jeśli zawiedzie, przerwij.
  2. Odszyfruj wiadomość.

Inne Względy Projektowe:

  1. nigdy niczego nie Kompresuj. Zaszyfrowany tekst nie jest kompresowalny; kompresowanie tekstu jawnego przed szyfrowaniem może prowadzić do wycieków informacji (np. przestępstwa i naruszenia na TLS).
  2. upewnij się, że używasz mb_strlen() i mb_substr(), używając trybu zestawu znaków '8bit', aby zapobiec problemom mbstring.func_overload.
  3. IVs powinny być generowane za pomocą CSPRNG ; jeśli używasz mcrypt_create_iv(), nie stosować MCRYPT_RAND!
  4. chyba że używasz konstrukcji AEAD, zawsze Szyfruj potem MAC!
  5. bin2hex(), base64_encode(), itd. może wyciekać informacje o kluczach szyfrowania za pośrednictwem czas pamięci podręcznej. Unikaj ich, jeśli to możliwe.

Nawet jeśli zastosujesz się do podanych tu rad, wiele może pójść nie tak z kryptografią. Zawsze miej eksperta w dziedzinie kryptografii.Jeśli nie masz szczęścia, aby zaprzyjaźnić się ze studentem kryptografii na lokalnym Uniwersytecie, zawsze możesz skorzystać z forum wymiany stosów kryptograficznych (Cryptography Stack Exchange).

Jeśli potrzebujesz profesjonalnej analizy realizacji, zawsze możesz wynająć renomowany zespół konsultantów ds. bezpieczeństwa do przeglądu Twojego kodu kryptograficznego PHP (ujawnienie: mój pracodawca).

Ważne: kiedy nie używać szyfrowania

Nie Szyfruj haseł. Zamiast tego chcesz hashować, używając jednego z tych algorytmów hashowania haseł:

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

Nie Szyfruj parametrów URL . to złe narzędzie do pracy.

Przykład szyfrowania łańcuchów PHP z Libsodium

Jeśli korzystasz z PHP sodium_compat, aby osiągnąć ten sam wynik (choć wolniejszy).

<?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 przetestować go out:

<?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);

Halit-Libsodium Łatwiejsze

Jednym z projektów, nad którymi pracowałem, jest biblioteka szyfrująca 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łą podstawową kryptografią zajmuje się 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 kodowane szesnastkowo wyjście.

Zarządzanie Kluczami Szyfrowania

Jeśli skusisz się na Użyj "hasła", przestań natychmiast. Potrzebujesz losowego 128-bitowego klucza szyfrującego, a nie ludzkiego pamiętnego hasła.

Możesz przechowywać klucz szyfrujący do długotrwałego użytkowania w następujący sposób:]}
$storeMe = bin2hex($key);

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

$key = hex2bin($storeMe);

I zdecydowanie zalecam po prostu przechowywanie losowo wygenerowanego klucza do długotrwałego użycia zamiast jakiegokolwiek hasła jako klucza (lub do uzyskania klucza).

Jeśli używasz Defuse ' s biblioteka:

"ale ja naprawdę chcę użyć hasła."

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

Najpierw Wygeneruj losowy klucz i zachowaj 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");

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

Następnie użyj PBKDF2 (w ten sposób), aby uzyskać odpowiedni klucz szyfrowania z hasła zamiast szyfrowania hasłem 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 szyfrowania zostanie komicznie złamany.

 334
Author: Scott Arciszewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 12:11:36

Czego nie robić

WARNING:
Ta odpowiedź używa EBC . EBC nie jest trybem szyfrowania, to tylko budulec. Użycie ECB, jak pokazano w tej odpowiedzi, nie zaszyfruje łańcucha znaków w sposób bezpieczny. Nie używaj EBC w swoim kodzie. Zobacz odpowiedź Scotta dla dobrego rozwiązania.

Mam to na sobie. Właściwie znalazłem jakąś odpowiedź w google i coś zmodyfikowałem. wynik jest całkowicie niepewny jednak.
<?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;
}
?>
 42
Author: 夏期劇場,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 22:12:32

Jestem spóźniony na imprezę, ale szukając właściwego sposobu, aby to zrobić, natknąłem się na tę stronę był to jeden z najlepszych wyników wyszukiwania Google, więc chciałbym podzielić się moim zdaniem na temat problemu, który uważam za aktualny w momencie pisania tego postu(początek 2017). Od PHP 7.1.0 mcrypt_decrypt i mcrypt_encrypt będą przestarzałe, więc budowanie przyszłościowego kodu powinno używać 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 potrzebujesz prostego rozwiązania bez konieczności brania udziału w awaryjnym kursie inżynierii kryptograficznej, nie pisz go sam, po prostu użyj biblioteki.

Możesz również użyć innych metod rozdrabniania, w zależności od potrzeb bezpieczeństwa. Aby zapoznać się z dostępnymi metodami chipper, zajrzyj do funkcji openssl_get_cipher_methods .

 28
Author: Emil Borconi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-11 20:42:57

For Laravel framework

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

$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 znaków 16, 24 lub 32 w kluczowa Opcja konfiguracji / aplikacji.plik php. W przeciwnym razie zaszyfrowane wartości nie będzie bezpieczny.

 11
Author: Somnath Muluk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 12:58:54

Notatka historyczna: to zostało napisane w czasie PHP4. To się teraz nazywa "legacy code".

Zostawiłam tę odpowiedź dla 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 z metodami szyfrowania w PHP i 2) ten kod nadal służy celowi, do którego był przeznaczony: zademonstrować minimum, uproszczona koncepcja działania szyfrowania w PHP.

Jeśli znajdziesz podobnie uproszczone," szyfrowanie PHP dla manekinów " źródło, które może uruchomić ludzi w 10-20 linijek kodu lub mniej, daj mi znać w komentarzach.

Poza tym, ciesz się klasycznym odcinkiem wczesnej ery minimalistycznego szyfrowania odpowiedzi PHP4.


Najlepiej mieć-lub można uzyskać-dostęp do biblioteki PHP mcrypt, jako jej z pewnością popularne i bardzo przydatne różnych zadań. Oto bieg poniżej różne rodzaje 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 odwracalnego lub "symetrycznego" szyfrowania, gdy wystarczy jednokierunkowy hash.

2) Jeśli dane są naprawdę wrażliwe, takie jak numery kart kredytowych lub ubezpieczeń społecznych, przestań; Potrzebujesz więcej niż jakikolwiek prosty fragment kodu, ale raczej potrzebujesz zaprojektowanej do tego celu biblioteki kryptograficznej i znacznej ilości czasu na badania metody niezbędne. Co więcej, kryptografia oprogramowania jest prawdopodobnie

3) każdy rodzaj łatwego do wdrożenia szyfrowania, jak wymieniono tutaj, może w rozsądny sposób chronić delikatnie ważne informacje, które chcesz zachować przed wścibstwem oczu lub ograniczyć narażenie w przypadku przypadkowego / zamierzonego wycieku. Ale ponieważ klucz jest przechowywany w postaci zwykłego tekstu na serwerze WWW, jeśli mogą uzyskać dane, mogą uzyskać klucz deszyfrujący.

Niech tak będzie, baw się dobrze:)

 6
Author: BrianH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 22:12:01

Jeśli nie chcesz używać biblioteki (którą 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);
 1
Author: Ascon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 15:27:21