For security purpose I don't want that my users can read a url to access to a picture for example.
Actually I have
https://files.domain.com/TERFD/TES/photos/20150729-0961577ba8bc6c31e7339acf0c53969a170609038345c3a0602d646a48067c10-ANnKb.jpeg?uid=3&token=360d641dc692041cbea673a
But I prefer that the user can read the picture's path or the token
So that, I wrote the following functions :
public function encrypt($data) {
$key = "df456gfd";
$data = serialize($data);
$td = mcrypt_module_open(MCRYPT_DES,"",MCRYPT_MODE_ECB,"");
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td,$key,$iv);
$data = base64_encode(mcrypt_generic($td, '!'.$data));
mcrypt_generic_deinit($td);
return $data;
}
public function decrypt($data) {
$key = "df456gfd";
$td = mcrypt_module_open(MCRYPT_DES,"",MCRYPT_MODE_ECB,"");
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td,$key,$iv);
$data = mdecrypt_generic($td, base64_decode($data));
mcrypt_generic_deinit($td);
if (substr($data,0,1) != '!')
return false;
$data = substr($data,1,strlen($data)-1);
return unserialize($data);
}
My URL is now:
https://files.domain.com/NcGDHiMnmM3fYW2W03ulyJdlCp6FaXCYDlxzWe74zH63+YpfUSPuKnxWIG1C1WNNjw/jU7coduYchvf44Lh4yiVdcL7uyx4vA4oOj14keiohQ9geIYVxsa4n07E0TXbstSETbhqGejE03Ai5hGcJEa7U/aA7z1fRkQEAxepH9j6yu+tQZESp3dXg7JUvVffI9lbpPtbGLj8=
I can decrypt it from files.domain.com and return the requested file.
Is it a good practice ?
Thx
You're actually just slowing down your application with this practice. Honestly, you're making it too complex. Why not use something like base64_encode() and base64_decode(). These functions are actually faster than your decryption and encryption functions.
Related
With the introduction of GDPR it is advisable to start encrypting sensitive data entered in your database. I intended to equip myself with a couple of experiences
The doubt is fast enough, changing servers or updating the php to a longer version, the algorithms are copied by php.net and well referenced by many users should they be obsolete and therefore no longer supported? I would find myself with a lot of data on a database without being able to do anything about it.
I'm currently using the next code.
When I use the secured_encrypt function several times, I get different results every time. How is it possible?
Thank you!
<?php
public function secured_encrypt($data){
$first_key = base64_decode($this -> FIRSTKEY);
$second_key = base64_decode($this -> SECONDKEY);
$method = "aes-256-cbc";
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
$first_encrypted = openssl_encrypt($data, $method,$first_key, OPENSSL_RAW_DATA ,$iv);
$second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
$output = base64_encode($iv.$second_encrypted.$first_encrypted);
return $output;
}
public function secured_decrypt($input){
$first_key = base64_decode($this -> FIRSTKEY);
$second_key = base64_decode($this -> SECONDKEY);
$mix = base64_decode($input);
$method = "aes-256-cbc";
$iv_length = openssl_cipher_iv_length($method);
$iv = substr($mix,0,$iv_length);
$second_encrypted = substr($mix,$iv_length,64);
$first_encrypted = substr($mix,$iv_length+64);
$data = openssl_decrypt($first_encrypted,$method, $first_key,OPENSSL_RAW_DATA, $iv);
$second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
if (hash_equals($second_encrypted,$second_encrypted_new))
return $data;
return false;
}
?>
You're setting the initialization vector with openssl_random_pseudo_bytes
I want to pass some variable to the URL using PHP $_GET['']; for example:
I have a form on a landing page with five input fields: Name, Surname, Email, Confirm-Email, Phone I pick these variables up now I want to add these to a Base URL so that they can be picked up by another web page.
My question is how safe is this and what is the best method to protect these variables or perhaps make them invisible under the new url...?
I could use php curl() or sockets but the server where I want to send the data to does not allow me to so thats why I want to use $_GET['']'
You should not use $_GET if you want to make your data secure.
There is no option using which you can make $_GET secure or invisible.
You should use $_POST for sending data to other url if you do not want to make your data visible and secure.
Even your data is not stored by browser when you use post method and it is more difficult to hack.
It's unsafe, as URI parameters can be edited by anyone and anything. Best method to "protect" those is to encrypt them when sending (which means the recipient must be able to decrypt them) combined with a checksum to make sure none of the parameters were altered. Another type of protection is not using HTTP but using HTTPS instead, of course.
Both GET and POST is unsafe if you're not using HTTPS.
So if you have to use GET to submit your form, a verify token is suggeseted. Like OAuth, server will return the data through GET, but there's a access_token to protect data.
Encode your variables first, then decode it on the other server. this way no one can easily revert it. Make sure to change var $skey = "SecretKey0001"; to something else.
<?php
class Encryption {
var $skey = "SecretKey0001"; // you can change it
public function safe_b64encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+','/','='),array('-','_',''),$data);
return $data;
}
public function safe_b64decode($string) {
$data = str_replace(array('-','_'),array('+','/'),$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
public function encode($value){
if(!$value){return false;}
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->skey, $text, MCRYPT_MODE_ECB, $iv);
return trim($this->safe_b64encode($crypttext));
}
public function decode($value){
if(!$value){return false;}
$crypttext = $this->safe_b64decode($value);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->skey, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
}
$data_array['Name'] = 'Name';
$data_array['Surname'] = 'Surname';
$data_array['Email'] = 'Email';
$data_array['Confirm-Email'] = 'Confirm-Email';
$data_array['Phone'] = 'Phone';
$data_json = json_encode($data_array);
$encrypt = new Encryption;
$encoded_vars = $encrypt->encode($data_json);
$BASE_URL = 'http://example.com?data=' . $encoded_vars;
echo $BASE_URL;
echo "<br>";
// reverse
$decrypt = new Encryption;
echo $decoded_vars = $decrypt->decode($encoded_vars);
echo "<br>";
$data = json_decode(urldecode($decoded_vars), true);
echo "<br>";
print_r($data);
?>
DEMO: http://sandbox.onlinephpfunctions.com/code/ef1acdcad0272d5d99e21b07183a479f564ac64c
I'm using PHP and CI framework, I want to send variable through URL but I want it to be somewhat encrypted.
For example i want to send variable named id:
www.trythis.com/site?id=123
I want it to be
www.trythis.com/site?id=VkxSiOW31S
The encrypted text is just an example.
How can I do that? or is there CI function that can do this?
Note: I already try base64_encode but it somehow can't be used in CI due to its special characters such as ==.
CodeIgniter has Encryption Class. You can use that class to encrypt and decrypt. To configure follow the steps
Setting your Key
$config['encryption_key'] = "YOUR KEY";
Initializing the Class
$this->load->library('encrypt');
To encode use
$id = '123';
$encrypted_id = $this->encrypt->encode($id);
//$url = 'www.trythis.com/site?id=' . $encrypted_id;
And to decode
//$encrypted_id = $_GET['id'];
$decrypted_id = $this->encrypt->decode($encrypted_id);
I made this class for the same purpose:
class Cypher
{
public $encrypted;
public $decrypted;
public function __construct($_value) {
$this->encrypted = $this->encrypting($_value);
$this->decrypted = $this->decrypting($_value);
}
private function encrypting($_value) {
$key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$utf8 = utf8_encode($_value);
$cipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $utf8, MCRYPT_MODE_CBC, $iv);
$cipher = $iv.$cipher;
$base64 = base64_encode($_value);
return $base64;
}
private function decrypting($_value) {
return base64_decode($_value);
}
}
Let's try your example:
$cypher = new Cypher('123');
header("location: www.trythis.com/site?id=".$cypher -> encrypted);
On www.trythis.com/site page:
$id = isset($_GET['id']) ? new Cypher($_GET['id']) : "";
echo $id -> decrypted;
I found out that I should not use global variables like global $auth_key for sensitive data's (Correct me if that's not true.) so I wanted to use defined variables for storing security keys.
Inside config.php salt keys are defined.
define('AUTH_KEY','::~K~UC*[tlu4Eq/]Lm|h');
define('SECURE_AUTH_KEY', 'QsTMvbV+tuU{K26!]J2');
In encryption.php contains the encryption functions where AUTH_KEY and SECURE_AUTH_KEY will be used inside.
function encrypt_text($value) {
if(!$value) return false;
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, **AUTH_KEY_HERE**, $value, MCRYPT_MODE_ECB, **SECURE_AUTH_KEY_HERE**);
return trim(base64_encode($crypttext));
}
function decrypt_text($value) {
if(!$value) return false;
$crypttext = base64_decode($value);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, **AUTH_KEY_HERE**, $crypttext, MCRYPT_MODE_ECB, **SECURE_AUTH_KEY_HERE**);
return trim($decrypttext);
}
Is there a way to do that? or any other solutions you can recommend? Please note that these keys are real important for encryption of sensitive informations.
Also, a another question, what is the maximum length of keys to be used on mcrypt?
Thank you and looking forward for reply of yours.
as a rule: the logner the key, the stonger the encryption. Secondly, don't use ECB unless your data is very short, you ought to use CBC or something stronger. Third: use a salt or initialization vector. Lastly read this: https://www.owasp.org/index.php/Cryptographic_Storage_Cheat_Sheet
Using a constant is just like using a variable except there is no dollar sign.
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, AUTH_KEY, $value, MCRYPT_MODE_ECB, SECURE_AUTH);
There is nothing inherently more secure in this approach over using the global key word. Though this approach is preferred. By using a constant you are saying this is a static value I will use across the application. Having to use global on the other hand is often just a result of bad design or laziness. It leads to code that is hard to follow, abusing what scoping tries to accomplish.
Key length is dependent on the encryption algorithm used. RTM.
Yes you can use the define variable like you are doing, see the example
define('AUTH_KEY','::~K~UC*[tlu4Eq/]Lm|h');
function abc()
{
echo AUTH_KEY;
}
abc(); // ::~K~UC*[tlu4Eq/]Lm|h
http://codepad.viper-7.com/tUAg6D
Although choosing constants would be preferable over plain variables, this kind of information is better stored inside a configuration file rather than your code.
Also, for better reuse and avoid having those global values lying around it would be a better idea to encapsulate the functionality:
class MyCrypto
{
private $key;
private $cipher;
private $mode;
public function __construct($key, $cipher, $mode = "cbc")
{
$this->key = $key;
$this->cipher = $cipher;
$this->mode = $mode;
}
public function generate_salt()
{
return mcrypt_create_iv(
mcrypt_get_iv_size($this->cipher, $this->mode),
MCRYPT_DEV_URANDOM
);
}
public function encrypt($data) { ... }
public function decrypt($data) { ... }
}
I've added a salt generator function to be used for every encryption operation;
Lastly, I would recommend using CBC mode - MCRYPT_MODE_CBC.
Update (27/09/17):
Since mcrypt_encrypt is DEPRECATED as of PHP 7.1.0. Ive added a simple encrypt/decrypt using openssl.
function encrypt($string, $key = 'PrivateKey', $secret = 'SecretKey', $method = 'AES-256-CBC') {
// hash
$key = hash('sha256', $key);
// create iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret), 0, 16);
// encrypt
$output = openssl_encrypt($string, $method, $key, 0, $iv);
// encode
return base64_encode($output);
}
function decrypt($string, $key = 'PrivateKey', $secret = 'SecretKey', $method = 'AES-256-CBC') {
// hash
$key = hash('sha256', $key);
// create iv - encrypt method AES-256-CBC expects 16 bytes
$iv = substr(hash('sha256', $secret), 0, 16);
// decode
$string = base64_decode($string);
// decrypt
return openssl_decrypt($string, $method, $key, 0, $iv);
}
$str = 'Encrypt this text';
echo "Plain: " .$str. "\n";
// encrypt
$encrypted_str = encrypt($str);
echo "Encrypted: " .$encrypted_str. "\n";
// decrypt
$decrypted_str = decrypt($encrypted_str);
echo "Decrypted: " .$decrypted_str. "\n";
In your example, you are using the same initialization vector **SECURE_AUTH_KEY_HERE** when you can allow PHP to create the iv for you this way you only need 1 SECURE_KEY defined.
<?php
define('SECURE_KEY',md5('your secret key'));
/**
* Encrypt a value
*/
function encrypt($str){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, SECURE_KEY, $str, MCRYPT_MODE_ECB, $iv);
}
/**
* Decrypt a value
*/
function decrypt($str){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, SECURE_KEY, $str, MCRYPT_MODE_ECB, $iv));
}
//32
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
//Create an initialization vector (IV) from a random source
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
echo decrypt(encrypt('Encrypt me'));
?>
Trying to write a couple of functions that will encrypt or decrypt a file and am using the class found here to try and accomplish this:
http://www.itnewb.com/v/PHP-Encryption-Decryption-Using-the-MCrypt-Library-libmcrypt
The encryption function below seems to work, in that it appears to encrypt the file and place it in the intended directory. I'm trying to decrypt the file now, and it just dies with the message "Failed to complete decryption" (which is coded in there...) There's nothing in the php error logs, so I'm not sure why it's failing, but as mcrypt is entirely new to me, I'm more than inclined to believe I'm doing something wrong here...
Here are the functions:
//ENCRYPT FILE
function encryptFile() {
global $cryptastic;
$pass = PGPPASS;
$salt = PGPSALT;
$key = $cryptastic->pbkdf2($pass, $salt, 1000, 32) or die("Failed to generate secret key.");
if ($handle = opendir(PATH.'/ftpd')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$newfile = PATH.'/encrypted/'.$file.'.txt';
$msg = file_get_contents(PATH.'/ftpd/'.$file);
$encrypted = $cryptastic->encrypt($msg, $key) or die("Failed to complete encryption.");
$nfile = fopen($newfile, 'w');
fwrite($nfile, $encrypted);
fclose($nfile);
unlink(PATH.'/ftpd/'.$file);
}
}
closedir($handle);
}
//DECRYPT FILE
function inFTP() {
global $cryptastic;
$pass = PGPPASS;
$salt = PGPSALT;
$key = $cryptastic->pbkdf2($pass, $salt, 1000, 32) or die("Failed to generate secret key.");
if ($handle = opendir(PATH.'/encrypted')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$newfile = PATH.'/decrypted/'.$file;
$msg = PATH.'/encrypted/'.$file;
$decrypted = $cryptastic->decrypt($msg, $key) or die("Failed to complete decryption.");
$nfile = fopen($newfile, 'w');
fwrite($nfile, $decrypted);
fclose($nfile);
//unlink(PATH.'/encrypted/'.$file);
}
}
closedir($handle);
}
//$crypt->decrypt($file);
}
Since mcrypt is abandonware and no longer recommended to be used, here's an example using openssl.
class AES256Encryption
{
public const BLOCK_SIZE = 8;
public const IV_LENGTH = 16;
public const CIPHER = 'AES256';
public static function generateIv(bool $allowLessSecure = false): string
{
$success = false;
$random = openssl_random_pseudo_bytes(openssl_cipher_iv_length(static::CIPHER));
if (!$success) {
if (function_exists('sodium_randombytes_random16')) {
$random = sodium_randombytes_random16();
} else {
try {
$random = random_bytes(static::IV_LENGTH);
}
catch (Exception $e) {
if ($allowLessSecure) {
$permitted_chars = implode(
'',
array_merge(
range('A', 'z'),
range(0, 9),
str_split('~!##$%&*()-=+{};:"<>,.?/\'')
)
);
$random = '';
for ($i = 0; $i < static::IV_LENGTH; $i++) {
$random .= $permitted_chars[mt_rand(0, (static::IV_LENGTH) - 1)];
}
}
else {
throw new RuntimeException('Unable to generate initialization vector (IV)');
}
}
}
}
return $random;
}
protected static function getPaddedText(string $plainText): string
{
$stringLength = strlen($plainText);
if ($stringLength % static::BLOCK_SIZE) {
$plainText = str_pad($plainText, $stringLength + static::BLOCK_SIZE - $stringLength % static::BLOCK_SIZE, "\0");
}
return $plainText;
}
public static function encrypt(string $plainText, string $key, string $iv): string
{
$plainText = static::getPaddedText($plainText);
return base64_encode(openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
}
public static function decrypt(string $encryptedText, string $key, string $iv): string
{
return openssl_decrypt(base64_decode($encryptedText), static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
}
$text = '8SViI0Gz4r-p7A15YxkwjOBFuW*#NTtbm{U]D&E=~6yLM+adX'P;h3$,KJ%/eo>}<Rs:2#gZ.9fqn"Cv_^[(H\c!)?`Ql';
$key = 'secretkey';
$iv = AES256Encryption::generateIv();
$encryptedText = AES256Encryption::encrypt($text, $key, $iv);
$decryptedText = AES256Encryption::decrypt($encryptedText, $key, $iv);
printf('Original Text: %s%s', $text, PHP_EOL);
printf('Encrypted: %s%s', $encryptedText, PHP_EOL);
printf('Decrypted: %s%s', $decryptedText, PHP_EOL);
Output:
// Long string with lots of different characters
Original Text: 8SViI0Gz4r-p7A15YxkwjOBFuW*#NTtbm{U]D&E=~6yLM+adX'P;h3$,KJ%/eo>}<Rs:2#gZ.9fqn"Cv_^[(H\c!)?`Ql
Encrypted : rsiF4PMCMyvAp+CTuJrxJYGoV4BSy8Fy+q+FL8m64+Mt5V3o0HS0elRkWXsy+//hPjzNhjmVktxVvMY55Negt4DyLcf2QpH05wUX+adJDe634J/9fWd+nlEFoDutXuhY+/Kep9zUZFDmLmszJaBHWQ==
Decrypted : 8SViI0Gz4r-p7A15YxkwjOBFuW*#NTtbm{U]D&E=~6yLM+adX'P;h3$,KJ%/eo>}<Rs:2#gZ.9fqn"Cv_^[(H\c!)?`Ql
Old Answer
Try this PHP5 class for encryption using mcrypt. In this case it's using AES encryption. You'll want to change the key for each site you use it on. If you don't use it at least it may guide you on writing your own version of it.
<?php
class Encryption
{
const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
/* Cryptographic key of length 16, 24 or 32. NOT a password! */
private $key;
public function __construct($key) {
$this->key = $key;
}
public function encrypt($plaintext) {
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
return base64_encode($iv.$ciphertext);
}
public function decrypt($ciphertext) {
$ciphertext = base64_decode($ciphertext);
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
if (strlen($ciphertext) < $ivSize) {
throw new Exception('Missing initialization vector');
}
$iv = substr($ciphertext, 0, $ivSize);
$ciphertext = substr($ciphertext, $ivSize);
$plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
return rtrim($plaintext, "\0");
}
}
Usage:
$key = /* CRYPTOGRAPHIC!!! key */;
$crypt = new Encryption($key);
$encrypted_string = $crypt->encrypt('this is a test');
$decrypted_string = $crypt->decrypt($encrypted_string); // this is a test
Notes:
This class is not safe for use with binary data (which may end in NUL bytes)
This class does not provide authenticated encryption.
While Johns answer is good, using base64 encoding just to fix the binary safety issue is overkill and will make your encrypted files 33% larger than the original. Here is my PHP Implementation of the AES Crypt file format which solves all the above issues transparently.
https://github.com/philios33/PHP-AES-File-Encryption
It is binary safe and includes authenticated encryption. Since it uses the open source aes crypt file format (.aes) it is fully compatible with other .aes software.
https://www.aescrypt.com/
The interface is pretty simple whether you are encrypting or decrypting. You just give it a source file and password.
You should not be using Mcrypt to encrypt/decrypt data. As shown in your question, and in the accepted answer, the data is not authenticated, which means it will fall victim to chosen ciphertext attacks.
Further, a great deal of effort has been done to make sure that developers put together cryptographic primitives correctly. As such, instead of Mcrypt, you should be using libsodium for your PHP projects. libsodium is a fork of NaCl. NaCl/libsodium is written to remove a lot of the cryptographic pitfalls that developers find themselves in, such as timing attacks with verification of MAC tags.
Mcrypt is deprecated in PHP 7.1, and libsodim is the preferred way to handle cryptography in PHP.
Using libsodium in your PHP project is easy, and secure. Scott Arciszewski has written an extensive ebook on using libsodium with PHP at https://paragonie.com/book/pecl-libsodium. It's worth the read for anyone doing PHP cryptography.
CakePHP has a pretty good implementation of rijndael. I'm not posting code directly here because not sure the legal ramifications.
Here are the api docs for the Security::rijndael() method.
If encoding a file, you will want to base64_encode() before calling this method with 'encrypt', and base64_decode() after calling this method with 'decrypt'