security - أبسط التشفير ثنائي الاتجاه باستخدام PHP




encryption cryptography encryption-symmetric (5)

هنا تنفيذ بسيط ولكنه آمن بما فيه الكفاية:

  • تشفير AES-256 في وضع CBC
  • PBKDF2 لإنشاء مفتاح التشفير من كلمة مرور النص العادي
  • HMAC لمصادقة الرسالة المشفرة.

رمز وأمثلة هنا: https://stackoverflow.com/a/19445173/1387163

ما هي أبسط طريقة للقيام بتشفير ثنائي الاتجاه في عمليات تثبيت PHP الشائعة؟

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

لا يعتبر الأمان كبيرًا على قدر القلق مثل إمكانية نقل الرمز ، لذلك أريد أن أتمكن من إبقاء الأمور بسيطة قدر الإمكان. في الوقت الحالي ، أستخدم تطبيق RC4 ، ولكن إذا تمكنت من العثور على شيء مدعوم أصلاً ، فإنني أتمكن من توفير الكثير من الشفرات غير الضرورية.


مهم : ما لم يكن لديك حالة استخدام خاصة جدًا ، لا تقم بتشفير كلمات المرور ، استخدم خوارزمية تجزئة كلمة المرور بدلاً من ذلك. عندما يقول أحدهم أنه يقوم بتشفير كلمات المرور الخاصة به في تطبيق من جانب الخادم ، فإنهم إما غير مطّلعين أو يصفون تصميم نظام خطير. يعد تخزين كلمات المرور بأمان مشكلة منفصلة تمامًا عن التشفير.

لتعلم. تصميم أنظمة آمنة.

تشفير البيانات المحمولة في PHP

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

إذا كانت أهداف قابلية النقل لديك لا تمنع استخدام ملحقات PECL ، libsodium بشدة باستخدام libsodium على أي شيء يمكنك أو أن أكتبه في PHP.

تحديث (2016-06-12): يمكنك الآن استخدام sodium_compat واستخدام نفس عروض crypto libsodium بدون تثبيت ملحقات PECL.

إذا كنت ترغب في تجربة يدك في هندسة التشفير ، اقرأ.

أولاً ، يجب أن تأخذ الوقت الكافي لتعلم مخاطر التشفير غير المصادق ومبدأ Dry التشفير .

  • لا يزال يمكن التلاعب بالبيانات المشفرة بواسطة مستخدم ضار.
  • مصادقة البيانات المشفرة تمنع العبث.
  • مصادقة البيانات غير المشفرة لا يمنع التلاعب.

التشفير وفك التشفير

التشفير في PHP بسيط في الواقع (سنستخدم openssl_encrypt() و openssl_decrypt() بمجرد اتخاذ بعض القرارات حول كيفية تشفير معلوماتك. استشر openssl_get_cipher_methods() للحصول على قائمة بالطرق المدعومة على نظامك. الاختيار هو AES في وضع CTR :

  • aes-128-ctr
  • aes-192-ctr
  • aes-256-ctr

لا يوجد حاليا أي سبب للاعتقاد بأن حجم مفتاح AES هو قضية مهمة يجب أن تقلق بشأنها (من المحتمل ألا يكون الحجم أكبر ، بسبب الجدولة السيئة في وضع 256 بت).

ملاحظة: نحن لا نستخدم mcrypt لأنها mcrypt ولها أخطاء غير مثبتة قد تؤثر على الأمن. بسبب هذه الأسباب ، أشجع مطوّري PHP الآخرين على تجنبها أيضًا.

تشفير بسيط / فك التشفير باستخدام OpenSSL

class UnsafeCrypto
{
    const METHOD = 'aes-256-ctr';

    /**
     * Encrypts (but does not authenticate) a message
     * 
     * @param string $message - plaintext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encode - set to TRUE to return a base64-encoded 
     * @return string (raw binary)
     */
    public static function encrypt($message, $key, $encode = false)
    {
        $nonceSize = openssl_cipher_iv_length(self::METHOD);
        $nonce = openssl_random_pseudo_bytes($nonceSize);

        $ciphertext = openssl_encrypt(
            $message,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $nonce
        );

        // Now let's pack the IV and the ciphertext together
        // Naively, we can just concatenate
        if ($encode) {
            return base64_encode($nonce.$ciphertext);
        }
        return $nonce.$ciphertext;
    }

    /**
     * Decrypts (but does not verify) a message
     * 
     * @param string $message - ciphertext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encoded - are we expecting an encoded string?
     * @return string
     */
    public static function decrypt($message, $key, $encoded = false)
    {
        if ($encoded) {
            $message = base64_decode($message, true);
            if ($message === false) {
                throw new Exception('Encryption failure');
            }
        }

        $nonceSize = openssl_cipher_iv_length(self::METHOD);
        $nonce = mb_substr($message, 0, $nonceSize, '8bit');
        $ciphertext = mb_substr($message, $nonceSize, null, '8bit');

        $plaintext = openssl_decrypt(
            $ciphertext,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $nonce
        );

        return $plaintext;
    }
}

مثال على الاستخدام

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

عرض توضيحي : https://3v4l.org/jl7qR

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

ملاحظة : بشكل افتراضي ، UnsafeCrypto::encrypt() سلسلة ثنائية raw. يطلق عليه مثل هذا إذا كنت بحاجة إلى تخزينه في تنسيق ثنائي آمن (base64-encoded):

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key, true);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key, true);

var_dump($encrypted, $decrypted);

العرض التوضيحي : http://3v4l.org/f5K93

غلاف توثيق بسيط

class SaferCrypto extends UnsafeCrypto
{
    const HASH_ALGO = 'sha256';

    /**
     * Encrypts then MACs a message
     * 
     * @param string $message - plaintext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encode - set to TRUE to return a base64-encoded string
     * @return string (raw binary)
     */
    public static function encrypt($message, $key, $encode = false)
    {
        list($encKey, $authKey) = self::splitKeys($key);

        // Pass to UnsafeCrypto::encrypt
        $ciphertext = parent::encrypt($message, $encKey);

        // Calculate a MAC of the IV and ciphertext
        $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);

        if ($encode) {
            return base64_encode($mac.$ciphertext);
        }
        // Prepend MAC to the ciphertext and return to caller
        return $mac.$ciphertext;
    }

    /**
     * Decrypts a message (after verifying integrity)
     * 
     * @param string $message - ciphertext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encoded - are we expecting an encoded string?
     * @return string (raw binary)
     */
    public static function decrypt($message, $key, $encoded = false)
    {
        list($encKey, $authKey) = self::splitKeys($key);
        if ($encoded) {
            $message = base64_decode($message, true);
            if ($message === false) {
                throw new Exception('Encryption failure');
            }
        }

        // Hash Size -- in case HASH_ALGO is changed
        $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
        $mac = mb_substr($message, 0, $hs, '8bit');

        $ciphertext = mb_substr($message, $hs, null, '8bit');

        $calculated = hash_hmac(
            self::HASH_ALGO,
            $ciphertext,
            $authKey,
            true
        );

        if (!self::hashEquals($mac, $calculated)) {
            throw new Exception('Encryption failure');
        }

        // Pass to UnsafeCrypto::decrypt
        $plaintext = parent::decrypt($ciphertext, $encKey);

        return $plaintext;
    }

    /**
     * Splits a key into two separate keys; one for encryption
     * and the other for authenticaiton
     * 
     * @param string $masterKey (raw binary)
     * @return array (two raw binary strings)
     */
    protected static function splitKeys($masterKey)
    {
        // You really want to implement HKDF here instead!
        return [
            hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
            hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
        ];
    }

    /**
     * Compare two strings without leaking timing information
     * 
     * @param string $a
     * @param string $b
     * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
     * @return boolean
     */
    protected static function hashEquals($a, $b)
    {
        if (function_exists('hash_equals')) {
            return hash_equals($a, $b);
        }
        $nonce = openssl_random_pseudo_bytes(32);
        return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
    }
}

مثال على الاستخدام

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = SaferCrypto::encrypt($message, $key);
$decrypted = SaferCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

العروض التوضيحية : ثنائية الخام ، base64-encoded

إذا كان أي شخص يرغب في استخدام مكتبة SaferCrypto هذه في بيئة إنتاج ، أو SaferCrypto الخاص للمفاهيم نفسها ، فإنني أوصي بشدة SaferCrypto التشفير المقيمين لديك للحصول على رأي ثانٍ قبل القيام بذلك. سيكونون قادرين على إخبارك عن الأخطاء التي قد لا أكون على علم بها.

سوف تكون أفضل حالا باستخدام مكتبة التشفير ذات السمعة الطيبة .


طرق أصلية بسيطة للغاية ومبطّنة أحاديًا لتشفير / فك تشفير ، دون أي مفاتيح أو مكتبات خارجية:

هذه هي أبسط الطرق للقيام بتشفير ثنائي الاتجاه (وهمية / زائفة) في عمليات تثبيت PHP الشائعة.



To support mattmanser answer . Here's an example using MachineKey class to encrypt/decrypt URL safe values.

Something to bear in mind, as mentioned before, this will use Machine config settings ( https://msdn.microsoft.com/en-us/library/ff649308.aspx ). You can set encryption and decryption key/algorithm manually (you might need this specially if your site is running on multiple servers) in web.config file. You can generate keys from IIS (see here: https://blogs.msdn.microsoft.com/vijaysk/2009/05/13/iis-7-tip-10-you-can-generate-machine-keys-from-the-iis-manager/ ) or can use an online machine key generator like: http://www.developerfusion.com/tools/generatemachinekey/

    private static readonly UTF8Encoding Encoder = new UTF8Encoding();

    public static string Encrypt(string unencrypted)
    {
        if (string.IsNullOrEmpty(unencrypted)) 
            return string.Empty;

        try
        {
            var encryptedBytes = MachineKey.Protect(Encoder.GetBytes(unencrypted));

            if (encryptedBytes != null && encryptedBytes.Length > 0)
                return HttpServerUtility.UrlTokenEncode(encryptedBytes);    
        }
        catch (Exception)
        {
            return string.Empty;
        }

        return string.Empty;
    }

    public static string Decrypt(string encrypted)
    {
        if (string.IsNullOrEmpty(encrypted)) 
            return string.Empty;

        try
        {
            var bytes = HttpServerUtility.UrlTokenDecode(encrypted);
            if (bytes != null && bytes.Length > 0)
            {
                var decryptedBytes = MachineKey.Unprotect(bytes);
                if(decryptedBytes != null && decryptedBytes.Length > 0)
                    return Encoder.GetString(decryptedBytes);
            }

        }
        catch (Exception)
        {
            return string.Empty;
        }

        return string.Empty;
    }




php security encryption cryptography encryption-symmetric