На основании того, что я вижу в вопросе, ответах и комментариях; Я бы посоветовал воспользоваться OpenSSL. Предполагается, что вашему сайту требуется периодический доступ к этой информации (то есть это может быть запланировано). Как вы заявили:
Эта информация потребуется серверу для отправки платежей в самых разных ситуациях. Для этого не требуется, чтобы «владелец» указанных ключей входил в систему, на самом деле владелец никогда не захочет увидеть их снова, как только они предоставят их в первый раз.
Именно из этого комментария и предположения, что доступ к данным, которые вы хотите сохранить, можно поместить в задание cron. Кроме того, предполагается, что у вас есть SSL (https) на вашем сервере, поскольку вы будете иметь дело с конфиденциальной информацией пользователя, и у вас есть модули OpenSSL
и mcrypt
. Кроме того, нижеследующее будет довольно общим относительно того, «как» это может быть достигнуто, но на самом деле это не детали того, как это сделать в вашей ситуации. Также следует отметить, что это «практическое руководство» является общим, и вам следует провести дополнительные исследования, прежде чем применять его. При этом, давайте начнем.
Во-первых, давайте поговорим о том, что предоставляет OpenSSL. OpenSSL дает нам шифрование с открытым ключом: возможность шифровать данные с помощью открытого ключа (который, в случае компрометации не поставит под угрозу безопасность данных, зашифрованных с его помощью.) Во-вторых, он обеспечивает способ доступа к этой информации с помощью «закрытого ключа». Поскольку мы не заботимся о создании сертификата (нам нужны только ключи шифрования), их можно получить с помощью простой функции (которую вы будете использовать только один раз):
function makeKeyPair()
{
//Define variables that will be used, set to ''
$private = '';
$public = '';
//Generate the resource for the keys
$resource = openssl_pkey_new();
//get the private key
openssl_pkey_export($resource, $private);
//get the public key
$public = openssl_pkey_get_details($resource);
$public = $public["key"];
$ret = array('privateKey' => $private, 'publicKey' => $public);
return $ret;
}
Теперь у вас есть открытый и закрытый ключ. Защитите закрытый ключ, храните его на сервере и не допускайте попадания в базу данных. Храните его на другом сервере, компьютере, который может запускать задания cron и т. Д. Просто нигде рядом с общественностью, если только вы не можете требовать, чтобы администратор присутствовал каждый раз, когда вам требуется обработка платежа и зашифровать закрытый ключ с помощью шифрования AES или чего-то еще. похожий. Однако открытый ключ будет жестко запрограммирован в вашем приложении и будет использоваться каждый раз, когда пользователь вводит свою информацию для сохранения.
Затем вам нужно определить, как вы планируете проверять расшифрованные данные (чтобы вы не начинали отправлять в платежные API недопустимые запросы). Я предполагаю, что есть несколько полей, которые необходимо сохранить, и поскольку мы только хотим для однократного шифрования он будет в массиве PHP, который можно сериализовать d. В зависимости от того, сколько данных необходимо сохранить, мы сможем либо зашифровать их напрямую, либо сгенерировать пароль для шифрования с помощью открытого ключа и использовать этот случайный пароль для шифрования самих данных. Я собираюсь пойти этим путем в объяснении. Чтобы пойти по этому пути, мы будем использовать шифрование AES и должны иметь под рукой функцию шифрования и дешифрования, а также способ случайным образом сгенерировать приличный одноразовый блокнот для данных. Я предоставлю генератор паролей, который я использую, хотя я перенес его из кода, который написал недавно, он будет служить цели, или вы можете написать лучший. ^^
public function generatePassword() {
//create a random password here
$chars = array( 'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H', 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P', 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X', 'y', 'Y', 'z', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '?', '<', '>', '.', ',', ';', '-', '@', '!', '#', '$', '%', '^', '&', '*', '(', ')');
$max_chars = count($chars) - 1;
srand( (double) microtime()*1000000);
$rand_str = '';
for($i = 0; $i < 30; $i++)
{
$rand_str .= $chars[rand(0, $max_chars)];
}
return $rand_str;
}
Эта конкретная функция будет генерировать 30 цифр, что обеспечивает приличную энтропию, но вы можете изменить ее для своих нужд. Затем функция для шифрования AES:
/**
* Encrypt AES
*
* Will Encrypt data with a password in AES compliant encryption. It
* adds built in verification of the data so that the {@link this::decryptAES}
* can verify that the decrypted data is correct.
*
* @param String $data This can either be string or binary input from a file
* @param String $pass The Password to use while encrypting the data
* @return String The encrypted data in concatenated base64 form.
*/
public function encryptAES($data, $pass) {
//First, let's change the pass into a 256bit key value so we get 256bit encryption
$pass = hash('SHA256', $pass, true);
//Randomness is good since the Initialization Vector(IV) will need it
srand();
//Create the IV (CBC mode is the most secure we get)
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
//Create a base64 version of the IV and remove the padding
$base64IV = rtrim(base64_encode($iv), '=');
//Create our integrity check hash
$dataHash = md5($data);
//Encrypt the data with AES 128 bit (include the hash at the end of the data for the integrity check later)
$rawEnc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $pass, $data . $dataHash, MCRYPT_MODE_CBC, $iv);
//Transfer the encrypted data from binary form using base64
$baseEnc = base64_encode($rawEnc);
//attach the IV to the front of the encrypted data (concatenated IV)
$ret = $base64IV . $baseEnc;
return $ret;
}
(Изначально я написал эти функции как часть класса и предлагаю вам реализовать их в собственном классе.) Кроме того, использование этой функции подходит для одноразового блокнота, который создается, однако, если он используется с пароль для конкретного пользователя для другого приложения, вам определенно понадобится немного соли, чтобы добавить к паролю. Затем, чтобы расшифровать и проверить правильность расшифрованных данных:
/**
* Decrypt AES
*
* Decrypts data previously encrypted WITH THIS CLASS, and checks the
* integrity of that data before returning it to the programmer.
*
* @param String $data The encrypted data we will work with
* @param String $pass The password used for decryption
* @return String|Boolean False if the integrity check doesn't pass, or the raw decrypted data.
*/
public function decryptAES($data, $pass){
//We used a 256bit key to encrypt, recreate the key now
$pass = hash('SHA256', $this->salt . $pass, true);
//We should have a concatenated data, IV in the front - get it now
//NOTE the IV base64 should ALWAYS be 22 characters in length.
$base64IV = substr($data, 0, 22) .'=='; //add padding in case PHP changes at some point to require it
//change the IV back to binary form
$iv = base64_decode($base64IV);
//Remove the IV from the data
$data = substr($data, 22);
//now convert the data back to binary form
$data = base64_decode($data);
//Now we can decrypt the data
$decData = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $pass, $data, MCRYPT_MODE_CBC, $iv);
//Now we trim off the padding at the end that php added
$decData = rtrim($decData, "\0");
//Get the md5 hash we stored at the end
$dataHash = substr($decData, -32);
//Remove the hash from the data
$decData = substr($decData, 0, -32);
//Integrity check, return false if it doesn't pass
if($dataHash != md5($decData)) {
return false;
} else {
//Passed the integrity check, give use their data
return $decData;
}
}
Посмотрите на обе функции, прочтите комментарии и т. Д. Выясните, что они делают и как работают, чтобы вы не реализовали их неправильно. Теперь перейдем к шифрованию пользовательских данных. Мы зашифруем его с помощью открытого ключа, и следующие функции предполагают, что все функции до сих пор (и в будущем) находятся в одном классе. Я предоставлю обе функции шифрования / дешифрования OpenSSL сразу, вторая нам понадобится позже.
/**
* Public Encryption
*
* Will encrypt data based on the public key
*
* @param String $data The data to encrypt
* @param String $publicKey The public key to use
* @return String The Encrypted data in base64 coding
*/
public function publicEncrypt($data, $publicKey) {
//Set up the variable to get the encrypted data
$encData = '';
openssl_public_encrypt($data, $encData, $publicKey);
//base64 code the encrypted data
$encData = base64_encode($encData);
//return it
return $encData;
}
/**
* Private Decryption
*
* Decrypt data that was encrypted with the assigned private
* key's public key match. (You can't decrypt something with
* a private key if it doesn't match the public key used.)
*
* @param String $data The data to decrypt (in base64 format)
* @param String $privateKey The private key to decrypt with.
* @return String The raw decoded data
*/
public function privateDecrypt($data, $privateKey) {
//Set up the variable to catch the decoded date
$decData = '';
//Remove the base64 encoding on the inputted data
$data = base64_decode($data);
//decrypt it
openssl_private_decrypt($data, $decData, $privateKey);
//return the decrypted data
return $decData;
}
$data
в них всегда будет одноразовым блокнотом, а не информацией о пользователе. Затем функции по объединению как шифрования с открытым ключом, так и AES одноразового блокнота для шифрования и дешифрования.
/**
* Secure Send
*
* OpenSSL and 'public-key' schemes are good for sending
* encrypted messages to someone that can then use their
* private key to decrypt it. However, for large amounts
* of data, this method is incredibly slow (and limited).
* This function will take the public key to encrypt the data
* to, and using that key will encrypt a one-time-use randomly
* generated password. That one-time password will be
* used to encrypt the data that is provided. So the data
* will be encrypted with a one-time password that only
* the owner of the private key will be able to uncover.
* This method will return a base64encoded serialized array
* so that it can easily be stored, and all parts are there
* without modification for the receive function
*
* @param String $data The data to encrypt
* @param String $publicKey The public key to use
* @return String serialized array of 'password' and 'data'
*/
public function secureSend($data, $publicKey)
{
//First, we'll create a 30digit random password
$pass = $this->generatePassword();
//Now, we will encrypt in AES the data
$encData = $this->encryptAES($data, $pass);
//Now we will encrypt the password with the public key
$pass = $this->publicEncrypt($pass, $publicKey);
//set up the return array
$ret = array('password' => $pass, 'data' => $encData);
//serialize the array and then base64 encode it
$ret = serialize($ret);
$ret = base64_encode($ret);
//send it on its way
return $ret;
}
/**
* Secure Receive
*
* This is the complement of {@link this::secureSend}.
* Pass the data that was returned from secureSend, and it
* will dismantle it, and then decrypt it based on the
* private key provided.
*
* @param String $data the base64 serialized array
* @param String $privateKey The private key to use
* @return String the decoded data.
*/
public function secureReceive($data, $privateKey) {
//Let's decode the base64 data
$data = base64_decode($data);
//Now let's put it into array format
$data = unserialize($data);
//assign variables for the different parts
$pass = $data['password'];
$data = $data['data'];
//Now we'll get the AES password by decrypting via OpenSSL
$pass = $this->privateDecrypt($pass, $privateKey);
//and now decrypt the data with the password we found
$data = $this->decryptAES($data, $pass);
//return the data
return $data;
}
Я оставил комментарии нетронутыми, чтобы облегчить понимание этих функций. А теперь перейдем к самой интересной части, собственно, к работе с данными пользователей. $data
в методе send
- это данные пользователя в сериализованном массиве. Помните, что для метода отправки $publicKey
жестко запрограммирован, вы можете сохранить его как переменную в своем классе и получить к нему доступ таким образом, чтобы меньше переменных передавалось в него, или чтобы он вводился из другого места для отправки в метод каждый раз. Пример использования для шифрования данных:
$myCrypt = new encryptClass();
$userData = array(
'id' => $_POST['id'],
'password' => $_POST['pass'],
'api' => $_POST['api_key']
);
$publicKey = "the public key from earlier";
$encData = $myCrypt->secureSend(serialize($userData), $publicKey));
//Now store the $encData in the DB with a way to associate with the user
//it is base64 encoded, so it is safe for DB input.
Теперь это простая часть, следующая часть - возможность использовать эти данные. Для этого вам понадобится страница на вашем сервере, которая принимает $_POST['privKey']
, а затем будет перебирать пользователей и т. Д. Способом, необходимым для вашего сайта, захватывая $encData
. Пример использования для расшифровки этого:
$myCrypt = new encryptClass();
$encData = "pulled from DB";
$privKey = $_POST['privKey'];
$data = unserialize($myCrypt->secureReceive($encData, $privKey));
//$data will now contain the original array of data, or false if
//it failed to decrypt it. Now do what you need with it.
Затем конкретная теория использования для доступа к этой защищенной странице с помощью закрытого ключа. На отдельном сервере у вас будет задание cron, которое запускает скрипт php, в частности, не в public_html
, содержащий закрытый ключ, а затем используйте _ 18_, чтобы опубликовать закрытый ключ на вашей странице, которая его ищет. (Убедитесь, что вы звоните по адресу, который начинается с https)
Я надеюсь, что это поможет ответить на вопрос, как можно безопасно хранить пользовательскую информацию в вашем приложении, которая не будет скомпрометирована при доступе ни к вашему коду, ни к вашей базе данных.
person
Jon
schedule
11.03.2013
OAuth provides a method for users to grant third-party access to their resources without sharing their passwords. It also provides a way to grant limited access (in scope, duration, etc.).
Для описанной ситуации (например, для обработки платежей) серверу требуется неограниченный доступ к этим API в любое время по разным причинам. Я не вижу, как это могло бы улучшить ситуацию - общий пароль не обязательно является проблемой. - person Shackrock   schedule 09.03.2013