security موقع - كيف تشفير وفك تشفير سلسلة PHP؟




لفك كلمات (5)

قبل أن تفعل أي شيء آخر ، حاول فهم الفرق بين التشفير والتوثيق ، ولماذا تريد تشفيرًا موثوقًا به بدلاً من التشفير فقط.

لتنفيذ التشفير المصادق ، تريد تشفير ثم MAC. ترتيب التشفير والتوثيق مهم جدا! واحدة من الإجابات الموجودة على هذا السؤال جعلت هذا الخطأ. كما تفعل العديد من مكتبات التشفير المكتوبة بلغة PHP.

يجب تجنب تنفيذ التشفير الخاص بك ، وبدلاً من ذلك استخدام مكتبة آمنة مكتوبة من قبل ومراجعتها من قبل خبراء التشفير.

تحديث: PHP 7.2 الآن يوفر libsodium ! تحديث إلى PHP 7.2 أو أعلى واتبع فقط نصيحة libsodium في هذه الإجابة.

استخدام libsodium إذا كان لديك الوصول PECL (أو sodium_compat إذا كنت تريد libsodium دون PECL) ؛ غير ذلك...
استخدم defuse / php-encryption ؛ لا تدحرج الترميز الخاص بك!

كل من المكتبات المرتبطة أعلاه أعلاه تجعل من السهل وغير مؤلم تنفيذ التشفير المصادق في المكتبات الخاصة بك.

إذا كنت لا تزال ترغب في كتابة ونشر مكتبة التشفير الخاصة بك ، مقابل الحكمة التقليدية لكل خبير تشفير على الإنترنت ، فهذه هي الخطوات التي يجب عليك اتخاذها.

التشفير:

  1. تشفير باستخدام AES في وضع CTR. يمكنك أيضًا استخدام GCM (الذي يزيل الحاجة إلى MAC منفصل). بالإضافة إلى ذلك ، ChaCha20 و Salsa20 (التي توفرها libsodium ) هي ciphers الدفق ولا تحتاج وسائط خاصة.
  2. إذا لم تكن قد اخترت GCM أعلاه ، فيجب عليك مصادقة النص المشفر باستخدام HMAC-SHA-256 (أو ، بالنسبة إلى ciphers الدفق ، Poly1305 - معظم واجهات برمجة التطبيقات libsodium تفعل ذلك من أجلك). يجب على MAC تغطية الـ IV بالإضافة إلى النص المشفر!

فك التشفير:

  1. ما لم يتم استخدام Poly1305 أو GCM ، أعد حساب MAC hash_equals() MAC الذي تم إرساله باستخدام hash_equals() . إذا فشل ، إجهاض.
  2. فك تشفير الرسالة.

اعتبارات التصميم الأخرى:

  1. لا تضغط أي شيء من أي وقت مضى. النص المشفر غير قابل للانضغاط. يمكن أن يؤدي ضغط النص العادي قبل التشفير إلى حدوث تسرب للمعلومات (مثل CRIME و BREACH على TLS).
  2. تأكد من استخدام mb_strlen() و mb_substr() ، باستخدام وضع مجموعة الأحرف '8bit' لمنع مشاكل mbstring.func_overload .
  3. يجب أن يكون توليد CSPRNG باستخدام CSPRNG ؛ إذا كنت تستخدم mcrypt_create_iv() ، MCRYPT_RAND تستخدم MCRYPT_RAND !
  4. ما لم تكن تستخدم تصميم AEAD ، قم بتشفير ALWAY ثم MAC!
  5. bin2hex() و base64_encode() وما إلى ذلك في تسرب معلومات حول مفاتيح التشفير عبر توقيت ذاكرة التخزين المؤقت. تجنبها إن أمكن.

حتى إذا اتبعت النصيحة الموضحة هنا ، يمكن أن يحدث خطأ كبير في التشفير. احرص دومًا على مراجعة خبير التشفير الخاص بالتطبيق. إذا لم تكن محظوظًا بما يكفي لتكون صديقًا شخصيًا مع طالب تشفير في جامعتك المحلية ، فيمكنك دائمًا تجربة منتدى Cryptography Stack Exchange للحصول على المشورة.

إذا كنت بحاجة إلى تحليل مهني للتنفيذ الخاص بك ، فيمكنك دائمًا الاستعانة بفريق من خبراء الأمان المشهورين لمراجعة كود التشفير الخاص بـ PHP (الكشف: صاحب العمل الخاص بي).

هام: متى لا تستخدم التشفير

لا تقم بتشفير كلمات المرور . تريد تجزئتها بدلاً من ذلك ، باستخدام واحدة من خوارزميات تجزئة كلمات المرور هذه:

لا تستخدم أبداً دالة هاش ذات غرض عام (MD5، SHA256) لتخزين كلمة المرور.

لا تشفِّر معلمات عناوين URL . إنها الأداة الخاطئة لهذا المنصب.

مثال لتشفير سلسلة PHP مع Libsodium

إذا كنت تستخدم PHP <7.2 أو لا يوجد لديك الليبسيمودي مثبتة ، فيمكنك استخدام 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 he 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 Made Easier

أحد المشاريع التي كنت أعمل عليها هي مكتبة تشفير تسمى Halite ، والتي تهدف إلى جعل الليبسيوديه أسهل وأكثر بديهية.

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

يتم التعامل مع كل التشفير الكامن بواسطة الليبسيوم.

مثال مع 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);

ملاحظة : إرجاع Crypto::encrypt() إخراج ترميز عشري.

إدارة مفتاح التشفير

إذا كنت تميل إلى استخدام "كلمة المرور" ، فتوقف الآن. تحتاج إلى مفتاح تشفير عشوائي 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 حرفًا. مفتاح التشفير الخاص بك سيتم كسر كومكس.

ما أعنيه هو:

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 ، لكنها لم تنجح بالنسبة لي.


لإطار 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. خلاف ذلك ، لن تكون القيم المشفرة آمنة.


ما الذي عليك عدم فعله

تحذير:
يستخدم هذا الجواب ECB . إن البنك المركزي الأوروبي ليس طريقة تشفير ، بل هو مجرد لبنة. استخدام ECB كما هو موضح في هذه الإجابة لا يقوم بالفعل بتشفير السلسلة بشكل آمن. لا تستخدم البنك المركزي الأوروبي في التعليمات البرمجية. انظر share عن حل جيد.

حصلت على نفسي. في الواقع ، وجدت بعض الإجابات على google وقمت بتعديل شيء ما. والنتيجة غير آمنة تماما.

<?php
define("ENCRYPTION_KEY", "[email protected]#$%^&*");
$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;
}
?>

ملاحظة تاريخية: تمت كتابة هذا في وقت PHP4. هذا ما نطلق عليه "الرمز القديم" الآن.

لقد تركت هذه الإجابة للأغراض التاريخية - ولكن بعض الأساليب تم إيقافها الآن ، وطريقة تشفير DES ليست ممارسة موصى بها ، إلخ.

لم أقم بتحديث هذا الرمز لسببين: 1) لم أعد أعمل مع أساليب التشفير يدويًا في PHP ، و 2) لا يزال هذا الرمز يخدم الغرض المقصود منه: إظهار الحد الأدنى ، المفهوم البسيط لكيفية عمل التشفير في PHP.

إذا وجدت نوعًا مشابهًا من التبسيط ، مثل "PHP encryption for dummies" ، يمكن أن يبدأ الأشخاص في 10-20 سطر من الكود أو أقل ، أخبرني في التعليقات.

أبعد من ذلك ، يرجى التمتع بهذه الحلقة الكلاسيكية من الإجابة على التشفير المبسط في PHP4 المبكرة.

من الناحية المثالية لديك - أو يمكنك الحصول على - الوصول إلى مكتبة PHP mcrypt ، باعتبارها بالتأكيد مجموعة متنوعة من المهام بالتأكيد مفيدة جدا ومفيدة للغاية. فيما يلي عملية تشغيل أنواع مختلفة من التشفير وبعض كود المثال: تقنيات التشفير في 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) إذا كانت البيانات حساسة حقاً ، مثل بطاقة الائتمان أو أرقام الضمان الاجتماعي ، فتوقف ؛ تحتاج إلى أكثر من أي جزء بسيط من التعليمات البرمجية ، ولكن تحتاج إلى مكتبة التشفير المصممة لهذا الغرض ، وكمية كبيرة من الوقت للبحث في الأساليب اللازمة. علاوة على ذلك ، فإن تشفير البرنامج ربما يكون <10٪ من أمان البيانات الحساسة. إنها مثل إعادة تركيب محطة طاقة نووية - تقبل أن المهمة خطيرة وصعبة وتتخطى معرفتك إذا كان هذا هو الحال. يمكن أن تكون العقوبات المالية هائلة ، لذا من الأفضل استخدام الخدمة ومسؤولية السفينة تجاههم.

3) أي نوع من التشفير القابل للتنفيذ بسهولة ، كما هو موضح هنا ، يمكن أن يحمي بشكل معقول المعلومات الهامة بشكل معتدل التي تريد الاحتفاظ بها من أعين المتطفلين أو الحد من التعرض في حالة تسرب غير مقصود / مقصود. ولكن بالنظر إلى كيفية تخزين المفتاح في نص عادي على خادم الويب ، إذا تمكنوا من الحصول على البيانات ، يمكنهم الحصول على مفتاح فك التشفير.

كن على هذا النحو ، استمتع :)


هناك خيار آخر يمكنك تجربته هو QueryPath . مستوحى من jQuery ، ولكن على الخادم في PHP ويستخدم في Drupal .





php security encryption cryptography encryption-symmetric