php - لجميع - كيفية معرفة كلمات المرور المكتوبة على متصفح الإنترنت




تشفير ثنائي الاتجاه: أحتاج إلى تخزين كلمات المرور التي يمكن استردادها (6)

أقوم بإنشاء تطبيق يقوم بتخزين كلمات المرور ، والتي يمكن للمستخدم استردادها ومشاهدتها. كلمات المرور هي لجهاز ، لذلك التحقق ضد التجزئة غير وارد.

ما أحتاج إلى معرفته هو:

  1. كيف أقوم بتشفير وفك تشفير كلمة المرور في PHP؟

  2. ما هي خوارزمية الأكثر أمانًا لتشفير كلمات المرور؟

  3. أين يمكنني تخزين المفتاح الخاص؟

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

  5. بأي طرق يمكن سرقة كلمة المرور وفك تشفيرها؟ ما الذي يجب أن أكون على علم به؟


كلمات المرور هي لجهاز ، لذلك التحقق ضد التجزئة غير وارد

إيه؟ أنا لا أفهم. هل تعني فقط أن كلمة المرور يجب أن تكون قابلة للاسترداد؟

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

لا تأتي معظم الثغرات الأمنية بسبب أن الخوارزمية الأساسية معيبة أو غير آمنة - ولكن بسبب مشاكل في طريقة استخدام الخوارزمية في شفرة التطبيق.

بعد قولي هذا ، من الممكن بناء نظام آمن إلى حد معقول.

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

ومع ذلك ، إذا قمت بتخزين المفتاح لفك تشفير الرسالة في نفس المكان مثل الرسالة المشفرة (أو حيث يتم تخزين الرسالة المشفرة) فإن النظام غير آمن. استخدم نفس الرمز المميز لمصادقة المستخدم بالنسبة لمفتاح فك التشفير (أو في حالة التشفير التحليلي ، استخدم الرمز المميز ككلمة مرور مفتاح خاص). نظرًا لأنك ستحتاج إلى تخزين الرمز المميز على الخادم حيث يحدث فك التشفير مؤقتًا على الأقل ، قد ترغب في استخدام طبقة تخزين غير قابلة للبحث في جلسة العمل ، أو تمرير الرمز المميز مباشرة إلى خادم مرتبط بالجلسة التي تخزن رمز في الذاكرة وتنفيذ فك التشفير من الرسائل على الطلب.


  1. وظيفة PHP التي تتابعها هي Mcrypt ( http://www.php.net/manual/en/intro.mcrypt.php ).

تم تعديل المثال من الدليل قليلاً لهذا المثال):

<?php
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$pass = "PasswordHere";
echo strlen($pass) . "\n";

$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $pass, MCRYPT_MODE_ECB, $iv);
echo strlen($crypttext) . "\n";
?>

يمكنك استخدام mcrypt_decrypt لفك تشفير كلمة المرور الخاصة بك.

  1. أفضل algorithm غير ذاتية - اسأل 5 أشخاص ، احصل على 5 إجابات. شخصيا إذا كان الافتراضي (السمكة المنتفخة) ليست جيدة بما فيه الكفاية بالنسبة لك ، ربما لديك مشاكل أكبر!

  2. وبالنظر إلى أن PHP تحتاج إلى تشفير - لست متأكدًا من أنه يمكنك إخفاؤه في أي مكان - نرحب بالتعليقات على ذلك. تطبق أفضل ممارسات الترميز PHP القياسية بالطبع!

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

  4. من الواضح ، إذا سرقت كلمة المرور المشفرة ومفتاح التشفير ، ثم أكثر من لعبة.

لقد وضعت متسابق على جوابي - أنا لست خبيراً في التشفير ، لكنني أعتقد أن ما أجبت به هو ممارسة معيارية - أرحب بالتعليقات التي قد تكون لديكم.


استخدم password_hash و password_verify

<?php
/**
 * In this case, we want to increase the default cost for BCRYPT to 12.
 * Note that we also switched to BCRYPT, which will always be 60 characters.
 */
$options = [
    'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>

وفك تشفير:

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>

اقترح الكثير من المستخدمين استخدام mcrypt ... وهو أمر صحيح ، ولكني أحب أن أخطو خطوة أخرى لجعل تخزينها ونقلها أمرًا سهلاً (حيث أن القيم المشفرة في بعض الأحيان قد تجعل من الصعب إرسالها باستخدام تقنيات أخرى مثل curl أو json) .

بعد تشفير بنجاح باستخدام mcrypt ، قم بتشغيله من خلال base64_encode ثم قم بتحويله إلى hex code. وبمجرد الوصول إلى الشفرة السداسية ، يكون من السهل نقلها بطرق متنوعة.

$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$encrypted = mcrypt_generic($td, $unencrypted);
$encrypted = $ua."||||".$iv;
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$encrypted = base64_encode($encrypted);
$encrypted = array_shift(unpack('H*', $encrypted));

وعلى الجانب الآخر:

$encrypted = pack('H*', $encrypted);
$encrypted = base64_decode($encrypted);
list($encrypted,$iv) = explode("||||",$encrypted,2);
$td = mcrypt_module_open('tripledes', '', 'ecb', '');
$key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$unencrypted = mdecrypt_generic($td, $encrypted);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

كيف أقوم بتشفير وفك تشفير كلمة المرور في PHP؟ من خلال تنفيذ واحد من العديد من خوارزميات التشفير. (أو باستخدام واحدة من العديد من المكتبات)

ما هي خوارزمية الأكثر أمانًا لتشفير كلمات المرور؟ هناك أطنان من الخوارزميات المختلفة ، لا شيء منها آمن بنسبة 100٪. لكن العديد منها آمن بما يكفي لأغراض التجارة وحتى للأغراض العسكرية

أين يمكنني تخزين المفتاح الخاص؟ إذا قررت تنفيذ مفتاح عام - خوارزمية تشفير (مثل RSA) ، فأنت لا تخزن المفتاح الخاص. المستخدم لديك مفتاح خاص. يحتوي نظامك على مفتاح عام يمكن تخزينه في أي مكان ترغب فيه.

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

بأي طرق يمكن سرقة كلمة المرور وفك تشفيرها؟ ما الذي يجب أن أكون على علم به؟ هذا يعتمد على الخوارزمية المستخدمة. ومع ذلك تأكد دائمًا من عدم إرسال كلمة المرور غير مشفرة إلى أو من المستخدم. إما تشفير / فك تشفيرها على جانب العميل ، أو استخدام https (أو مستخدم آخر يعني التشفير لتأمين الاتصال بين الخادم والعميل).

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


شخصيا ، أود أن استخدام mcrypt مثل الآخرين نشرها. ولكن هناك ما هو أكثر بكثير من أن نلاحظ ...

  1. كيف أقوم بتشفير وفك تشفير كلمة المرور في PHP؟

    انظر أدناه لفئة قوية تعتني بكل شيء من أجلك:

  2. ما هي خوارزمية الأكثر أمانًا لتشفير كلمات المرور؟

    أسلم ؟ أيا منهم. الطريقة الأسلم إذا كنت ستقوم بالتشفير هي الحماية من نقاط ضعف الكشف عن المعلومات (XSS ، والتضمين عن بعد ، وما إلى ذلك). إذا خرجت ، يمكن للمهاجم في نهاية المطاف كسر التشفير (لا يوجد تشفير 100٪ لا يمكن عكسه بدون المفتاح - كما يشيرNullUserException إلى أن هذا ليس صحيحًا تمامًا. هناك بعض أنظمة التشفير التي يتعذر OneTimePad مثل OneTimePad ) .

  3. أين يمكنني تخزين المفتاح الخاص؟

    ما سأفعله هو استخدام مفاتيح 3. واحد هو تزويد المستخدم ، واحد هو تطبيق محدد والآخر هو المستخدم محددة (مثل الملح). يمكن تخزين المفتاح الخاص بالتطبيق في أي مكان (في ملف تهيئة خارج جذر الويب ، في متغير بيئي ، وما إلى ذلك). سيتم تخزين المستخدم المحدد في عمود في db بجانب كلمة المرور المشفرة. المستخدم الذي تم توفيره لن يتم تخزينه. بعد ذلك ، ستفعل شيئًا كهذا:

    $key = $userKey . $serverKey . $userSuppliedKey;
    

    والفائدة هنا هي أن أي مفتاحين من المفاتيح يمكن اختراقهما دون تعرض البيانات للخطر. إذا كان هناك هجوم SQL Injection ، فيمكنهم الحصول على $userKey ، ولكن ليس على الطرف الآخر 2. إذا كان هناك استغلال محلي للخادم ، يمكنهم الحصول على $userKey و $serverKey ، ولكن ليس $userSuppliedKey . إذا قاموا بضرب المستخدم باستخدام مفتاح الربط ، فيمكنهم الحصول على $userSuppliedKey ، ولكن ليس 2 (ولكن مرة أخرى ، إذا تم ضرب المستخدم باستخدام مفتاح ربط ، فستكون قد فات الأوان على أية حال).

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

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

  5. بأي طرق يمكن سرقة كلمة المرور وفك تشفيرها؟ ما الذي يجب أن أكون على علم به؟

    سيتيح لك أي شكل من أشكال التنازلات في أنظمتك عرض البيانات المشفرة. إذا تمكنوا من حقن الكود أو الوصول إلى نظام الملفات الخاص بك ، فيمكنهم عرض البيانات التي تم فك تشفيرها (حيث يمكنهم تحرير الملفات التي تقوم بفك تشفير البيانات). أي شكل من أشكال هجوم Replay أو MITM سيعطيهم أيضًا حق الوصول الكامل إلى المفاتيح المعنية. إن استنشاق حركة مرور HTTP الأساسية سوف يمنحهم المفاتيح أيضًا.

    استخدم SSL لجميع الزيارات. وتأكد من عدم وجود أي نوع من أنواع الخلل في الخادم (CSRF ، XSS ، حقن SQL ، تصعيد الامتياز ، تنفيذ التعليمات البرمجية عن بعد ، إلخ).

تعديل: في ما يلي تطبيق لفئة PHP لأسلوب تشفير قوي:

/**
 * A class to handle secure encryption and decryption of arbitrary data
 *
 * Note that this is not just straight encryption.  It also has a few other
 *  features in it to make the encrypted data far more secure.  Note that any
 *  other implementations used to decrypt data will have to do the same exact
 *  operations.  
 *
 * Security Benefits:
 *
 * - Uses Key stretching
 * - Hides the Initialization Vector
 * - Does HMAC verification of source data
 *
 */
class Encryption {

    /**
     * @var string $cipher The mcrypt cipher to use for this instance
     */
    protected $cipher = '';

    /**
     * @var int $mode The mcrypt cipher mode to use
     */
    protected $mode = '';

    /**
     * @var int $rounds The number of rounds to feed into PBKDF2 for key generation
     */
    protected $rounds = 100;

    /**
     * Constructor!
     *
     * @param string $cipher The MCRYPT_* cypher to use for this instance
     * @param int    $mode   The MCRYPT_MODE_* mode to use for this instance
     * @param int    $rounds The number of PBKDF2 rounds to do on the key
     */
    public function __construct($cipher, $mode, $rounds = 100) {
        $this->cipher = $cipher;
        $this->mode = $mode;
        $this->rounds = (int) $rounds;
    }

    /**
     * Decrypt the data with the provided key
     *
     * @param string $data The encrypted datat to decrypt
     * @param string $key  The key to use for decryption
     * 
     * @returns string|false The returned string if decryption is successful
     *                           false if it is not
     */
    public function decrypt($data, $key) {
        $salt = substr($data, 0, 128);
        $enc = substr($data, 128, -64);
        $mac = substr($data, -64);

        list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);

        if (!hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
             return false;
        }

        $dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);

        $data = $this->unpad($dec);

        return $data;
    }

    /**
     * Encrypt the supplied data using the supplied key
     * 
     * @param string $data The data to encrypt
     * @param string $key  The key to encrypt with
     *
     * @returns string The encrypted data
     */
    public function encrypt($data, $key) {
        $salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
        list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);

        $data = $this->pad($data);

        $enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);

        $mac = hash_hmac('sha512', $enc, $macKey, true);
        return $salt . $enc . $mac;
    }

    /**
     * Generates a set of keys given a random salt and a master key
     *
     * @param string $salt A random string to change the keys each encryption
     * @param string $key  The supplied key to encrypt with
     *
     * @returns array An array of keys (a cipher key, a mac key, and a IV)
     */
    protected function getKeys($salt, $key) {
        $ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
        $keySize = mcrypt_get_key_size($this->cipher, $this->mode);
        $length = 2 * $keySize + $ivSize;

        $key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);

        $cipherKey = substr($key, 0, $keySize);
        $macKey = substr($key, $keySize, $keySize);
        $iv = substr($key, 2 * $keySize);
        return array($cipherKey, $macKey, $iv);
    }

    /**
     * Stretch the key using the PBKDF2 algorithm
     *
     * @see http://en.wikipedia.org/wiki/PBKDF2
     *
     * @param string $algo   The algorithm to use
     * @param string $key    The key to stretch
     * @param string $salt   A random salt
     * @param int    $rounds The number of rounds to derive
     * @param int    $length The length of the output key
     *
     * @returns string The derived key.
     */
    protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
        $size   = strlen(hash($algo, '', true));
        $len    = ceil($length / $size);
        $result = '';
        for ($i = 1; $i <= $len; $i++) {
            $tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
            $res = $tmp;
            for ($j = 1; $j < $rounds; $j++) {
                 $tmp  = hash_hmac($algo, $tmp, $key, true);
                 $res ^= $tmp;
            }
            $result .= $res;
        }
        return substr($result, 0, $length);
    }

    protected function pad($data) {
        $length = mcrypt_get_block_size($this->cipher, $this->mode);
        $padAmount = $length - strlen($data) % $length;
        if ($padAmount == 0) {
            $padAmount = $length;
        }
        return $data . str_repeat(chr($padAmount), $padAmount);
    }

    protected function unpad($data) {
        $length = mcrypt_get_block_size($this->cipher, $this->mode);
        $last = ord($data[strlen($data) - 1]);
        if ($last > $length) return false;
        if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
            return false;
        }
        return substr($data, 0, -1 * $last);
    }
}

لاحظ أنني أستخدم وظيفة مضافة في PHP 5.6: hash_equals . إذا كنت على أقل من 5.6 ، يمكنك استخدام هذه الوظيفة البديلة التي تقوم بتنفيذ وظيفة مقارنة آمنة للتوقيت باستخدام التحقق المزدوج من HMAC :

function hash_equals($a, $b) {
    $key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
    return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}

الاستعمال:

$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);

ثم ، لفك تشفير:

$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);

لاحظ أني استخدمت $e2 في المرة الثانية لإظهار أن المثيلات المختلفة ستستمر في فك تشفير البيانات بشكل صحيح.

الآن ، كيف تعمل / لماذا تستخدمها على حل آخر:

  1. مفاتيح

    • لا يتم استخدام المفاتيح مباشرة. بدلاً من ذلك ، يتم امتد المفتاح بواسطة اشتقاق PBKDF2 قياسي.

    • المفتاح المستخدم للتشفير فريد لكل كتلة نص مشفرة. وبالتالي يصبح المفتاح المزود "مفتاحًا رئيسيًا". ولذلك يوفر هذا الفصل دوران مفتاح التشفير ومفاتيح التشغيل.

    • ملاحظة هامة ، يتم تكوين المعلمة $rounds للمفاتيح العشوائية الحقيقية من قوة كافية (128 بت من عشوائية Cryptographically Secure كحد أدنى). إذا كنت ستستخدم كلمة مرور ، أو مفتاح غير عشوائي (أو أقل عشوائيًا ، ثم 128 بت من عشوائي CS) ، فيجب عليك زيادة هذه المعلمة. أود أن أقترح 10000 كحد أدنى لكلمات المرور (كلما زادت قدرتك على تحمل التكاليف ، كلما كان ذلك أفضل ، ولكنه سيضيف إلى وقت التشغيل) ...

  2. تكامل البيانات

    • تستخدم النسخة المحدّثة ENCRYPT-THEN-MAC ، وهي طريقة أفضل بكثير لضمان صحة البيانات المشفرة.
  3. التشفير:

    • ويستخدم mcrypt لتنفيذ التشفير بالفعل. أود أن أقترح استخدام إما MCRYPT_BLOWFISH أو MCRYPT_RIJNDAEL_128 cyphers و MCRYPT_MODE_CBC للوضع. إنه قوي بما فيه الكفاية ، ولا يزال سريعًا إلى حد ما (تستغرق دورة التشفير وفك التشفير حوالي 1/2 ثانية على جهازي).

الآن ، بالنسبة للنقطة 3 من القائمة الأولى ، فإن ما يمكن أن يعطيك هو وظيفة مثل:

function makeKey($userKey, $serverKey, $userSuppliedKey) {
    $key = hash_hmac('sha512', $userKey, $serverKey);
    $key = hash_hmac('sha512', $key, $userSuppliedKey);
    return $key;
}

يمكنك makeKey() وظيفة makeKey() ، ولكن نظرًا لأنه سيتم makeKey() لاحقًا ، فليس هناك بالفعل نقطة كبيرة للقيام بذلك.

بقدر ما حجم التخزين ، فإنه يعتمد على النص العادي. يستخدم Blowfish حجم كتلة 8 بايت ، لذلك سيكون لديك:

  • 16 بايت للملح
  • 64 بايت من أجل hmac
  • طول البيانات
  • الحشو بحيث طول البيانات٪ 8 == 0

لذا ، بالنسبة لمصدر بيانات مكون من 16 حرفًا ، سيكون هناك 16 حرفًا من البيانات المراد تشفيرها. وهذا يعني أن حجم البيانات المشفر الفعلي هو 16 بايت بسبب الحشو. ثم إضافة 16 بايت للملح و 64 بايت لـ hmac ويكون إجمالي حجم المخزن 96 بايت. لذلك هناك في أحسن الأحوال 80 حرفا ، وفي أسوأ الأحوال النفقات العامة 87 شخصية ...

أتمنى أن يساعد ذلك...

ملاحظة: 12/11/12: لقد قمت بتحديث هذا الفصل مع طريقة تشفير أفضل بكثير ، باستخدام مفاتيح مشتقة أفضل ، وإصلاح جيل MAC ...





passwords