การเข้ารหัสแบบสองทาง: ฉันจำเป็นต้องจัดเก็บรหัสผ่านที่สามารถดึงได้


174

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

สิ่งที่ฉันต้องรู้คือ:

  1. ฉันจะเข้ารหัสและถอดรหัสรหัสผ่านใน PHP ได้อย่างไร

  2. อัลกอริทึมที่ปลอดภัยที่สุดในการเข้ารหัสรหัสผ่านคืออะไร?

  3. ฉันจะเก็บกุญแจส่วนตัวได้ที่ไหน

  4. แทนที่จะเก็บรหัสส่วนตัวเป็นความคิดที่ดีที่จะกำหนดให้ผู้ใช้ป้อนรหัสส่วนตัวทุกครั้งที่ต้องการรหัสผ่านหรือไม่ (ผู้ใช้แอปพลิเคชันนี้สามารถเชื่อถือได้)

  5. รหัสผ่านสามารถขโมยและถอดรหัสได้ในรูปแบบใด? ฉันต้องระวังอะไรบ้าง


1
หมายเหตุ: ตอนนี้ Libsodium ถูกรวบรวมเป็นแกน PHP สำหรับ> = 7.2 นี่จะเป็นโซลูชัน "ไปที่" ในขณะนี้เนื่องจากเต็มไปด้วยวิธีการที่ทันสมัยซึ่งแตกต่างจาก mcrypt ซึ่งถือว่าเลิกใช้แล้วและถูกลบออกไป
แสดงนิทรรศการ

คำตอบ:


212

ส่วนตัวฉันจะใช้mcryptเหมือนคนอื่นโพสต์ แต่มีอีกมากมายที่ต้องทราบ ...

  1. ฉันจะเข้ารหัสและถอดรหัสรหัสผ่านใน PHP ได้อย่างไร

    ดูด้านล่างสำหรับคลาสที่แข็งแกร่งที่ดูแลทุกอย่างสำหรับคุณ:

  2. อัลกอริทึมที่ปลอดภัยที่สุดในการเข้ารหัสรหัสผ่านคืออะไร?

    ปลอดภัยที่สุด ? อะไรก็ได้. วิธีที่ปลอดภัยที่สุดหากคุณกำลังจะเข้ารหัสคือการป้องกันช่องโหว่การเปิดเผยข้อมูล (XSS, การรวมระยะไกล ฯลฯ ) หากออกมาแล้วผู้โจมตีสามารถถอดรหัสได้ในที่สุด (ไม่มีการเข้ารหัสใด ๆ ที่ไม่สามารถย้อนกลับได้ 100% หากไม่มีคีย์ - เนื่องจาก @NullUserException ชี้ให้เห็นว่าสิ่งนี้ไม่เป็นความจริงโดยสิ้นเชิงมีรูปแบบการเข้ารหัสบางอย่างที่ไม่สามารถถอดรหัสได้เช่นOneTimePad ) .

  3. ฉันจะเก็บกุญแจส่วนตัวได้ที่ไหน

    สิ่งที่ฉันจะทำคือใช้ 3 ปุ่ม หนึ่งคือผู้ใช้ที่ให้มาหนึ่งคือโปรแกรมเฉพาะและอื่น ๆ เป็นของผู้ใช้เฉพาะ (เช่นเกลือ) คีย์เฉพาะแอปพลิเคชันสามารถจัดเก็บได้ทุกที่ (ในไฟล์ปรับแต่งภายนอกของเว็บรูทในตัวแปรสภาพแวดล้อม ฯลฯ ) ผู้ใช้เฉพาะจะถูกเก็บไว้ในคอลัมน์ในฐานข้อมูลถัดจากรหัสผ่านที่เข้ารหัส ผู้ใช้ที่ให้มาจะไม่ถูกจัดเก็บ จากนั้นคุณจะทำสิ่งนี้:

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

    ประโยชน์ที่ได้รับคือกุญแจใด ๆ ที่สามารถประนีประนอมได้โดยไม่ทำให้ข้อมูลถูกบุกรุก หากมีการโจมตี SQL Injection พวกเขาจะได้รับ$userKeyแต่ไม่ได้อื่น ๆ 2. หากมีเซิร์ฟเวอร์ท้องถิ่นใช้ประโยชน์จากพวกเขาจะได้รับ$userKeyและแต่ไม่สาม$serverKey $userSuppliedKeyหากพวกเขาเอาชนะผู้ใช้ด้วยประแจพวกเขาสามารถได้รับ$userSuppliedKeyแต่ไม่ใช่อีก 2 คน (แต่หลังจากนั้นอีกครั้งหากผู้ใช้ทุบด้วยประแจคุณจะสายเกินไป)

  4. แทนที่จะเก็บรหัสส่วนตัวเป็นความคิดที่ดีที่จะกำหนดให้ผู้ใช้ป้อนรหัสส่วนตัวทุกครั้งที่ต้องการรหัสผ่านหรือไม่ (ผู้ใช้แอปพลิเคชันนี้สามารถเชื่อถือได้)

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

  5. รหัสผ่านสามารถขโมยและถอดรหัสได้ในรูปแบบใด? ฉันต้องระวังอะไรบ้าง

    รูปแบบของการประนีประนอมระบบของคุณจะช่วยให้พวกเขาดูข้อมูลที่เข้ารหัส หากพวกเขาสามารถฉีดโค้ดหรือไปยังระบบไฟล์ของคุณพวกเขาสามารถดูข้อมูลที่ถอดรหัสได้ (เนื่องจากพวกเขาสามารถแก้ไขไฟล์ที่ถอดรหัสข้อมูล) การโจมตี Replay หรือ MITM ทุกรูปแบบจะทำให้พวกเขาสามารถเข้าถึงกุญแจที่เกี่ยวข้องได้อย่างเต็มที่ การสูดดมทราฟฟิก HTTP แบบ raw จะให้กุญแจแก่พวกเขา

    ใช้ 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 hash_equals5.6: หากคุณต่ำกว่า 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 การรักษาความปลอดภัยแบบสุ่มอย่างน้อย) หากคุณจะใช้รหัสผ่านหรือคีย์ที่ไม่ใช่แบบสุ่ม (หรือสุ่มน้อยกว่า 128 บิตของ CS Random) คุณต้องเพิ่มพารามิเตอร์นี้ ฉันขอแนะนำอย่างน้อย 10,000 รหัสผ่าน (ยิ่งคุณสามารถจ่ายได้ดีเท่าไรก็ยิ่งดี แต่มันจะเพิ่มใน runtime) ...

  2. ความสมบูรณ์ของข้อมูล

    • รุ่นที่ได้รับการปรับปรุงใช้ ENCRYPT-THEN-MAC ซึ่งเป็นวิธีที่ดีกว่ามากในการรับรองความถูกต้องของข้อมูลที่เข้ารหัส
  3. การเข้ารหัสลับ:

    • มันใช้ mcrypt เพื่อทำการเข้ารหัส ฉันอยากจะแนะนำให้ใช้อย่างใดอย่างหนึ่งMCRYPT_BLOWFISHหรือMCRYPT_RIJNDAEL_128cyphers และ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()ฟังก์ชั่นได้ แต่เนื่องจากมันจะยืดตัวในภายหลัง

สำหรับขนาดหน่วยเก็บข้อมูลนั้นขึ้นอยู่กับข้อความธรรมดา ปักเป้าใช้ขนาดบล็อก 8 ไบต์ดังนั้นคุณจะมี:

  • 16 ไบต์สำหรับเกลือ
  • 64 ไบต์สำหรับ hmac
  • ความยาวข้อมูล
  • การเสริมความยาวของข้อมูล% 8 == 0

ดังนั้นสำหรับแหล่งข้อมูล 16 อักขระจะมีการเข้ารหัสข้อมูล 16 อักขระ ดังนั้นหมายความว่าขนาดข้อมูลที่เข้ารหัสจริงคือ 16 ไบต์เนื่องจากการขยาย จากนั้นเพิ่ม 16 ไบต์สำหรับเกลือและ 64 ไบต์สำหรับ hmac และขนาดที่เก็บไว้ทั้งหมดคือ 96 ไบต์ ดังนั้นจึงมีค่าใช้จ่าย 80 ตัวอักษรที่ดีที่สุดและที่แย่ที่สุดค่าตัวอักษร 87 ตัว ...

ฉันหวังว่าจะช่วย ...

หมายเหตุ: 12/11/12: ฉันเพิ่งอัปเดตคลาสนี้ด้วยวิธีการเข้ารหัสที่ดีกว่ามากใช้คีย์ที่ได้รับดีกว่าและแก้ไขการสร้าง MAC ...


3
บางคนไม่เข้าใจว่า "หยุด" หมายความว่าอะไร @IRC เป็นงานที่ดีในชั้นเรียนซึ่งเป็นรหัสที่ดีงาม
jcolebrand

1
ผลตอบแทนต่อไปนี้เป็นเท็จ มีความคิดอะไรไหม $ x = การเข้ารหัสใหม่ (MCRYPT_BlOWFISH, MCRYPT_MODE_CBC); $ test = $ x-> เข้ารหัส ("ทดสอบ", "a"); echo var_dump ($ x-> ถอดรหัส ($ test, "a"));
ความยาวคลื่น

2
โอ้และอีกครั้งในฟังก์ชั่นถอดรหัสเปลี่ยนสอง-64s เพื่อ-128ช่วย (เพื่อให้คุณได้รับ$enc = substr($data, 128, -128)และ$mac = substr($data, -128);
cosmorogers

4
@ircmaxell มันค่อนข้างนานแล้วตั้งแต่รหัสได้รับการแก้ไขครั้งล่าสุดดังนั้นฉันสงสัยว่ามันทันสมัยหรือไม่ ฉันจำเป็นต้องใช้สิ่งที่คล้ายกันสำหรับแอปพลิเคชันทางการเงินและมันจะดีถ้าคุณให้โอเคกับคลาสนี้ :-)
nt.bas

2
คำเตือน! ส่วนขยาย mcrypt นั้นได้ถูกทิ้งไว้เป็นเวลาเกือบหนึ่งทศวรรษแล้วและยังมีความซับซ้อนในการใช้งาน ดังนั้นจึงถูกเลิกใช้ในความโปรดปรานของ OpenSSL ซึ่งจะถูกลบออกจากแกนกลางและเป็น PECL ใน PHP 7.2 th1.php.net/manual/th/migration71.deprecated.php
vee

15

ฉันจะเข้ารหัสและถอดรหัสรหัสผ่านใน PHP ได้อย่างไร ด้วยการใช้อัลกอริธึมการเข้ารหัสหนึ่งในหลาย ๆ (หรือใช้หนึ่งในหลาย ๆ ไลบรารี)

อัลกอริทึมที่ปลอดภัยที่สุดในการเข้ารหัสรหัสผ่านคืออะไร? มีอัลกอริธึมที่แตกต่างกันมากมายซึ่งไม่มีความปลอดภัย 100% แต่หลายคนมีความปลอดภัยเพียงพอสำหรับการค้าและแม้กระทั่งวัตถุประสงค์ทางทหาร

ฉันจะเก็บกุญแจส่วนตัวได้ที่ไหน หากคุณตัดสินใจที่จะใช้กุญแจสาธารณะ - อัลกอริธึมการเข้ารหัส (เช่น RSA) คุณจะไม่เก็บคีย์ส่วนตัว ผู้ใช้มีรหัสส่วนตัว ระบบของคุณมีรหัสสาธารณะซึ่งสามารถจัดเก็บได้ทุกที่ที่คุณต้องการ

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

รหัสผ่านสามารถขโมยและถอดรหัสได้ในรูปแบบใด? ฉันต้องระวังอะไรบ้าง ขึ้นอยู่กับอัลกอริทึมที่ใช้ อย่างไรก็ตามตรวจสอบให้แน่ใจเสมอว่าคุณจะไม่ส่งรหัสผ่านที่ไม่ได้เข้ารหัสไปยังหรือจากผู้ใช้ เข้ารหัส / ถอดรหัสบนฝั่งไคลเอ็นต์หรือใช้ https (หรือวิธีการเข้ารหัสลับอื่น ๆ ของผู้ใช้เพื่อรักษาความปลอดภัยการเชื่อมต่อระหว่างเซิร์ฟเวอร์และไคลเอนต์)

อย่างไรก็ตามหากคุณต้องการเก็บรหัสผ่านด้วยวิธีการเข้ารหัสฉันขอแนะนำให้คุณใช้รหัส XOR แบบง่าย ปัญหาหลักของอัลกอริธึมนี้คือการวิเคราะห์ความถี่ทำได้ง่าย ๆ อย่างไรก็ตามโดยทั่วไปรหัสผ่านไม่ได้ทำมาจากข้อความภาษาอังกฤษย่อหน้ายาวฉันไม่คิดว่าคุณควรกังวลเกี่ยวกับมัน ปัญหาที่สองของ XOR Cipher คือถ้าคุณมีข้อความในรูปแบบที่เข้ารหัสและถอดรหัสคุณสามารถค้นหารหัสผ่านที่มันถูกเข้ารหัสได้อย่างง่ายดาย ไม่ใช่ปัญหาใหญ่ในกรณีของคุณเนื่องจากมีผลกับผู้ใช้ที่ถูกบุกรุกโดยวิธีการอื่นอยู่แล้ว


ในคำตอบ 3 เมื่อคุณบอกว่าผู้ใช้มีรหัสส่วนตัวฉันไม่เข้าใจความหมาย คุณไม่แนะนำให้ส่งรหัสส่วนตัวในแอปพลิเคชันด้วยตนเองโดยผู้ใช้ดังนั้นจะมีการส่งคีย์ส่วนตัวไปยังแอปพลิเคชันอย่างไร
HyderA

นั่นเป็นปัญหาเล็กน้อย รหัสส่วนตัวสามารถเก็บไว้ในไฟล์ข้อความแล้วคัดลอกวางไปยังแอป รหัสอาจถูกเก็บไว้บนเซิร์ฟเวอร์ แต่ในกรณีนี้ก็ควรจะเข้ารหัสด้วยอัลกอริทึมการเข้ารหัสอื่น ๆ เช่น XOR การใช้แฮคเกอร์ในกรณีนี้มีความปลอดภัยเพียงพอเนื่องจากมีคู่ข้อความรหัสผ่านเพียงคู่เดียวและข้อความค่อนข้างสุ่มดังนั้นการวิเคราะห์ความถี่จึงไม่สามารถใช้งานได้
อีวาน

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

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

1
@Ivan: ใช่ แต่นี่เป็นหนึ่งในกรณีที่ฉันคิดว่า DIY มันแย่จริงๆเว้นแต่คุณจะเข้าใจการเข้ารหัส มียันต์ที่แข็งแกร่งอยู่ทำไมไม่ลองใช้มัน?
ircmaxell

13
  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. อัลกอริทึมที่ดีที่สุดค่อนข้างเป็นอัตวิสัย - ถาม 5 คนได้ 5 คำตอบ โดยส่วนตัวถ้าค่าเริ่มต้น (ปักเป้า) ไม่ดีพอสำหรับคุณคุณอาจมีปัญหามากขึ้น!

  2. ระบุว่า PHP ต้องการการเข้ารหัส - ไม่แน่ใจว่าคุณสามารถซ่อนได้ทุกที่ - ยินดีรับความคิดเห็น แน่นอนว่าการใช้งานการเข้ารหัสมาตรฐานที่ดีที่สุดของ PHP นั้นแน่นอน!

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

  4. เห็นได้ชัดว่าหากรหัสผ่านที่เข้ารหัสและคีย์เข้ารหัสถูกขโมยไปให้ลองเล่นใหม่

ฉันจะให้นักปั่นตอบคำถามของฉัน - ฉันไม่ใช่ผู้เชี่ยวชาญ crypto PHP แต่ฉันคิดว่าสิ่งที่ฉันตอบคือการปฏิบัติตามมาตรฐาน - ฉันยินดีรับฟังความคิดเห็นอื่น ๆ ที่อาจมี


$pass = $text. ฉันคิดว่าเขาเปลี่ยนสิ่งนั้นเพื่อตอบคำถามและไม่ได้สังเกตเห็นเหตุการณ์ที่สอง
HyderA

3
สองสิ่งที่ควรทราบ ก่อนอื่นMCRYPT_MODE_ECBอย่าใช้ IV ประการที่สองถ้าเป็นเช่นนั้นคุณจะต้องเก็บ IV เพราะคุณไม่สามารถถอดรหัสข้อมูลโดยที่ไม่ต้องใช้มัน ...
ircmaxell

"อัลกอริทึมที่ดีที่สุดค่อนข้างเป็นอัตวิสัย - ถาม 5 คนได้ 5 คำตอบโดยส่วนตัวถ้าค่าเริ่มต้น (ปักเป้า) ไม่ดีพอสำหรับคุณคุณอาจมีปัญหาใหญ่กว่า!" มันผิดทั้งหมด ผู้เชี่ยวชาญ crypto ทุกคนจะเห็นด้วยกับgist.github.com/tqbf/be58d2d39690c3b366adมากขึ้นหรือน้อยลงซึ่งไม่รวมปลาปักเป้าเป็นพิเศษ
Scott Arciszewski

6

ผู้ใช้หลายคนแนะนำให้ใช้ mcrypt ... ซึ่งถูกต้อง แต่ฉันชอบที่จะก้าวไปอีกขั้นหนึ่งเพื่อให้สามารถจัดเก็บและถ่ายโอนได้ง่าย (บางครั้งค่าเข้ารหัสสามารถทำให้ยากต่อการส่งโดยใช้เทคโนโลยีอื่นเช่น curl หรือ json) .

หลังจากที่คุณเข้ารหัสสำเร็จโดยใช้ mcrypt ให้รันผ่าน base64_encode แล้วแปลงเป็นรหัสฐานสิบหก ครั้งเดียวในรหัสฐานสิบหกมันง่ายที่จะถ่ายโอนในหลากหลายวิธี

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


2
ก็คือในปี 2011: P
แบรดลีย์

5

ฉันขอแนะนำให้ใช้การเข้ารหัสคีย์สาธารณะเฉพาะในกรณีที่คุณต้องการความสามารถในการตั้งรหัสผ่านของผู้ใช้โดยไม่ต้องมีการโต้ตอบใด ๆ (ซึ่งเป็นประโยชน์สำหรับการรีเซ็ตและรหัสผ่านที่แชร์)

กุญแจสาธารณะ

  1. OpenSSLขยายเฉพาะopenssl_public_encryptและopenssl_private_decrypt
  2. นี่จะเป็น RSA ที่ตรงไปตรงมาถ้ารหัสผ่านของคุณจะพอดีกับขนาดของคีย์ - การเติมเต็มมิฉะนั้นคุณต้องมีเลเยอร์สมมาตร
  3. เก็บคีย์ทั้งสองไว้สำหรับผู้ใช้แต่ละคนข้อความรหัสผ่านส่วนตัวของไพรเวตคือรหัสผ่านแอปพลิเคชันของพวกเขา

สมมาตร

  1. Mcryptขยาย
  2. AES-256 อาจเป็นทางออกที่ปลอดภัย แต่นี่อาจเป็นคำถาม SO ในตัวของมันเอง
  3. คุณทำไม่ได้ - นี่คือรหัสผ่านแอปพลิเคชันของพวกเขา

ทั้งสอง

4. ใช่ - ผู้ใช้จะต้องป้อนรหัสผ่านแอปพลิเคชันทุกครั้ง แต่การเก็บไว้ในเซสชันจะทำให้เกิดปัญหาอื่น ๆ

5.

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

หลีกเลี่ยงการ Mcrypt openssl_private_decrypt()ต้องระวังด้วย
Scott Arciszewski

2

ฉันลองอะไรเช่นนี้ แต่โปรดทราบว่าฉันไม่ได้เป็น cryptographer หรือฉันมีความรู้เชิงลึกเกี่ยวกับphpหรือภาษาการเขียนโปรแกรมใด ๆ มันเป็นแค่ความคิด ความคิดของฉันคือการจัดเก็บkeyในไฟล์บางไฟล์หรือdatabase(หรือป้อนด้วยตนเอง) ซึ่ง (ตำแหน่ง) ไม่สามารถคาดเดาได้ง่าย (และแน่นอนว่าทุกอย่างจะถูกถอดรหัสสักวันหนึ่งแนวคิดคือการยืดเวลาถอดรหัส) และเข้ารหัสข้อมูลที่มีความละเอียดอ่อน

$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH , MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "evenifyouaccessmydatabaseyouwillneverfindmyemail";
$text = "myemail@domain.com";
echo "Key : ".$key."<br/>";
echo "Text : ".$text . "<br/>";
echo "Md5 : ".md5($text). "<br/>";
echo "Sha1 : ".sha1($text). "<br/>";



$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH , $key, $text, MCRYPT_MODE_ECB, $iv);
echo "Crypted Data : ".$crypttext."<br>";

$base64 = base64_encode($crypttext);
echo "Encoded Data : ".$base64."<br/>";
$decode =  base64_decode($base64);


$decryptdata = mcrypt_decrypt(MCRYPT_BLOWFISH , $key, $crypttext, MCRYPT_MODE_ECB, $iv);

echo "Decoded Data : ".ereg_replace("?", null ,  $decryptdata); 
//event if i add '?' to the sting to the text it works, I don't know why.

โปรดทราบว่ามันเป็นเพียงแนวคิด การปรับปรุงใด ๆ ของรหัสนี้จะสามารถประเมินได้


2

รหัสผ่านมีไว้สำหรับอุปกรณ์ฮาร์ดแวร์ดังนั้นการตรวจสอบกับแฮชนั้นเกิดจากคำถาม

ใช่มั้ย? ฉันไม่เข้าใจ คุณแค่หมายความว่ารหัสผ่านจะต้องกู้คืนได้

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

ช่องโหว่ด้านความปลอดภัยส่วนใหญ่ไม่ได้เกิดขึ้นเนื่องจากอัลกอริธึมพื้นฐานมีข้อบกพร่องหรือไม่ปลอดภัย - แต่เนื่องจากปัญหาเกี่ยวกับวิธีการใช้อัลกอริทึมภายในรหัสแอปพลิเคชัน

ต้องบอกว่ามันเป็นไปได้ที่จะสร้างระบบที่ปลอดภัยพอสมควร

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

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


1

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