ก่อนที่คุณจะทำอะไรต่อไปพยายามที่จะเข้าใจความแตกต่างระหว่างการเข้ารหัสและการตรวจสอบและทำไมคุณอาจต้องการการเข้ารหัสรับรองความถูกต้องมากกว่าแค่การเข้ารหัส
ในการใช้การเข้ารหัสที่ผ่านการรับรองความถูกต้องคุณต้องเข้ารหัสแล้วใช้ MAC ลำดับการเข้ารหัสและการรับรองความถูกต้องเป็นสิ่งที่สำคัญมาก! หนึ่งในคำตอบที่มีอยู่สำหรับคำถามนี้ทำผิดพลาดนี้ เช่นเดียวกับไลบรารี่เข้ารหัสจำนวนมากที่เขียนด้วย PHP
คุณควรหลีกเลี่ยงการใช้วิทยาการเข้ารหัสลับของคุณเองและควรใช้ห้องสมุดที่ปลอดภัยซึ่งเขียนและตรวจสอบโดยผู้เชี่ยวชาญด้านการเข้ารหัส
อัปเดต: PHP 7.2 มี libsodium แล้ว ! เพื่อความปลอดภัยที่ดีที่สุดให้อัพเดตระบบของคุณเพื่อใช้ PHP 7.2 หรือสูงกว่าและทำตามคำแนะนำ libsodium ในคำตอบนี้เท่านั้น
ใช้ libsodium หากคุณมีการเข้าถึง PECL (หรือsodium_compatหากคุณต้องการ libsodium โดยไม่มี PECL) มิฉะนั้น ...
ใช้การลบข้อความ / php-encryption ; อย่าม้วนการเข้ารหัสของคุณเอง!
ไลบรารีทั้งสองที่ลิงก์ด้านบนทำให้ง่ายและไม่ลำบากในการใช้การเข้ารหัสที่รับรองความถูกต้องในไลบรารีของคุณเอง
หากคุณยังคงต้องการเขียนและปรับใช้ไลบรารีการเข้ารหัสของคุณเองกับภูมิปัญญาดั้งเดิมของผู้เชี่ยวชาญการเข้ารหัสทุกคนบนอินเทอร์เน็ตนี่คือขั้นตอนที่คุณต้องทำ
การเข้ารหัสลับ:
- เข้ารหัสโดยใช้ AES ในโหมด CTR คุณอาจใช้ GCM (ซึ่งไม่จำเป็นต้องใช้ MAC แยกต่างหาก) นอกจากนี้ ChaCha20 และ Salsa20 (จัดทำโดยlibsodium ) เป็นกระแสข้อมูล ciphers และไม่ต้องการโหมดพิเศษ
- ยกเว้นว่าคุณเลือก GCM ข้างต้นคุณควรรับรองความถูกต้องของข้อความเข้ารหัสด้วย HMAC-SHA-256 (หรือสำหรับรหัสสตรีม, Poly1305 - libsodium API ส่วนใหญ่ทำสิ่งนี้ให้คุณ) MAC ควรครอบคลุม IV เช่นเดียวกับ ciphertext!
ถอดรหัส:
- เว้นแต่ Poly1305 หรือ GCM ถูกนำมาใช้คำนวณ MAC ของ ciphertext และเปรียบเทียบกับ MAC
hash_equals()
ที่ถูกส่งโดยใช้ หากล้มเหลวให้ยกเลิก
- ถอดรหัสข้อความ
ข้อควรพิจารณาในการออกแบบอื่น ๆ :
- อย่าบีบอัดอะไรเลย Ciphertext ไม่สามารถบีบอัดได้ การบีบอัดข้อความธรรมดาก่อนการเข้ารหัสสามารถนำไปสู่การรั่วไหลของข้อมูล (เช่น CRIME และ BREACH บน TLS)
- ตรวจสอบให้แน่ใจว่าคุณใช้
mb_strlen()
และmb_substr()
ใช้'8bit'
โหมดชุดอักขระเพื่อป้องกันmbstring.func_overload
ปัญหา
- เกลือควรจะสร้างโดยใช้CSPRNG ; หากคุณกำลังใช้
mcrypt_create_iv()
, ไม่ได้ใช้MCRYPT_RAND
!
- นอกจากว่าคุณใช้โครงสร้าง AEAD เข้ารหัสเสมอแล้ว MAC!
bin2hex()
, base64_encode()
ฯลฯ อาจรั่วไหลข้อมูลเกี่ยวกับคีย์เข้ารหัสของคุณผ่านการกำหนดเวลาแคช หลีกเลี่ยงพวกเขาถ้าเป็นไปได้
แม้ว่าคุณจะทำตามคำแนะนำที่ได้รับจากที่นี่ แต่ก็มีหลายอย่างที่ผิดปกติกับการเข้ารหัส ให้ผู้เชี่ยวชาญด้านการเข้ารหัสตรวจสอบการใช้งานของคุณเสมอ หากคุณโชคดีไม่พอที่จะเป็นเพื่อนส่วนตัวกับนักศึกษาวิทยาการเข้ารหัสลับในมหาวิทยาลัยของคุณคุณสามารถลองใช้ฟอรัมCryptography Stack Exchangeเพื่อขอคำแนะนำได้เสมอ
หากคุณต้องการการวิเคราะห์การใช้งานอย่างมืออาชีพคุณสามารถจ้างทีมที่ปรึกษาด้านความปลอดภัยที่มีชื่อเสียงเพื่อตรวจสอบรหัสการเข้ารหัส PHP ของคุณ (การเปิดเผย: นายจ้างของฉัน)
สำคัญ: เมื่อใดที่จะไม่ใช้การเข้ารหัส
ไม่ได้เข้ารหัสรหัสผ่าน คุณต้องการแฮชพวกเขาแทนโดยใช้หนึ่งในอัลกอริทึมการแฮชรหัสผ่านเหล่านี้:
อย่าใช้ฟังก์ชันแฮชวัตถุประสงค์ทั่วไป (MD5, SHA256) สำหรับการจัดเก็บรหัสผ่าน
อย่าเข้ารหัส URL พารามิเตอร์ มันเป็นเครื่องมือที่ผิดสำหรับงาน
ตัวอย่างการเข้ารหัสสตริงของ PHP ด้วย Libsodium
หากคุณใช้ PHP <7.2 หรือไม่ได้ติดตั้ง libsodium คุณสามารถใช้sodium_compatเพื่อให้ได้ผลลัพธ์เดียวกัน (แม้ว่าจะช้ากว่า)
<?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;
}
จากนั้นเพื่อทดสอบ:
<?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 ทำได้ง่ายขึ้น
หนึ่งในโครงการที่ฉันได้ทำคือห้องสมุดการเข้ารหัสที่ชื่อว่าHaliteซึ่งมีจุดมุ่งหมายเพื่อให้ libsodium ง่ายขึ้นและใช้งานง่ายขึ้น
<?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);
การเข้ารหัสพื้นฐานทั้งหมดจะถูกจัดการโดย libsodium
ตัวอย่างที่มีการคลี่คลาย / php- การเข้ารหัส
<?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);
หมายเหตุ : Crypto::encrypt()
ส่งคืนเอาต์พุตที่เข้ารหัสด้วย hex
การจัดการคีย์การเข้ารหัส
หากคุณถูกล่อลวงให้ใช้ "รหัสผ่าน" หยุดทันที คุณต้องใช้คีย์เข้ารหัส 128 บิตแบบสุ่มไม่ใช่รหัสผ่านที่น่าจดจำของมนุษย์
คุณสามารถจัดเก็บคีย์การเข้ารหัสสำหรับการใช้งานระยะยาวเช่น:
$storeMe = bin2hex($key);
และตามความต้องการคุณสามารถเรียกคืนได้เช่น:
$key = hex2bin($storeMe);
ฉันขอแนะนำอย่างยิ่งให้เก็บคีย์ที่สร้างแบบสุ่มสำหรับการใช้งานระยะยาวแทนรหัสผ่านประเภทใด ๆ เป็นคีย์ (หรือเพื่อรับคีย์)
หากคุณกำลังใช้ห้องสมุดของ Defuse:
"แต่ฉันจริงๆต้องการที่จะใช้รหัสผ่าน."
นั่นเป็นความคิดที่ไม่ดี แต่โอเคนี่คือวิธีการทำอย่างปลอดภัย
ก่อนอื่นให้สร้างคีย์สุ่มและเก็บไว้ในค่าคงที่
/**
* 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");
โปรดทราบว่าคุณกำลังเพิ่มงานพิเศษและสามารถใช้ค่าคงที่นี้เป็นกุญแจสำคัญและช่วยให้คุณปวดใจได้อย่างมาก!
จากนั้นใช้ PBKDF2 (เช่นนั้น) เพื่อรับคีย์การเข้ารหัสที่เหมาะสมจากรหัสผ่านของคุณแทนที่จะเข้ารหัสด้วยรหัสผ่านของคุณโดยตรง
/**
* 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
);
}
อย่าใช้รหัสผ่าน 16 ตัวอักษรเท่านั้น คีย์การเข้ารหัสของคุณจะถูกทำลายอย่างขบขัน