คุณเข้ารหัสและถอดรหัสสตริง PHP ได้อย่างไร


225

ที่ฉันหมายถึงคือ:

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

อาจจะชอบ:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • ใน PHP คุณจะทำสิ่งนี้ได้อย่างไร

พยายามใช้Crypt_Blowfishแต่ไม่ได้ผลสำหรับฉัน


35
@Rogue เขาไม่ต้องการแฮชเขาต้องการการเข้ารหัสแบบสมมาตร (เช่น AES) เขาแค่ไม่รู้ว่ามันเรียกอะไร (และตอนนี้เขาทำอย่างนั้น :))
Patashu

มันต้องมีความปลอดภัยแค่ไหน?

3
@ 夏期劇場คุณไม่ได้เข้ารหัส 'เกลือ' สมมาตรคุณใช้กุญแจ คีย์ต้องถูกเก็บเป็นความลับ เกลือสามารถเป็นที่สาธารณะได้โดยไม่ทำอันตรายต่อความปลอดภัย (ตราบใดที่เกลือของทุกคนแตกต่างกัน) และเป็นคำที่ใช้ในการแฮ็ชรหัสผ่าน
Patashu

2
คุณจำเป็นต้องมีเกลือ (คีย์ส่วนตัว) คีย์สาธารณะขั้นตอนวิธีการเข้ารหัสเช่น AES-256: wpy.me/blog/15-encrypt-and-decrypt-data-in-php-using-aes-256
wappy

8
@CristianFlorea ผู้เขียนโพสต์บล็อกนั้นใช้คำศัพท์ที่ไม่ได้ทำให้รู้สึกถึงเรื่องเล็กน้อยในบริบทของการเข้ารหัสแบบสมมาตร ไม่มีรหัสสาธารณะกับ AES และไม่มีเกลือ มีคีย์เดียว; จะต้องเก็บเป็นความลับ ในบางโหมดของการทำงานมี IV ที่ไม่จำเป็นต้องเป็นความลับ แต่ IV ไม่ใช่เกลือ (ขึ้นอยู่กับโหมดนั้นอาจมีข้อกำหนดที่แตกต่างกันมาก) และไม่จำเป็นต้องเป็นความลับในขณะที่คีย์การเข้ารหัสจริงไม่สามารถเปิดเผยต่อสาธารณะได้ คีย์สาธารณะ / ส่วนตัวนำไปใช้กับการเข้ารหัสลับแบบอสมมาตร แต่ไม่มีอะไรเกี่ยวข้องกับ AES
cpast

คำตอบ:


410

ก่อนที่คุณจะทำอะไรต่อไปพยายามที่จะเข้าใจความแตกต่างระหว่างการเข้ารหัสและการตรวจสอบและทำไมคุณอาจต้องการการเข้ารหัสรับรองความถูกต้องมากกว่าแค่การเข้ารหัส

ในการใช้การเข้ารหัสที่ผ่านการรับรองความถูกต้องคุณต้องเข้ารหัสแล้วใช้ MAC ลำดับการเข้ารหัสและการรับรองความถูกต้องเป็นสิ่งที่สำคัญมาก! หนึ่งในคำตอบที่มีอยู่สำหรับคำถามนี้ทำผิดพลาดนี้ เช่นเดียวกับไลบรารี่เข้ารหัสจำนวนมากที่เขียนด้วย PHP

คุณควรหลีกเลี่ยงการใช้วิทยาการเข้ารหัสลับของคุณเองและควรใช้ห้องสมุดที่ปลอดภัยซึ่งเขียนและตรวจสอบโดยผู้เชี่ยวชาญด้านการเข้ารหัส

อัปเดต: PHP 7.2 มี libsodium แล้ว ! เพื่อความปลอดภัยที่ดีที่สุดให้อัพเดตระบบของคุณเพื่อใช้ PHP 7.2 หรือสูงกว่าและทำตามคำแนะนำ libsodium ในคำตอบนี้เท่านั้น

ใช้ libsodium หากคุณมีการเข้าถึง PECL (หรือsodium_compatหากคุณต้องการ libsodium โดยไม่มี PECL) มิฉะนั้น ...
ใช้การลบข้อความ / php-encryption ; อย่าม้วนการเข้ารหัสของคุณเอง!

ไลบรารีทั้งสองที่ลิงก์ด้านบนทำให้ง่ายและไม่ลำบากในการใช้การเข้ารหัสที่รับรองความถูกต้องในไลบรารีของคุณเอง

หากคุณยังคงต้องการเขียนและปรับใช้ไลบรารีการเข้ารหัสของคุณเองกับภูมิปัญญาดั้งเดิมของผู้เชี่ยวชาญการเข้ารหัสทุกคนบนอินเทอร์เน็ตนี่คือขั้นตอนที่คุณต้องทำ

การเข้ารหัสลับ:

  1. เข้ารหัสโดยใช้ AES ในโหมด CTR คุณอาจใช้ GCM (ซึ่งไม่จำเป็นต้องใช้ MAC แยกต่างหาก) นอกจากนี้ ChaCha20 และ Salsa20 (จัดทำโดยlibsodium ) เป็นกระแสข้อมูล ciphers และไม่ต้องการโหมดพิเศษ
  2. ยกเว้นว่าคุณเลือก GCM ข้างต้นคุณควรรับรองความถูกต้องของข้อความเข้ารหัสด้วย HMAC-SHA-256 (หรือสำหรับรหัสสตรีม, Poly1305 - libsodium API ส่วนใหญ่ทำสิ่งนี้ให้คุณ) MAC ควรครอบคลุม IV เช่นเดียวกับ ciphertext!

ถอดรหัส:

  1. เว้นแต่ Poly1305 หรือ GCM ถูกนำมาใช้คำนวณ MAC ของ ciphertext และเปรียบเทียบกับ MAC hash_equals()ที่ถูกส่งโดยใช้ หากล้มเหลวให้ยกเลิก
  2. ถอดรหัสข้อความ

ข้อควรพิจารณาในการออกแบบอื่น ๆ :

  1. อย่าบีบอัดอะไรเลย Ciphertext ไม่สามารถบีบอัดได้ การบีบอัดข้อความธรรมดาก่อนการเข้ารหัสสามารถนำไปสู่การรั่วไหลของข้อมูล (เช่น CRIME และ BREACH บน TLS)
  2. ตรวจสอบให้แน่ใจว่าคุณใช้mb_strlen()และmb_substr()ใช้'8bit'โหมดชุดอักขระเพื่อป้องกันmbstring.func_overloadปัญหา
  3. เกลือควรจะสร้างโดยใช้CSPRNG ; หากคุณกำลังใช้mcrypt_create_iv(), ไม่ได้ใช้MCRYPT_RAND !
  4. นอกจากว่าคุณใช้โครงสร้าง AEAD เข้ารหัสเสมอแล้ว MAC!
  5. 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 ตัวอักษรเท่านั้น คีย์การเข้ารหัสของคุณจะถูกทำลายอย่างขบขัน


3
ทำรหัสผ่านที่ไม่ได้เข้ารหัส , กัญชาพวกเขาด้วยและตรวจสอบพวกเขาด้วยpassword_hash() password_verify()
Scott Arciszewski

2
"อย่าบีบอัดอะไรเลย" คุณหมายถึงเช่น HTTP, spdy และโปรโตคอลอื่น ๆ ก่อน TLS คำแนะนำที่แน่นอนมาก?
Tiberiu-Ionuț สแตน

1
@ScottArciszewski ฉันชอบความคิดเห็นของคุณเกี่ยวกับคีย์ "// ทำอย่างนี้แล้วเก็บไว้อย่างใด:" .. อย่างใดฮ่า ๆ :)) ดีวิธีการเก็บ 'สำคัญ' นี้ (ซึ่งเป็นวัตถุ) เป็นสตริงธรรมดา hardcoded? ฉันต้องการกุญแจเองเป็นสตริง ฉันจะเอามันมาจากวัตถุนี้ได้ไหม? ขอบคุณ
แอนดรูว์

2
ขอบคุณฉันต้องแก้ไขหนึ่งบรรทัดเพื่อให้ตัวอย่างโซเดียมของคุณทำงาน:function getKeyFromPassword($password, $keysize = \Sodium\CRYPTO_SECRETBOX_KEYBYTES)
Alexey Ozerov

1
ว้าว! +1 สำหรับการกล่าวถึงไม่เข้ารหัสพารามิเตอร์ URL ฉันชอบบทความที่คุณให้: paragonie.com/blog/2015/09/…
Lynnell Emmanuel Neri

73

ฉันไปงานปาร์ตี้ช้า แต่การค้นหาวิธีที่ถูกต้องที่จะทำฉันเจอหน้านี้มันเป็นหนึ่งในผลการค้นหายอดนิยมของ Google ดังนั้นฉันจะแบ่งปันมุมมองของฉันเกี่ยวกับปัญหาซึ่งฉันคิดว่ามันเป็น ล่าสุดในเวลาที่เขียนโพสต์นี้ (ต้นปี 2560) จาก PHP 7.1.0 mcrypt_decryptและmcrypt_encryptจะเลิกใช้ดังนั้นการสร้างรหัสหลักฐานในอนาคตควรใช้openssl_encryptและopenssl_decrypt

คุณสามารถทำสิ่งที่ชอบ:

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

สำคัญ : โหมดนี้ใช้โหมด ECBซึ่งไม่ปลอดภัย หากคุณต้องการวิธีการง่ายๆโดยไม่ต้องลงเรียนหลักสูตรความผิดพลาดในงานวิศวกรรมการเข้ารหัสอย่าเขียนด้วยตัวเองเพียงแค่ใช้ไลบรารี

คุณสามารถใช้วิธีการย่อยอื่น ๆ ได้เช่นกันขึ้นอยู่กับความต้องการด้านความปลอดภัยของคุณ หากต้องการค้นหาวิธีการย่อยที่มีอยู่โปรดดูฟังก์ชั่นopenssl_get_cipher_methods


10
ขอบคุณฉันแปลกใจที่คำตอบที่ง่ายและชัดเจนนี้ไม่ได้ยกระดับขึ้นอีก ฉันไม่ควรอ่านการอภิปราย 10 หน้าในหัวข้อเช่นคำตอบที่ดีที่สุดเมื่อทั้งหมดที่ฉันต้องการคือการเข้ารหัส / ถอดรหัสสตริงธรรมดา
เรน

4
นี่ไม่ใช่คำตอบที่ปลอดภัย ไม่ควรใช้โหมด ECB ถ้าคุณต้องการ "คำตอบที่ง่ายและชัดเจน" เพียงแค่ใช้ห้องสมุด
Scott Arciszewski

2
@ScottArciszewski ใช่ฉันยอมรับว่าฉันพูดเร็วเกินไปในขณะที่มองหารหัสง่ายๆ ฉันได้เพิ่ม IV และใช้ CBC ในรหัสของฉันซึ่งดีพอสำหรับการใช้งานของฉัน
เรน

อ่านและพิจารณาใหม่อีกครั้ง ด้วยโหมด CBC ผู้โจมตีสามารถเปลี่ยนข้อความได้อย่างสมบูรณ์ หากข้อความนั้นเป็นไฟล์ผู้โจมตีสามารถพลิกบิตและป๊อป calc.exe ความเสี่ยงของการเข้ารหัสที่ไม่ได้รับการพิสูจน์ตัวตนนั้นรุนแรง
Scott Arciszewski

7
ทุกอย่างขึ้นอยู่กับกรณีการใช้งาน! มีบางกรณีที่นี่เป็น FINE ที่สมบูรณ์แบบ ตัวอย่างเช่นฉันต้องการส่งพารามิเตอร์ GET จากหน้าหนึ่งไปอีกสมมติว่าprod_id=123ฉันไม่ต้องการให้ทุกคนอ่าน 123 ได้แม้ว่าจะสามารถอ่านได้ แต่ก็ไม่เป็นปัญหา ผู้โจมตีที่สามารถแทนที่ 123 เป็นค่าที่กำหนดเองจะไม่ก่อให้เกิดอันตรายใด ๆ เขา / เธอจะสามารถรับรายละเอียดของผลิตภัณฑ์อื่น ๆ ได้ แต่ผู้ใช้ Joe เฉลี่ยจะไม่ทราบวิธีการรับรายละเอียดสำหรับ เช่นผลิตภัณฑ์ 124 สำหรับสถานการณ์เช่นนี้มันเป็นโซลูชั่นที่สมบูรณ์แบบเพื่อความปลอดภัยไม่ต้องไป!
Emil Borconi

43

ไม่ควรทำอะไร

คำเตือน:
คำตอบนี้ใช้ECB ECB ไม่ใช่โหมดเข้ารหัส แต่เป็นแบบเอกสารสำเร็จรูป การใช้ ECB ตามที่แสดงในคำตอบนี้ไม่ได้เข้ารหัสสตริงอย่างปลอดภัย อย่าใช้ ECB ในรหัสของคุณ ดูคำตอบของ Scottเพื่อหาทางออกที่ดี

ฉันได้รับมันด้วยตัวเอง ที่จริงฉันพบคำตอบใน google และเพิ่งแก้ไขบางสิ่ง ผลที่ได้คือไม่ปลอดภัยอย่างสมบูรณ์อย่างไรก็ตาม

<?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
คุณสามารถใช้ "MCRYPT_MODE_CBC" แทน "MCRYPT_MODE_ECB" เพื่อความปลอดภัยมากขึ้น
Parixit

10
Ramesh นี้เป็นเพราะคุณได้รับข้อมูลที่เข้ารหัสลับแบบดิบ คุณสามารถรับข้อมูลที่เข้ารหัสรุ่นที่ดีกว่าได้โดยใช้ base64 เช่นนี้: base64_encode(encrypt($string))- เพื่อถอดรหัส:decrypt(base64_decode($encrypted))
mendezcode

82
คำเตือน: นี้ไม่ปลอดภัย ไม่ควรใช้โหมด ECB สำหรับสตริง, ECB ไม่ใช้ IV, นี่ไม่ใช่การรับรองความถูกต้อง, ใช้รหัสลับเก่า (Blowfish) แทน AES, คีย์ไม่ใช่ไบนารีเป็นต้นเป็นต้นชุมชน SO ควรหยุดการเข้ารหัส / ถอดรหัสที่เพียงแค่ "ทำงาน"และเริ่มต้นการถอนคำตอบที่ทราบว่าปลอดภัย หากคุณไม่รู้อย่างแน่นอนอย่าลงคะแนน
Maarten Bodewes

3
ชนิดของฉันทำอย่างนี้อยู่แล้วโดยมีโค้ดตัวอย่างของการmcrypt_encryptแทนที่: php.net/manual/en/function.mcrypt-encrypt.php โปรดทราบว่าการตรวจสอบตอนนี้อาจเป็นไปได้ว่าrtrimสำหรับตัวละคร"\0"ในตอนท้าย
Maarten Bodewes

4
คำตอบที่ถูกต้องคือการใช้สิ่งที่ชอบกลบเกลื่อน / การเข้ารหัส phpแทนการเขียนรหัส mcrypt ของคุณเอง
Scott Arciszewski

18

สำหรับกรอบ Laravel

หากคุณใช้เฟรมเวิร์ก Laravel คุณสามารถเข้ารหัสและถอดรหัสด้วยฟังก์ชันภายในได้ง่ายขึ้น

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

หมายเหตุ: ตรวจสอบให้แน่ใจว่าตั้งค่าสตริงแบบสุ่ม 16, 24 หรือ 32 ตัวในคีย์ตัวเลือกของไฟล์ config / app.php มิฉะนั้นค่าที่เข้ารหัสจะไม่ปลอดภัย


1
แน่นอนว่ามันอาจจะใช้งานง่าย แต่มันปลอดภัยหรือไม่ มันแก้ไขปัญหาในstackoverflow.com/a/30159120/781723 ได้อย่างไร มันใช้การเข้ารหัสที่รับรองความถูกต้องหรือไม่? มันหลีกเลี่ยงช่องโหว่ด้านข้างและตรวจสอบให้แน่ใจว่ามีการตรวจสอบความเท่าเทียมกันตลอดเวลาหรือไม่? มันใช้คีย์สุ่มอย่างแท้จริงแทนที่จะใช้รหัสผ่าน / วลีรหัสผ่านหรือไม่? มันใช้โหมดการทำงานที่เหมาะสมหรือไม่? มันสร้าง IV แบบสุ่มอย่างถูกต้องหรือไม่?
DW

10

Updated

PHP เวอร์ชัน 7 พร้อมแล้ว มันใช้openssl_encryptฟังก์ชั่นจาก 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
นี่เป็นเวอร์ชั่นที่ดีกว่าและปลอดภัยกว่ามาก ขอบคุณมันใช้งานได้ตามที่คาดไว้เช่นกัน

1
คุณจะสร้างและเก็บคีย์การเข้ารหัสได้อย่างไร
user2800464

7

บันทึกทางประวัติศาสตร์:สิ่งนี้ถูกเขียนขึ้นตอน PHP4 นี่คือสิ่งที่เราเรียกว่า "รหัสดั้งเดิม" ตอนนี้

ฉันได้ทิ้งคำตอบนี้ไว้เพื่อจุดประสงค์ทางประวัติศาสตร์ - แต่บางวิธีก็เลิกใช้แล้ววิธีการเข้ารหัส DES ไม่ใช่วิธีปฏิบัติที่แนะนำ ฯลฯ

ฉันไม่ได้อัปเดตรหัสนี้ด้วยเหตุผลสองประการ: 1) ฉันไม่ทำงานกับวิธีการเข้ารหัสด้วยมือใน PHP อีกต่อไปและ 2) รหัสนี้ยังคงทำหน้าที่ตามวัตถุประสงค์ที่ตั้งใจไว้: เพื่อแสดงให้เห็นถึงแนวคิดขั้นต่ำสุด ใน PHP

หากคุณพบแหล่งข้อมูลประเภท "PHP encryption for dummies" ที่คล้ายกันซึ่งสามารถทำให้ผู้คนเริ่มต้นด้วยรหัส 10-20 บรรทัดหรือน้อยกว่านั้นให้ฉันทราบในความคิดเห็น

นอกเหนือจากนั้นโปรดเพลิดเพลินไปกับ Classic Episode ของคำตอบการเข้ารหัสแบบมินิมัลลิสต์ PHP4 ในยุคต้น


เป็นการดีที่คุณมีหรือสามารถเข้าถึงไลบรารี mcrypt PHP เนื่องจากเป็นงานที่ได้รับความนิยมและมีประโยชน์อย่างมาก นี่คือการเข้ารหัสแบบต่าง ๆ และตัวอย่างรหัส: เทคนิคการเข้ารหัสใน 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 ); 
?> 

คำเตือนเล็กน้อย:

1) อย่าใช้การเข้ารหัสแบบย้อนกลับได้หรือ "การเข้ารหัส" สมมาตรเมื่อแฮชทางเดียวจะทำ

2) หากข้อมูลมีความละเอียดอ่อนอย่างแท้จริงเช่นบัตรเครดิตหรือหมายเลขประกันสังคมหยุด; คุณต้องการมากกว่าโค้ดที่เรียบง่าย แต่จะให้ไลบรารี crypto ที่ออกแบบมาเพื่อวัตถุประสงค์นี้และใช้เวลาในการค้นคว้าวิธีการที่จำเป็น นอกจากนี้ crypto ซอฟต์แวร์น่าจะเป็น <10% ของความปลอดภัยของข้อมูลที่สำคัญ มันเหมือนกับการเดินสายโรงไฟฟ้านิวเคลียร์ - ยอมรับว่างานนั้นอันตรายและยากและเกินความรู้ของคุณหากเป็นเช่นนั้น บทลงโทษทางการเงินอาจเป็นเรื่องใหญ่ดังนั้นควรใช้บริการและส่งมอบความรับผิดชอบให้กับพวกเขา

3) การเข้ารหัสที่สามารถนำไปใช้งานได้อย่างง่ายดายตามที่ระบุไว้ในที่นี้สามารถปกป้องข้อมูลสำคัญที่คุณไม่ควรมองข้ามหรือ จำกัด การสัมผัสในกรณีที่มีการรั่วไหลโดยไม่ตั้งใจ / ไม่เจตนา แต่การมองว่าการจัดเก็บคีย์นั้นเป็นข้อความธรรมดาบนเว็บเซิร์ฟเวอร์ได้อย่างไรหากพวกเขาสามารถรับข้อมูลพวกเขาสามารถรับคีย์ถอดรหัสได้

เป็นอย่างที่มันอาจจะมีความสนุกสนาน :)


9
อ๊ะเดอ AES อยู่ที่ไหน
Patashu

2
ขอบคุณ! แต่บางประเด็น ฉันได้รับM�������f=�_=ตัวละครแปลก ๆ เป็นตัวเข้ารหัส ฉันขอตัวละครธรรมดา ๆ ไม่ได้เหรอ? 2a2ffa8f13220befbe30819047e23b2cไลค์: นอกจากนี้ฉันไม่สามารถเปลี่ยน LENGTH ของ$key_value(กำหนดเป็น 8 ???) และ LENGTH ของเอาต์พุตได้$encrypted_textหรือไม่ (ไม่สามารถยาวได้ 32 หรือ 64 หรือนานกว่านี้อีกแล้ว?)
夏期劇場

3
@ 夏期劇場ผลลัพธ์ของการเข้ารหัสคือข้อมูลไบนารี่ หากคุณต้องการให้มนุษย์สามารถอ่านได้ให้ใช้การเข้ารหัส base64 หรือฐานสิบหกบนมัน 'ฉันไม่สามารถเปลี่ยนความยาวของค่าคีย์ได้หรือไม่' อัลกอริธึมการเข้ารหัสแบบสมมาตรที่แตกต่างกันมีข้อกำหนดที่แตกต่างกันสำหรับค่าคีย์ 'และความยาวของเอาต์พุต ... ' ความยาวของข้อความที่เข้ารหัสต้องมีอย่างน้อยเท่ากับข้อความต้นฉบับหรือมีข้อมูลไม่เพียงพอที่จะสร้างข้อความต้นฉบับใหม่ (นี่คือแอปพลิเคชันของหลักการ Pigeonhole) BTW คุณควรใช้ AES แทน DES DES แตกง่ายและไม่ปลอดภัยอีกต่อไป
Patashu

8
mcrypt_ecb ถูกเลิกใช้ตั้งแต่ PHP 5.5.0 การใช้งานฟังก์ชั่นนี้ไม่ได้รับผลกระทบมากนัก php.net/manual/en/function.mcrypt-ecb.php
Hafez Divandari

1
@BrianDHall เหตุผลที่ยังคงได้รับการลงคะแนนเป็นเพราะโหมด ECB ไม่ปลอดภัย (ใช้ CBC, CTR, GCM หรือ Poly1305), DES อ่อนแอ (คุณต้องการ AES ซึ่งก็คือMCRYPT_RIJNDAEL_128) และ ciphertexts ควรได้รับการรับรองความถูกต้อง ( hash_hmac()ตรวจสอบแล้ว ด้วยhash_equals())
Scott Arciszewski

7

หากคุณไม่ต้องการใช้ไลบรารี่ (ซึ่งคุณควร) ให้ใช้สิ่งนี้ (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);

สามารถแทนที่เป็นstackoverflow.com/a/16606352/126833หรือไม่ ฉันใช้อดีตจนกระทั่งโฮสต์ของฉันอัพเกรดเป็น PHP 7.2
anjanesh

@anjanesh คุณจะไม่สามารถถอดรหัสข้อมูลเก่ากับคนนี้ (ขั้นตอนวิธีการที่แตกต่างกัน + คนนี้ลงลายมือชื่อตรวจสอบ)
แอสคอน

2
ในกรณีของคุณอาจเป็นเช่นนี้ควรทำ: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

นี่คือสุดยอด!
anjanesh

2

นี่คือวิธีการที่กะทัดรัดในการเข้ารหัส / ถอดรหัสสตริงด้วย PHP โดยใช้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));
}

การใช้งาน:

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

0

โค้ดด้านล่างทำงานเป็น php สำหรับสตริงทั้งหมดที่มีอักขระพิเศษ

   // 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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.