Bagaimana cara mengenkripsi / mendekripsi data di php?

110

Saat ini saya adalah seorang pelajar dan saya sedang belajar PHP, saya mencoba membuat enkripsi / dekripsi data sederhana dalam PHP. Saya melakukan beberapa riset online dan beberapa di antaranya cukup membingungkan (setidaknya bagi saya).

Inilah yang saya coba lakukan:

Saya memiliki tabel yang terdiri dari bidang-bidang ini (UserID, Fname, Lname, Email, Password)

Yang ingin saya miliki adalah semua bidang dienkripsi dan kemudian didekripsi (Apakah mungkin digunakan sha256untuk enkripsi / dekripsi, jika bukan algoritma enkripsi apa pun)

Hal lain yang ingin saya pelajari adalah bagaimana membuat satu cara yang hash(sha256)dikombinasikan dengan "garam" yang baik. (Pada dasarnya saya hanya ingin implementasi enkripsi / dekripsi sederhana, hash(sha256)+salt) Pak / Bu, jawaban Anda akan sangat membantu dan sangat kami hargai. Terima kasih ++

Randel Ramirez
sumber
3
Kengerian enkripsi Sha-1!
Naftali alias Neal
9
SHA adalah hash, bukan enkripsi. Poin utamanya adalah hash tidak dapat dibalik ke data asli (tidak mudah). Anda mungkin ingin mcrypt atau jika tidak tersedia saya akan merekomendasikan phpseclib - meskipun penting untuk dicatat bahwa implementasi PHP murni apa pun yang melibatkan banyak matematika tingkat rendah akan menjadi sloooooowww ... Itulah mengapa saya suka phpseclib, karena ia menggunakan mcrypt terlebih dahulu jika tersedia dan hanya kembali ke implementasi PHP sebagai upaya terakhir.
DaveRandom
7
Anda biasanya tidak ingin bisa mendekripsi kata sandi!
Ja͢ck
1
Pada dasarnya Anda tidak boleh memikirkan enkripsi pada level ini, Anda harus memikirkan tentang kontrol akses, kerahasiaan, integritas, dan otentikasi. Setelah itu periksa bagaimana Anda dapat melakukannya, mungkin menggunakan enkripsi atau hashing aman. Anda mungkin ingin membaca PBKDF2 dan bcrypt / scrypt untuk memahami hashing kata sandi yang aman dan sejenisnya.
Maarten Bodewes

Jawaban:

289

Kata pengantar

Dimulai dengan definisi tabel Anda:

- UserID
- Fname
- Lname
- Email
- Password
- IV

Berikut perubahannya:

  1. Bidang Fname, Lnamedan Emailakan dienkripsi menggunakan cipher simetris, disediakan oleh OpenSSL ,
  2. The IVlapangan akan menyimpan vektor inisialisasi yang digunakan untuk enkripsi. Persyaratan penyimpanan tergantung pada sandi dan mode yang digunakan; lebih lanjut tentang ini nanti.
  3. The Passwordlapangan akan diacak dengan menggunakan satu arah hash password,

Enkripsi

Sandi dan mode

Memilih cipher dan mode enkripsi terbaik berada di luar cakupan jawaban ini, tetapi pilihan terakhir memengaruhi ukuran kunci enkripsi dan vektor inisialisasi; untuk posting ini kita akan menggunakan AES-256-CBC yang memiliki ukuran blok tetap 16 byte dan ukuran kunci 16, 24 atau 32 byte.

Kunci enkripsi

Kunci enkripsi yang baik adalah gumpalan biner yang dihasilkan dari generator nomor acak yang andal. Contoh berikut akan direkomendasikan (> = 5.3):

$key_size = 32; // 256 bits
$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
// $strong will be true if the key is crypto safe

Ini dapat dilakukan sekali atau beberapa kali (jika Anda ingin membuat rantai kunci enkripsi). Jaga kerahasiaannya sebisa mungkin.

IV

Vektor inisialisasi menambahkan keacakan pada enkripsi dan diperlukan untuk mode CBC. Nilai-nilai ini idealnya harus digunakan hanya sekali (secara teknis sekali per kunci enkripsi), jadi pembaruan ke bagian mana pun dari baris harus memperbaruinya.

Sebuah fungsi disediakan untuk membantu Anda menghasilkan IV:

$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);

Contoh

Mari mengenkripsi bidang nama, menggunakan yang sebelumnya $encryption_keydan $iv; untuk melakukan ini, kita harus memasukkan data kita ke ukuran blok:

function pkcs7_pad($data, $size)
{
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}

$name = 'Jack';
$enc_name = openssl_encrypt(
    pkcs7_pad($name, 16), // padded data
    'AES-256-CBC',        // cipher and mode
    $encryption_key,      // secret key
    0,                    // options (not used)
    $iv                   // initialisation vector
);

Persyaratan penyimpanan

Output terenkripsi, seperti IV, adalah biner; Menyimpan nilai-nilai ini dalam database bisa dilakukan dengan menggunakan tipe kolom yang ditentukan seperti BINARYatau VARBINARY.

Nilai keluaran, seperti IV, adalah biner; untuk menyimpan nilai-nilai tersebut di MySQL, pertimbangkan untuk menggunakan kolom BINARYatauVARBINARY . Jika ini bukan pilihan, Anda juga dapat mengonversi data biner menjadi representasi tekstual menggunakan base64_encode()atau bin2hex(), melakukannya membutuhkan ruang penyimpanan antara 33% hingga 100% lebih banyak.

Dekripsi

Dekripsi dari nilai yang disimpan serupa:

function pkcs7_unpad($data)
{
    return substr($data, 0, -ord($data[strlen($data) - 1]));
}

$row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
// $enc_name = base64_decode($row['Name']);
// $enc_name = hex2bin($row['Name']);
$enc_name = $row['Name'];
// $iv = base64_decode($row['IV']);
// $iv = hex2bin($row['IV']);
$iv = $row['IV'];

$name = pkcs7_unpad(openssl_decrypt(
    $enc_name,
    'AES-256-CBC',
    $encryption_key,
    0,
    $iv
));

Enkripsi terautentikasi

Anda selanjutnya dapat meningkatkan integritas teks sandi yang dihasilkan dengan menambahkan tanda tangan yang dihasilkan dari kunci rahasia (berbeda dari kunci enkripsi) dan teks sandi. Sebelum teks sandi didekripsi, tanda tangan diverifikasi terlebih dahulu (sebaiknya dengan metode perbandingan waktu konstan).

Contoh

// generate once, keep safe
$auth_key = openssl_random_pseudo_bytes(32, $strong);

// authentication
$auth = hash_hmac('sha256', $enc_name, $auth_key, true);
$auth_enc_name = $auth . $enc_name;

// verification
$auth = substr($auth_enc_name, 0, 32);
$enc_name = substr($auth_enc_name, 32);
$actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);

if (hash_equals($auth, $actual_auth)) {
    // perform decryption
}

Lihat juga: hash_equals()

Hashing

Menyimpan kata sandi yang dapat dibalik dalam database Anda harus sebisa mungkin dihindari; Anda hanya ingin memverifikasi kata sandi daripada mengetahui isinya. Jika pengguna kehilangan kata sandinya, lebih baik izinkan mereka menyetel ulang daripada mengirimkan kata sandi asli kepada mereka (pastikan penyetelan ulang kata sandi hanya dapat dilakukan untuk waktu yang terbatas).

Menerapkan fungsi hash adalah operasi satu arah; setelah itu dapat digunakan dengan aman untuk verifikasi tanpa mengungkap data asli; untuk kata sandi, metode brute force adalah pendekatan yang layak untuk mengungkapnya karena panjangnya yang relatif pendek dan pilihan kata sandi yang buruk dari banyak orang.

Algoritme hash seperti MD5 atau SHA1 dibuat untuk memverifikasi konten file terhadap nilai hash yang diketahui. Mereka sangat dioptimalkan untuk membuat verifikasi ini secepat mungkin dengan tetap akurat. Mengingat ruang keluaran yang relatif terbatas, mudah untuk membangun database dengan kata sandi yang diketahui dan keluaran hash masing-masing, tabel pelangi.

Menambahkan salt ke kata sandi sebelum melakukan hashing akan membuat tabel pelangi tidak berguna, tetapi kemajuan perangkat keras baru-baru ini membuat pencarian brute force menjadi pendekatan yang layak. Itulah mengapa Anda memerlukan algoritme hashing yang sengaja lambat dan tidak mungkin dioptimalkan. Ini juga harus dapat meningkatkan beban untuk perangkat keras yang lebih cepat tanpa mempengaruhi kemampuan untuk memverifikasi hash kata sandi yang ada untuk membuatnya menjadi bukti di masa mendatang.

Saat ini ada dua pilihan populer yang tersedia:

  1. PBKDF2 (Fungsi Penurunan Kunci Berbasis Kata Sandi v2)
  2. bcrypt (alias Blowfish)

Jawaban ini akan menggunakan contoh dengan bcrypt.

Generasi

Hash kata sandi dapat dibuat seperti ini:

$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('$2y$%02d$%s',
    13, // 2^n cost factor
    substr(strtr(base64_encode($random), '+', '.'), 0, 22)
);

$hash = crypt($password, $salt);

Salt dibuat openssl_random_pseudo_bytes()untuk membentuk gumpalan data acak yang kemudian dijalankan base64_encode()dan strtr()dicocokkan dengan alfabet yang diperlukan [A-Za-z0-9/.].

The crypt()melakukan fungsi hashing berdasarkan algoritma ( $2y$untuk Blowfish), faktor biaya (faktor dari 13 Dibutuhkan sekitar 0.40s pada mesin 3GHz) dan garam 22 karakter.

Validasi

Setelah Anda mengambil baris yang berisi informasi pengguna, Anda memvalidasi kata sandi dengan cara ini:

$given_password = $_POST['password']; // the submitted password
$db_hash = $row['Password']; // field with the password hash

$given_hash = crypt($given_password, $db_hash);

if (isEqual($given_hash, $db_hash)) {
    // user password verified
}

// constant time string compare
function isEqual($str1, $str2)
{
    $n1 = strlen($str1);
    if (strlen($str2) != $n1) {
        return false;
    }
    for ($i = 0, $diff = 0; $i != $n1; ++$i) {
        $diff |= ord($str1[$i]) ^ ord($str2[$i]);
    }
    return !$diff;
}

Untuk memverifikasi kata sandi, Anda memanggil crypt()lagi tetapi Anda meneruskan hash yang dihitung sebelumnya sebagai nilai garam. Nilai yang dikembalikan menghasilkan hash yang sama jika kata sandi yang diberikan cocok dengan hash. Untuk memverifikasi hash, sering kali disarankan untuk menggunakan fungsi perbandingan waktu konstan untuk menghindari serangan waktu.

Hash sandi dengan PHP 5.5

PHP 5.5 memperkenalkan fungsi hashing kata sandi yang dapat Anda gunakan untuk menyederhanakan metode hashing di atas:

$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);

Dan memverifikasi:

if (password_verify($given_password, $db_hash)) {
    // password valid
}

Lihat juga: password_hash(),password_verify()

Mendongkrak
sumber
Berapa panjang yang harus saya gunakan untuk menyimpan nama, nama keluarga, email, dll untuk taruhan teraman? varbinary (???)
BentCoder
2
Tentu, tapi itu tergantung bagaimana itu digunakan. Jika Anda menerbitkan pustaka enkripsi, Anda tidak tahu bagaimana pengembang akan menerapkannya. Itulah mengapa github.com/defuse/php-encryption menyediakan enkripsi kunci simetris yang diautentikasi dan tidak membiarkan pengembang melemahkannya tanpa mengedit kodenya.
Scott Arciszewski
2
@Scott Baiklah, saya telah menambahkan contoh enkripsi terotentikasi; terima kasih atas dorongannya :)
Ja͢ck
1
1 untuk enkripsi terotentikasi. Tidak ada cukup informasi dalam pertanyaan untuk mengatakan bahwa AE tidak diperlukan di sini. Tentu saja lalu lintas SQL sering melewati jaringan dengan properti keamanan yang tidak diketahui, seperti halnya lalu lintas dari database ke penyimpanan. Cadangan dan replikasi juga. Apa model ancamannya? Pertanyaannya tidak mengatakan, dan mungkin berbahaya untuk membuat asumsi.
Jason Orendorff
1
Alih-alih hard-coding $iv_size = 16;, saya akan menggunakan: $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC"))untuk menunjukkan hubungan antara ukuran iv untuk digunakan dengan sandi yang digunakan. Anda juga dapat memperluas sedikit tentang kebutuhan (atau tidak) dari pkcs7_pad()/ pkcs7_unpad(), atau cukup sederhanakan posting dengan menyingkirkannya dan menggunakan "aes-256-ctr". Pos hebat @ Ja͢ck
Patrick Allaert
24

Saya pikir ini telah dijawab sebelumnya ... tetapi bagaimanapun, jika Anda ingin mengenkripsi / mendekripsi data, Anda tidak dapat menggunakan SHA256

//Key
$key = 'SuperSecretKey';

//To Encrypt:
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, 'I want to encrypt this', MCRYPT_MODE_ECB);

//To Decrypt:
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_ECB);
romo
sumber
7
Anda juga tidak boleh menggunakan ECB, dalam hal ini.
Maarten Bodewes
7
Kunci harus byte acak, atau Anda harus menggunakan fungsi derivasi kunci aman.
Maarten Bodewes
4
MCRYPT_RIJNDAEL_256 bukan fungsi standar, Anda harus menggunakan AES (MCRYPT_RIJNDAEL_128)
Maarten Bodewes
14

Latar Belakang Jawaban dan Penjelasan

Untuk memahami pertanyaan ini, Anda harus terlebih dahulu memahami apa itu SHA256. SHA256 adalah Fungsi Hash Kriptografi . Fungsi Hash Cryptographic adalah fungsi satu arah, yang keluarannya aman secara kriptografis. Ini berarti mudah untuk menghitung hash (setara dengan mengenkripsi data), tetapi sulit mendapatkan input asli menggunakan hash (setara dengan mendekripsi data). Karena menggunakan fungsi hash Cryptographic berarti dekripsi tidak layak secara komputasi, jadi oleh karena itu Anda tidak dapat melakukan dekripsi dengan SHA256.

Yang ingin Anda gunakan adalah fungsi dua arah, tetapi lebih khusus lagi, Block Cipher . Sebuah fungsi yang memungkinkan enkripsi dan dekripsi data. Fungsi mcrypt_encryptdan mcrypt_decryptsecara default menggunakan algoritma Blowfish. Penggunaan mcrypt PHP dapat ditemukan di manual ini . Daftar definisi cipher untuk memilih penggunaan cipher mcrypt juga ada. Wiki tentang Blowfish dapat ditemukan di Wikipedia . Sebuah cipher blok mengenkripsi input dalam blok dengan ukuran dan posisi yang diketahui dengan kunci yang diketahui, sehingga data tersebut nantinya dapat didekripsi menggunakan kunci tersebut. Inilah yang tidak dapat disediakan SHA256 untuk Anda.

Kode

$key = 'ThisIsTheCipherKey';

$ciphertext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, 'This is plaintext.', MCRYPT_MODE_CFB);

$plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB);
sitinus
sumber
Anda juga tidak boleh menggunakan ECB, dalam hal ini.
Maarten Bodewes
Kunci harus byte acak, atau Anda harus menggunakan fungsi derivasi kunci aman.
Maarten Bodewes
4
Jangan pernah menggunakan mode ECB. Ini tidak aman dan sebagian besar waktu tidak benar-benar membantu dalam benar-benar mengenkripsi data (bukan hanya menyandikannya). Lihat artikel Wikipedia yang sangat bagus tentang subjek tersebut untuk informasi lebih lanjut.
Holger Hanya
1
Yang terbaik adalah tidak menggunakan mcrypt, itu adalah perangkat yang ditinggalkan, belum diperbarui selama bertahun-tahun dan tidak mendukung bantalan standar PKCS # 7 (née PKCS # 5), hanya bantalan nol non-standar yang bahkan tidak dapat digunakan dengan data biner . mcrypt memiliki banyak bug yang belum diketahui sejak tahun 2003 .. Daripada menggunakan defuse , bug tersebut dipertahankan dan sudah benar.
zaf
9

Berikut ini contoh penggunaan openssl_encrypt

//Encryption:
$textToEncrypt = "My Text to Encrypt";
$encryptionMethod = "AES-256-CBC";
$secretHash = "encryptionhash";
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$encryptedText = openssl_encrypt($textToEncrypt,$encryptionMethod,$secretHash, 0, $iv);

//Decryption:
$decryptedText = openssl_decrypt($encryptedText, $encryptionMethod, $secretHash, 0, $iv);
print "My Decrypted Text: ". $decryptedText;
Vivek
sumber
2
Alih-alih mcrypt_create_iv(), saya akan menggunakan openssl_random_pseudo_bytes(openssl_cipher_iv_length($encryptionMethod)):, dengan cara ini metodologi bekerja untuk nilai apa pun dari $ encryptionMethod dan hanya akan menggunakan ekstensi openssl.
Patrick Allaert
Kode di atas kembali falseuntuk openssl_decrypt(). Lihat stackoverflow.com/q/41952509/1066234 Karena cipher blok seperti AES memerlukan data input untuk menjadi kelipatan yang tepat dari ukuran blok (16-byte untuk AES) padding diperlukan.
Kai Noack
6
     function my_simple_crypt( $string, $action = 'e' ) {
        // you may change these values to your own
        $secret_key = 'my_simple_secret_key';
        $secret_iv = 'my_simple_secret_iv';

        $output = false;
        $encrypt_method = "AES-256-CBC";
        $key = hash( 'sha256', $secret_key );
        $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );

        if( $action == 'e' ) {
            $output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
        }
        else if( $action == 'd' ){
            $output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
        }

        return $output;
    }
gauravbhai daxini
sumber
sangat sederhana ! Saya menggunakannya untuk enkripsi-dekripsi segmen url. Terima kasih
Mahbub Tito
0

Butuh waktu cukup lama bagi saya untuk mencari tahu, bagaimana tidak mendapatkan falsesaat menggunakan openssl_decrypt()dan mendapatkan enkripsi dan dekripsi berfungsi.

    // cryptographic key of a binary string 16 bytes long (because AES-128 has a key size of 16 bytes)
    $encryption_key = '58adf8c78efef9570c447295008e2e6e'; // example
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted = openssl_encrypt($plaintext, 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, $iv);
    $encrypted = $encrypted . ':' . base64_encode($iv);

    // decrypt to get again $plaintext
    $parts = explode(':', $encrypted);
    $decrypted = openssl_decrypt($parts[0], 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, base64_decode($parts[1])); 

Jika Anda ingin meneruskan string yang dienkripsi melalui URL, Anda perlu melakukan urlencode string tersebut:

    $encrypted = urlencode($encrypted);

Untuk lebih memahami apa yang sedang terjadi, baca:

Untuk menghasilkan kunci panjang 16 byte, Anda dapat menggunakan:

    $bytes = openssl_random_pseudo_bytes(16);
    $hex = bin2hex($bytes);

Untuk melihat pesan error openssl Anda dapat menggunakan: echo openssl_error_string();

Semoga membantu.

Kai Noack
sumber