programing

양방향 암호화 : 검색 할 수있는 비밀번호를 저장해야합니다

new-time 2020. 5. 24. 22:26
반응형

양방향 암호화 : 검색 할 수있는 비밀번호를 저장해야합니다


사용자가 검색하고 볼 수있는 비밀번호를 저장할 애플리케이션을 작성 중입니다. 암호는 하드웨어 장치 용이므로 해시 검사는 문제가되지 않습니다.내가 알아야 할 것은 :

  1. PHP에서 암호를 어떻게 암호화하고 해독합니까?
  2. 비밀번호를 암호화하는 가장 안전한 알고리즘은 무엇입니까?
  3. 개인 키는 어디에 저장합니까?
  4. 개인 키를 저장하는 대신 암호를 해독해야 할 때마다 사용자가 개인 키를 입력하도록 요구하는 것이 좋습니다. (이 응용 프로그램의 사용자는 신뢰할 수 있습니다)
  5. 비밀번호를 도난 및 해독 할 수있는 방법은 무엇입니까? 무엇을 알고 있어야합니까?

개인적으로

mcrypt

다른 사람들이 게시 한 것처럼 사용 합니다. 그러나 주목할 것이 훨씬 더 많습니다 ...

  1. PHP에서 암호를 어떻게 암호화하고 해독합니까?당신을 위해 모든 것을 돌보는 강력한 수업은 아래를 참조하십시오.
  2. 비밀번호를 암호화하는 가장 안전한 알고리즘은 무엇입니까?

    가장 안전한

    가요? 그들 중 하나. 암호화하려는 경우 가장 안전한 방법은 정보 유출 취약성 (XSS, 원격 포함 등)을 방지하는 것입니다. 그것이 유출되면 공격자는 결국 암호화를 해독 할 수 있습니다 (키없이 암호화를 되돌릴 수없는 100 %는 없습니다-@NullUserException이 지적한 것처럼 이것은 완전히 사실이 아닙니다.

    OneTimePad

    와 같이 해독이 불가능한 일부 암호화 체계가 있습니다 ) .
  3. 개인 키는 어디에 저장합니까?내가 할 일은 3 키를 사용하는 것입니다. 하나는 사용자가 제공하고 다른 하나는 응용 프로그램에 따라 다른 하나는 소금과 같은 사용자에 따라 다릅니다. 응용 프로그램 특정 키는 웹 루트 외부의 환경 설정 파일, 환경 변수 등 어디에나 저장할 수 있습니다. 사용자 별 암호는 암호화 된 암호 옆에있는 DB의 열에 저장됩니다. 사용자가 제공 한 것이 저장되지 않았습니다. 그런 다음 다음과 같은 작업을 수행합니다.
    $key = $userKey . $serverKey . $userSuppliedKey;
    
    이로 인한 이점은 데이터를 손상시키지 않고 2 개의 키를 손상시킬 수 있다는 것입니다. SQL 주입 공격이 있다면, 그들은을 얻을 수

    $userKey

    있지만, 다른 2 로컬 서버가 악용이 있다면, 그들이 얻을 수

    $userKey

    하고

    $serverKey

    있지만, 셋째 없습니다

    $userSuppliedKey

    . 그들이 렌치로 사용자를 이길 경우

    $userSuppliedKey

    다른 2를 얻을 수는 없지만 다시 렌치를 사용하면 너무 늦습니다.
  4. 개인 키를 저장하는 대신 암호를 해독해야 할 때마다 사용자가 개인 키를 입력하도록 요구하는 것이 좋습니다. (이 응용 프로그램의 사용자는 신뢰할 수 있습니다)물론. 사실, 내가 할 유일한 방법입니다. 그렇지 않으면 암호화되지 않은 버전을 내구성있는 저장소 형식 (APC 또는 memcached와 같은 공유 메모리 또는 세션 파일)으로 저장해야합니다. 그것은 추가 타협에 노출됩니다. 암호화되지 않은 암호 버전을 로컬 변수 이외의 다른 곳에 저장하지 마십시오.
  5. 비밀번호를 도난 및 해독 할 수있는 방법은 무엇입니까? 무엇을 알고 있어야합니까?시스템이 손상되면 암호화 된 데이터를 볼 수 있습니다. 코드를 삽입하거나 파일 시스템에 액세스 할 수있는 경우 해독 된 데이터를 볼 수 있습니다 (데이터를 해독하는 파일을 편집 할 수 있으므로). 모든 형태의 Replay 또는 MITM 공격도 관련된 키에 대한 모든 액세스 권한을 부여합니다. 원시 HTTP 트래픽을 스니핑하면 키가 제공됩니다.모든 트래픽에 SSL을 사용하십시오. 그리고 서버에 어떤 종류의 취약점 (CSRF, XSS, SQL Injection, Privilege Escalation, Remote Code Execution 등)이 없는지 확인하십시오.

편집 :

강력한 암호화 방법의 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 비트 암호화 보안 임의)로 구성됩니다. 암호 또는 비 랜덤 키 (또는 임의의 128 비트 CS 랜덤보다 적은 임의의 키)를 사용하려는 경우이 매개 변수

      늘려야합니다. 나는 암호에 대해 최소 10000을 제안 할 것입니다 (여러분이 감당할수록 더 좋지만 런타임에 추가됩니다) ...
  2. 데이터 무결성
    • 업데이트 된 버전은 ENCRYPT-THEN-MAC를 사용하는데, 이는 암호화 된 데이터의 신뢰성을 보장하는 훨씬 더 좋은 방법입니다.
  3. 암호화 :
    • mcrypt를 사용하여 실제로 암호화를 수행합니다. 하나 MCRYPT_BLOWFISH또는 MCRYPT_RIJNDAEL_128사이퍼 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()

함수 에서 확장 할 수는 있지만 나중에 확장 될 것이기 때문에 실제로 그렇게 큰 포인트는 없습니다.저장 크기까지는 일반 텍스트에 따라 다릅니다. Blowfish는 8 바이트 블록 크기를 사용하므로 다음과 같은 이점이 있습니다.

  • 소금은 16 바이트
  • hmac의 경우 64 바이트
  • 데이터 길이
  • 데이터 길이 % 8 == 0이되도록 채우기

따라서 16 자 데이터 소스의 경우 16 자 데이터가 암호화됩니다. 따라서 실제 암호화 된 데이터 크기는 패딩으로 인해 16 바이트입니다. 그런 다음 salt에 16 바이트를 추가하고 hmac에 64 바이트를 추가하면 총 저장 크기는 96 바이트입니다. 따라서 최대 80 자의 오버 헤드가 발생하고 최악의 경우 87 자의 오버 헤드가 발생합니다.도움이되기를 바랍니다 ...

참고 :

12/11/12 : 방금 더 나은 파생 키를 사용하고 MAC 생성을 수정하는 훨씬 나은 암호화 방법 으로이 클래스를 업데이트했습니다 ...


PHP에서 암호를 어떻게 암호화하고 해독합니까?

많은 암호화 알고리즘 중 하나를 구현합니다. (또는 많은 라이브러리 중 하나를 사용)

비밀번호를 암호화하는 가장 안전한 알고리즘은 무엇입니까?

다양한 알고리즘이 있으며 100 % 안전하지는 않습니다. 그러나 이들 중 다수는 상업 및 군사 목적으로도 충분히 안전합니다.

개인 키는 어디에 저장합니까?

공개 키-암호화 알고리즘 (예 : RSA)을 구현하기로 결정한 경우 개인 키를 저장하지 않습니다. 사용자는 개인 키를 가지고 있습니다. 시스템에는 원하는 곳에 저장할 수있는 공개 키가 있습니다.

개인 키를 저장하는 대신 암호를 해독해야 할 때마다 사용자가 개인 키를 입력하도록 요구하는 것이 좋습니다. (이 응용 프로그램의 사용자는 신뢰할 수 있습니다.)

사용자가 엄청나게 긴 소수를 기억할 수 있다면-그렇습니다. 그러나 일반적으로 사용자는 키를 어딘가에 저장할 수있는 시스템을 고안해야합니다.

비밀번호를 도난 및 해독 할 수있는 방법은 무엇입니까? 무엇을 알고 있어야합니까?

이것은 사용 된 알고리즘에 따라 다릅니다. 그러나 항상 암호화되지 않은 비밀번호를 사용자에게 보내거나 사용자에게 보내지 마십시오. 클라이언트 측에서 암호화 / 암호 해독하거나 https (또는 사용자의 다른 암호화 수단을 사용하여 서버와 클라이언트 간의 연결을 보호하십시오).그러나 암호를 암호화 된 방식으로 저장하기 만하면 간단한 XOR 암호를 사용하는 것이 좋습니다. 이 알고리즘의 주된 문제점은 주파수 분석에 의해 쉽게 깨질 수 있다는 것입니다. 그러나 일반적으로 암호는 영어 텍스트의 긴 단락으로 만들어지지 않으므로 걱정할 필요가 없다고 생각합니다. XOR 암호의 두 번째 문제점은 암호화 된 형식과 암호 해독 된 형식의 메시지가 있으면 암호화 된 암호를 쉽게 찾을 수 있다는 것입니다. 다시 말하지만, 다른 방법으로 이미 손상된 사용자에게만 영향을 미치기 때문에 큰 문제는 아닙니다.


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

You would use mcrypt_decrypt to decrypt your password.

  1. The best algorithm is rather subjective - ask 5 people, get 5 answers. Personally if the the default (Blowfish) isn't good enough for you, you probably have bigger problems!

  2. Given that it is needed by PHP to encrypt - not sure you can hide it anywhere - welcome comments on this. Standard PHP best coding practices apply of course!

  3. Given that the encryption key will be in your code anyway, not sure what you will gain, providing the rest of your application is secure.

  4. Obviously, if the encrypted password and the encryption key are stolen, then game over.

I'd put a rider on my answer - I'm not a PHP crypto expert, but, I think what I have answered is standard practice - I welcome comments other may have.


A lot of users have suggested using mcrypt... which is correct, but I like to go a step further to make it easily stored and transfered (as sometimes encrypted values can make them hard to send using other technologies like curl, or json).

After you have successfully encrypted using mcrypt, run it through base64_encode and then convert it to hex code. Once in hex code it's easy to transfer in a variety of ways.

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

And on the other side:

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

I'd only suggest public key encryption if you want the ability to set a user's password without their interaction (this can be handy for resets and shared passwords).

Public key

  1. The OpenSSL extension, specifically openssl_public_encrypt and openssl_private_decrypt
  2. This would be straight RSA assuming your passwords will fit in key size - padding, otherwise you need a symmetric layer
  3. Store both keys for each user, the private key's passphrase is their application password

Symmetric

  1. The Mcrypt extension
  2. AES-256 is probably a safe bet, but this could be a SO question in itself
  3. You don't - this would be their application password

Both

4. Yes - users would have to enter their application password every time, but storing it in the session would raise other issues

5.

  • If someone steals the application data, it's as secure as the symmetric cipher (for the public key scheme, it's used to protect the private key with the passphrase.)
  • Your application should definitely be only accessible over SSL, preferably using client certificates.
  • Consider adding a second factor for authentication which would only be used once per session, like a token sent via SMS.

I tried something like this but please note that I am not cryptographer nor I hold in-depth knowledge about php or any programming language. It's just an idea. My idea is to store key in some file or database (or enter manually) which(location) cannot be easily predicted(And of course anything will be decrypted someday, the concept is to lengthen the decryption time) and encrypt sensitive information.

$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.

Please note that it is just a concept. Any improvement on this code would be highly appreciable.


The passwords are for a hardware device, so checking against hashes are out of the question

Eh? I don't understand. Do you just mean that password must be recoverable?

As others have said, the mcrypt extension provides access to lots of cryptographic functions - however you are inviting your users to put all their eggs in one basket - one which will be potentially be a target for attackers - and if you don't even know how to start solving the problem then you are doing your users a disservice. You are not in a position to understand how to protect the data.

Most security vulnerabilities come about not because the underlying algorithm is flawed or insecure - but because of problems with the way the algorithm is used within the application code.

Having said that, it is possible to build a reasonably secure system.

You should only consider asymmetric encryption if you have a requirement for a user to create a secure message which is readable by another (specific) user. The reason being that its computationally expensive. If you just want to provide a repository for users to enter and retrieve their own data, symmetric encryption is adequate.

If, however, you store the key for decrypting the message in the same place as the encrypted message (or where the encrypted message is stored) then the system is not secure. Use the same token for authenticating the user as for the decryption key (or in the case of assymetric encryption, use the token as the private key pass phrase). Since you will need to store the token on the server where the decryption takes place at least temporarily, you might want to consider using a non-searchable session storage substrate, or passing the token directly to a daemon associated with the session which would store the token in memory and perform the decryption of messages on demand.


Use password_hash and 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";
?>

And to decrypt:

<?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.';
}
?>

참고URL : https://stackoverflow.com/questions/5089841/two-way-encryption-i-need-to-store-passwords-that-can-be-retrieved

반응형