Я создаю приложение, которое будет хранить пароли, которые пользователь может получить и посмотреть. Пароли для аппаратного устройства, поэтому проверка на хэш не может быть и речи.
Мне нужно знать:
Как шифровать и расшифровывать пароль в PHP?
Какой самый безопасный алгоритм шифрования паролей?
Где я храню закрытый ключ?
Вместо того, чтобы хранить закрытый ключ, рекомендуется ли пользователям вводить закрытый ключ в любое время, когда им нужен пароль, расшифрованный? (Пользователям этого приложения можно доверять)
Каким образом пароль может быть украден и дешифрован? Что мне нужно знать?
Лично я бы использовал mcrypt
как и другие. Но есть еще много примечаний …
Как шифровать и расшифровывать пароль в PHP?
Ниже приведен сильный класс, который заботится обо всем для вас:
Какой самый безопасный алгоритм шифрования паролей?
безопаснее ? любой из них. Самый безопасный метод, если вы собираетесь шифровать, – это защита от уязвимостей раскрытия информации (XSS, удаленное включение и т. Д.). Если это выяснится, злоумышленник может в конечном итоге взломать шифрование (без шифрования на 100% не обратимо без ключа). Как указывает @NullUserException, это не совсем так. Существуют некоторые схемы шифрования, которые невозможно взломать, например OneTimePad ) ,
Где я храню закрытый ключ?
Что бы я сделал, это использовать 3 клавиши. Один из них предоставляется пользователю, один из них является специфичным для приложения, а другой – специфичным для пользователя (например, соль). Ключ, специфичный для приложения, можно хранить в любом месте (в файле конфигурации вне веб-корня, в переменной среды и т. Д.). Конкретный пользователь будет храниться в столбце в db рядом с зашифрованным паролем. Пользователь, поставленный пользователем, не будет сохранен. Тогда вы сделали бы что-то вроде этого:
$key = $userKey . $serverKey . $userSuppliedKey;
Преимущество там состоит в том, что любые 2 ключа могут быть скомпрометированы без ущерба для данных. Если есть атака SQL Injection, они могут получить $userKey
, но не другие 2. Если есть локальный серверный эксплойт, они могут получить $userKey
и $serverKey
, но не третий $userSuppliedKey
. Если они будут бить пользователя с помощью гаечного ключа, они могут получить $userSuppliedKey
, но не другие 2 (но опять же, если пользователь избит с помощью гаечного ключа, вы все равно слишком поздно).
Вместо того, чтобы хранить закрытый ключ, рекомендуется ли пользователям вводить закрытый ключ в любое время, когда им нужен пароль, расшифрованный? (Пользователям этого приложения можно доверять)
Абсолютно. На самом деле, это единственный способ сделать это. В противном случае вам нужно будет сохранить незашифрованную версию в формате длительного хранения (разделяемая память, такая как APC или memcached, или в файле сеанса). Это подвергает себя дополнительным компромиссам. Никогда не храните незашифрованную версию пароля ничем, кроме локальной переменной.
Каким образом пароль может быть украден и дешифрован? Что мне нужно знать?
Любая форма компрометации ваших систем позволит им просматривать зашифрованные данные. Если они могут вводить код или подключаться к вашей файловой системе, они могут просматривать дешифрованные данные (поскольку они могут редактировать файлы, которые расшифровывают данные). Любая форма атаки 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
чтобы показать вам разные экземпляры, которые все равно будут правильно расшифровывать данные.
Теперь, как это работает / зачем использовать его над другим решением:
Ключи
Ключи не используются напрямую. Вместо этого ключ растягивается стандартным выводом PBKDF2.
Ключ, используемый для шифрования, уникален для каждого зашифрованного блока текста. Таким образом, поставляемый ключ становится «основным ключом». Поэтому этот класс обеспечивает ключевое вращение для ключей шифрования и авторизации.
ВАЖНОЕ ПРИМЕЧАНИЕ. Параметр $rounds
настроен для истинных случайных ключей достаточной силы (128 бит криптографически безопасного случайного как минимум). Если вы собираетесь использовать пароль или неслучайный ключ (или менее случайные, а затем 128 бит CS random), вы должны увеличить этот параметр. Я бы предложил минимум 10000 для паролей (чем больше вы можете позволить себе, тем лучше, но это добавит во время выполнения) …
Целостность данных
Шифрование:
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-символьного источника данных будет зашифровано 16 символов данных. Таким образом, фактический размер зашифрованных данных составляет 16 байтов из-за заполнения. Затем добавьте 16 байт для соли и 64 байта для hmac, а общий размер хранится в 96 байт. Таким образом, в лучшем случае на 80 персонажей накладные расходы, а в худшем – 87 символов накладных расходов …
Надеюсь, это поможет…
Примечание: 12/11/12: Я только что обновил этот класс с помощью лучшего метода шифрования MUCH, используя лучшие производные ключи и исправляя генерацию MAC …
Как шифровать и расшифровывать пароль в PHP? Используя один из многих алгоритмов шифрования. (или используя одну из многих библиотек)
Какой самый безопасный алгоритм шифрования паролей? Существует множество различных алгоритмов, ни одна из которых не защищена на 100%. Но многие из них достаточно безопасны для торговли и даже военных целей
Где я храню закрытый ключ? Если вы решили реализовать алгоритм шифрования с открытым ключом (например, RSA), вы не храните закрытый ключ. пользователь имеет закрытый ключ. ваша система имеет открытый ключ, который можно хранить в любом месте.
Вместо того, чтобы хранить закрытый ключ, рекомендуется ли пользователям вводить закрытый ключ в любое время, когда им нужен пароль, расшифрованный? (Пользователям этого приложения можно доверять) Хорошо, если ваш пользователь может запомнить смехотворно длинные простые числа, тогда – да, почему бы и нет. Но обычно вам нужно придумать систему, которая позволит пользователю хранить свой ключ где-то.
Каким образом пароль может быть украден и дешифрован? Что мне нужно знать? Это зависит от используемого алгоритма. Однако всегда убедитесь, что вы не отправляете пароль, не зашифрованный для пользователя или от него. Либо шифруйте / расшифруйте его на стороне клиента, либо используйте https (или другие криптографические средства для защиты соединения между сервером и клиентом).
Однако, если вам нужно хранить пароли в зашифрованном виде, я бы предложил вам использовать простой XOR-шифр. Основная проблема с этим алгоритмом заключается в том, что он может быть легко разбит на частотный анализ. Однако, как правило, пароли не составлены из длинных абзацев английского текста, я не думаю, что вам следует беспокоиться об этом. Вторая проблема с XOR Cipher заключается в том, что если у вас есть сообщение в зашифрованном и расшифрованном виде, вы можете легко узнать пароль, с которым он был зашифрован. Опять же, не большая проблема в вашем случае, так как она влияет только на пользователя, который уже был скомпрометирован другими способами.
Пример из руководства слегка изменен для этого примера):
<?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 для дешифрования своего пароля.
Лучший алгоритм довольно субъективен – попросите 5 человек, получите 5 ответов. Лично, если по умолчанию (Blowfish) недостаточно для вас, у вас, вероятно, больше проблем!
Учитывая, что PHP необходим для шифрования – не уверен, что вы можете спрятать его в любом месте – приветствуем комментарии по этому поводу. Разумеется, стандартные методы кодирования PHP лучше всего!
Учитывая, что ключ шифрования будет в вашем коде в любом случае, не уверен, что вы выиграете, предоставляя остальную часть вашего приложения в безопасности.
Очевидно, что если зашифрованный пароль и ключ шифрования украдены, тогда игра закончится.
Я отвечу всадника на мой ответ – я не эксперт по криптографии PHP, но, я думаю, что я ответил, это стандартная практика – я приветствую комментарии, которые могут иметь другие.
Многие пользователи предложили использовать 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);
Я бы предложил только шифрование с открытым ключом, если вы хотите установить пароль пользователя без их взаимодействия (это может быть удобно для сброса и общих паролей).
openssl_public_encrypt
и openssl_private_decrypt
4
. Да – пользователи должны каждый раз вводить пароль своего приложения, но сохранение его в сеансе приведет к возникновению других проблем
5
.
Я пробовал что-то подобное, но, пожалуйста, обратите внимание, что я не криптограф, и я не владею глубокими знаниями о 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.
Обратите внимание, что это всего лишь концепция. Любое улучшение этого кода было бы весьма значительным.
Пароли для аппаратного устройства, поэтому проверка на хеши не может быть и речи
А? Я не понимаю. Вы просто имеете в виду, что пароль должен быть восстановлен?
Как утверждают другие, расширение mcrypt обеспечивает доступ к множеству криптографических функций, однако вы предлагаете своим пользователям поместить все свои яйца в одну корзину – та, которая потенциально станет мишенью для злоумышленников, – и если вы даже не знаете как начать решение проблемы, тогда вы делаете своих пользователей плохим сервисом. Вы не можете понять, как защитить данные.
Большинство уязвимостей безопасности возникают не потому, что основной алгоритм является ошибочным или небезопасным, но из-за проблем с тем, как алгоритм используется в коде приложения.
Сказав это, можно построить достаточно безопасную систему.
Вы должны учитывать асимметричное шифрование, если у вас есть требование для пользователя создать безопасное сообщение, которое читается другим (конкретным) пользователем. Причина в том, что его вычислительно дорого. Если вы просто хотите предоставить репозиторий пользователям для ввода и извлечения своих собственных данных, симметричное шифрование является адекватным.
Если, однако, вы храните ключ для дешифрования сообщения в том же месте, что и зашифрованное сообщение (или там, где хранится зашифрованное сообщение), тогда система небезопасна. Используйте тот же токен для аутентификации пользователя, что и для ключа дешифрования (или в случае ассиметричного шифрования, используйте токен в качестве фразы пароля закрытого ключа). Поскольку вам нужно будет хранить токен на сервере, где дешифрование происходит, по крайней мере, временно, вы можете захотеть использовать подложку для хранения без поиска, или передать токен непосредственно демонам, связанным с сеансом, который будет хранить токена в памяти и выполнять дешифрование сообщений по требованию.
Использовать 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.'; } ?>