Rectangle 27 258

Starting with your table definition:

- UserID
- Fname
- Lname
- Email
- Password
- IV

Here are the changes:

  • The fields Fname, Lname and Email will be encrypted using a symmetric cipher, provided by OpenSSL,
  • The IV field will store the initialisation vector used for encryption. The storage requirements depend on the cipher and mode used; more about this later.
  • The Password field will be hashed using a one-way password hash,

Cipher and mode

Choosing the best encryption cipher and mode is beyond the scope of this answer, but the final choice affects the size of both the encryption key and initialisation vector; for this post we will be using AES-256-CBC which has a fixed block size of 16 bytes and a key size of either 16, 24 or 32 bytes.

A good encryption key is a binary blob that's generated from a reliable random number generator. The following example would be recommended (>= 5.3):

$key_size = 32; // 256 bits
$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
// $strong will be true if the key is crypto safe

This can be done once or multiple times (if you wish to create a chain of encryption keys). Keep these as private as possible.

The initialisation vector adds randomness to the encryption and required for CBC mode. These values should be ideally be used only once (technically once per encryption key), so an update to any part of a row should regenerate it.

$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);

Let's encrypt the name field, using the earlier $encryption_key and $iv; to do this, we have to pad our data to the block size:

function pkcs7_pad($data, $size)
{
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}

$name = 'Jack';
$enc_name = openssl_encrypt(
    pkcs7_pad($name, 16), // padded data
    'AES-256-CBC',        // cipher and mode
    $encryption_key,      // secret key
    0,                    // options (not used)
    $iv                   // initialisation vector
);

The encrypted output, like the IV, is binary; storing these values in a database can be accomplished by using designated column types such as BINARY or VARBINARY.

The output value, like the IV, is binary; to store those values in MySQL, consider using BINARY or VARBINARY columns. If this is not an option, you can also convert the binary data into a textual representation using base64_encode() or bin2hex(), doing so requires between 33% to 100% more storage space.

Decryption of the stored values is similar:

function pkcs7_unpad($data)
{
    return substr($data, 0, -ord($data[strlen($data) - 1]));
}

$row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
// $enc_name = base64_decode($row['Name']);
// $enc_name = hex2bin($row['Name']);
$enc_name = $row['Name'];
// $iv = base64_decode($row['IV']);
// $iv = hex2bin($row['IV']);
$iv = $row['IV'];

$name = pkcs7_unpad(openssl_decrypt(
    $enc_name,
    'AES-256-CBC',
    $encryption_key,
    0,
    $iv
));

You can further improve the integrity of the generated cipher text by appending a signature that's generated from a secret key (different from the encryption key) and the cipher text. Before the cipher text is decrypted, the signature is first verified (preferably with a constant-time comparison method).

// generate once, keep safe
$auth_key = openssl_random_pseudo_bytes(32, $strong);

// authentication
$auth = hash_hmac('sha256', $enc_name, $auth_key, true);
$auth_enc_name = $auth . $enc_name;

// verification
$auth = substr($auth_enc_name, 0, 32);
$enc_name = substr($auth_enc_name, 32);
$actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);

if (hash_equals($auth, $actual_auth)) {
    // perform decryption
}
hash_equals()

Storing a reversible password in your database must be avoided as much as possible; you only wish to verify the password rather than knowing its contents. If a user loses their password, it's better to allow them to reset it rather than sending them their original one (make sure that password reset can only be done for a limited time).

Applying a hash function is a one-way operation; afterwards it can be safely used for verification without revealing the original data; for passwords, a brute force method is a feasible approach to uncover it due to its relatively short length and poor password choices of many people.

Hashing algorithms such as MD5 or SHA1 were made to verify file contents against a known hash value. They're greatly optimized to make this verification as fast as possible while still being accurate. Given their relatively limited output space it was easy to build a database with known passwords and their respective hash outputs, the rainbow tables.

Adding a salt to the password before hashing it would render a rainbow table useless, but recent hardware advancements made brute force lookups a viable approach. That's why you need a hashing algorithm that's deliberately slow and simply impossible to optimize. It should also be able to increase the load for faster hardware without affecting the ability to verify existing password hashes to make it future proof.

A password hash can be generated like this:

$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('$2y$%02d$%s',
    13, // 2^n cost factor
    substr(strtr(base64_encode($random), '+', '.'), 0, 22)
);

$hash = crypt($password, $salt);

The salt is generated with openssl_random_pseudo_bytes() to form a random blob of data which is then run through base64_encode() and strtr() to match the required alphabet of [A-Za-z0-9/.].

The crypt() function performs the hashing based on the algorithm ($2y$ for Blowfish), the cost factor (a factor of 13 takes roughly 0.40s on a 3GHz machine) and the salt of 22 characters.

Once you have fetched the row containing the user information, you validate the password in this manner:

$given_password = $_POST['password']; // the submitted password
$db_hash = $row['Password']; // field with the password hash

$given_hash = crypt($given_password, $db_hash);

if (isEqual($given_hash, $db_hash)) {
    // user password verified
}

// constant time string compare
function isEqual($str1, $str2)
{
    $n1 = strlen($str1);
    if (strlen($str2) != $n1) {
        return false;
    }
    for ($i = 0, $diff = 0; $i != $n1; ++$i) {
        $diff |= ord($str1[$i]) ^ ord($str2[$i]);
    }
    return !$diff;
}

To verify a password, you call crypt() again but you pass the previously calculated hash as the salt value. The return value yields the same hash if the given password matches the hash. To verify the hash, it's often recommended to use a constant-time comparison function to avoid timing attacks.

Password hashing with PHP 5.5

PHP 5.5 introduced the password hashing functions that you can use to simplify the above method of hashing:

$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
if (password_verify($given_password, $db_hash)) {
    // password valid
}
password_hash()
password_verify()

This is great stuff! One of the best answers I seen on Stackoverflow.

Sure, but it depends on how it's being used. If you publish an encryption library, you don't know how developers will implement it. That's why github.com/defuse/php-encryption provides authenticated symmetric-key encryption and doesn't let developers weaken it without editing its code.

@Scott Very well, I've added an example of authenticated encryption; thanks for the push :)

+1 for authenticated encryption. There's not enough information in the question to say that AE isn't necessary here. Certainly SQL traffic often goes over a network with unknown security properties, as does traffic from database to storage. Backups and replication too. What's the threat model? The question doesn't say, and it might be dangerous to make assumptions.

Instead of hard-coding$iv_size = 16;, I would use: $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("AES-256-CBC")) in order to indicate the link between iv's size to use with the cipher used. You could also expand a bit on the need (or not) of pkcs7_pad()/pkcs7_unpad(), or just simplify the post by getting rid of them and use "aes-256-ctr". Great post @Jack

security - How to encrypt/decrypt data in php? - Stack Overflow

php security encryption cryptography encryption-symmetric
Rectangle 27 288

$key = 'password to (en/de)crypt';
$string = ' string to be encrypted '; // note the spaces
$iv = mcrypt_create_iv(
    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC),
    MCRYPT_DEV_URANDOM
);

$encrypted = base64_encode(
    $iv .
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        $string,
        MCRYPT_MODE_CBC,
        $iv
    )
);
$data = base64_decode($encrypted);
$iv = substr($data, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

$decrypted = rtrim(
    mcrypt_decrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        substr($data, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),
        MCRYPT_MODE_CBC,
        $iv
    ),
    "\0"
);

Warning: The above example encrypts information, but it does not authenticate the ciphertext to prevent tampering. You should not rely on unauthenticated encryption for security, especially since the code as provided is vulnerable to padding oracle attacks.

echo 'Encrypted:' . "\n";
var_dump($encrypted); // "m1DSXVlAKJnLm7k3WrVd51omGL/05JJrPluBonO9W+9ohkNuw8rWdJW6NeLNc688="

echo "\n";

echo 'Decrypted:' . "\n";
var_dump($decrypted); // " string to be encrypted "

Actually, not "some random", but "random and stored after that".)

I was just wondering on the significance of the spaces around $string. Are they added artificially before encrypting to increase difficulty of cracking? Sorry if I missed the point on that ;)

@wired00: No, not at all! If you decrypt an encrypted string you'll notice that some NULL bytes are added at the end of the string. Usually people just use [r]trim() to get rid of those NULL bytes, but in the process spaces are also removed. I added the spaces so that people could test and verify that the right way to trim a decrypted string is: $original = rtrim($decrypted, "\0"); - note the \0. =)

Holy hell. This is bad even as a suggestion for how to store passwords. It's not even reversible (so why are you using encryption in the first place)... And it doesn't use bcrypt (which you mention in the first line). As far as how to encrypt data, take a look at this SO Answer which includes the proper tools to set it up correctly... Example: you're missing padding (which CBC requires)...

Well, and after looking at the code deeper, you're not storing the password at all. You're using the password as a cipher key to store other data. So this doesn't even answer the question. If you're doing that, you need to use a key derivation function on the password first. Something like PBKDF2...

mcrypt - Best way to use PHP to encrypt and decrypt passwords? - Stack...

php mcrypt encryption scramble
Rectangle 27 287

$key = 'password to (en/de)crypt';
$string = ' string to be encrypted '; // note the spaces
$iv = mcrypt_create_iv(
    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC),
    MCRYPT_DEV_URANDOM
);

$encrypted = base64_encode(
    $iv .
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        $string,
        MCRYPT_MODE_CBC,
        $iv
    )
);
$data = base64_decode($encrypted);
$iv = substr($data, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

$decrypted = rtrim(
    mcrypt_decrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        substr($data, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),
        MCRYPT_MODE_CBC,
        $iv
    ),
    "\0"
);

Warning: The above example encrypts information, but it does not authenticate the ciphertext to prevent tampering. You should not rely on unauthenticated encryption for security, especially since the code as provided is vulnerable to padding oracle attacks.

echo 'Encrypted:' . "\n";
var_dump($encrypted); // "m1DSXVlAKJnLm7k3WrVd51omGL/05JJrPluBonO9W+9ohkNuw8rWdJW6NeLNc688="

echo "\n";

echo 'Decrypted:' . "\n";
var_dump($decrypted); // " string to be encrypted "

Actually, not "some random", but "random and stored after that".)

I was just wondering on the significance of the spaces around $string. Are they added artificially before encrypting to increase difficulty of cracking? Sorry if I missed the point on that ;)

@wired00: No, not at all! If you decrypt an encrypted string you'll notice that some NULL bytes are added at the end of the string. Usually people just use [r]trim() to get rid of those NULL bytes, but in the process spaces are also removed. I added the spaces so that people could test and verify that the right way to trim a decrypted string is: $original = rtrim($decrypted, "\0"); - note the \0. =)

Holy hell. This is bad even as a suggestion for how to store passwords. It's not even reversible (so why are you using encryption in the first place)... And it doesn't use bcrypt (which you mention in the first line). As far as how to encrypt data, take a look at this SO Answer which includes the proper tools to set it up correctly... Example: you're missing padding (which CBC requires)...

Well, and after looking at the code deeper, you're not storing the password at all. You're using the password as a cipher key to store other data. So this doesn't even answer the question. If you're doing that, you need to use a key derivation function on the password first. Something like PBKDF2...

mcrypt - Best way to use PHP to encrypt and decrypt passwords? - Stack...

php mcrypt encryption scramble
Rectangle 27 1

I question the value of this second database that holds keys and salts. Consider:

  • The "something" in the user's data that identifies the salt and key will necessarily have to be encrypted differently from the rest of the user's data. Otherwise, you wouldn't be able to get it without first already having it.
  • Statistical analysis of the encrypted user data would almost certainly discover that the "something" is encrypted differently. That will be like waving a red flag at a bull, and an attacker will concentrate on figuring out why that's different.
  • You can assume that if an attacker can get the database of encrypted user information, he can also get the database of salts and keys.

Given that, there are two possible scenarios:

  • The encryption of the "something" that identifies the key and salt is unbreakable. That is, it's so good that the attacker's best efforts fail to reveal the connection between that "something" and the key/salt database.
  • The attacker discovers the encryption of the "something," and therefore is able to decrypt your sensitive data.

If #1 is the case, then you probably should use that encryption algorithm for all of your user data. Why do something in two steps when you can do it just as effectively in one?

If #2 is the case, then all the work you've done just put up a little bump in the road for the attacker.

So, short answer: what you propose looks like either unnecessary work or ineffective road blocking. In either case, it looks to me like a whole lot of work and added complexity for no appreciable gain.

here is my point, If we know, we need to add a salt and a key to the algorithm. Why not make the salt and the key unique(ish ( theoretical 2 years may have same key and salt combo but very unlikely)). I want to have a table of prelisted keys and salts, where some algorythm will determine which salt and which key to use. So unless the attacker go to the file system and looked at the algorythm, there isnt any easy way decrypt the info, because if the attacker got 5... or 10 sets of data, and tried to reverse engineer the equation using that, there would be no way to do so.

If the attacker got access to the file system, then I'm screwed anyway. If the attacker got access to the DB, they would see 100's of different cells of random text. I did some quick math, there are about 6760000 different possibly key and salt combo's. I figured better then everyone using same key etc. And yes, its just an extra layer for an attacker to have to break. Hope by then, my security system would detect the possible intruder.

algorithm - Best way to encrypt and decrypt data using php and mysql -...

algorithm encryption
Rectangle 27 7

The main issue appears to be that your string_encrypt and string_decrypt PHP functions don't have access to the $key variable, so for the encryption key mcrypt_encrypt is using \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0. See this question for an explanation. PHP should report a notice that key is undefined, have you turned off error reporting perhaps? Echo the key from inside the encrypt function to confirm this.

Another issue is a bug in the Mcrypt JS library. This library pads the encryption key with \0 if the key length is less than 32 bytes, the problem is that this is not how the PHP mcrypt_encrypt function pads the key. The mcrypt_encrypt function pads the key up to the nearest valid key length (16, 24, or 32 bytes). The issue in mcrypt.js is at lines 63 and 64, change this:

if(key.length<32)
    key+=Array(33-key.length).join(String.fromCharCode(0));
if(key.length<16)
    key+=Array(17-key.length).join(String.fromCharCode(0));
else if(key.length<24 && key.length>16)
    key+=Array(25-key.length).join(String.fromCharCode(0));
else if(key.length<32 && key.length>24)
    key+=Array(33-key.length).join(String.fromCharCode(0));

Now we can confirm the fix...

function string_encrypt($string) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, "", $string, MCRYPT_MODE_ECB);
    return $crypted_text;
}

$test_str = "This is test message to be encrypted.";
$enc_str = string_encrypt($test_str);
echo bin2hex($enc_str);

Output:
f98fca4ddc4c10d6cd47df56b081b78566ee4facbcf2254b46f7809d9b255529d2078f28b150e802d72818be1888536fac6219f6ce7b9d9332a24afa09288f0e
function toHex(str) {
    var hex = '';
    for(var i=0;i<str.length;i++) {
        var val = ''+str.charCodeAt(i).toString(16);
        if(val.length == 1)
            hex += '0'+val;
        else
            hex += val;
    }
    return hex;
}

var enc_str = mcrypt.Encrypt("This is test message to be encrypted.", "", "", "rijndael-256", "ecb");
alert(toHex(enc_str));

Output:
f98fca4ddc4c10d6cd47df56b081b78566ee4facbcf2254b46f7809d9b255529d2078f28b150e802d72818be1888536fac6219f6ce7b9d9332a24afa09288f0e

Finally, all of these encryption functions produce binary as their output. Binary cannot be written as plain text in most cases without damaging the data. To solve this, either encode the binary to Hex or Base64 and then decode it before trying to decrypt.

So to get everything working...

<?php 
$key = 'testtesttesttesttesttesttesttest';

function string_encrypt($string, $key) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_ECB);
    return $crypted_text;
}

function string_decrypt($encrypted_string, $key) {
    $decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted_string, MCRYPT_MODE_ECB);
    return trim($decrypted_text);
}

echo $test_str = 'This is test message to be encrypted.';   echo '<br />';
$enc_str = string_encrypt($test_str, $key);
echo bin2hex($enc_str);                                     echo '<br />';
echo string_decrypt($enc_str, $key);                        echo '<br />';

?>

<script src='rijndael.js'></script>
<script src='mcrypt.js'></script>

<script lang='javascript'>
    function toHex(str) {
        var hex = '';
        for(var i=0;i<str.length;i++) {
            var val = ''+str.charCodeAt(i).toString(16);
            if(val.length == 1)
                hex += '0'+val;
            else
                hex += val;
        }
        return hex;
    }
    function hexToString (hex) {
        var str = '';
        for (var i=0; i<hex.length; i+=2) {
            str += ''+String.fromCharCode(parseInt(hex.charAt(i)+hex.charAt(i+1), 16));
        }
        return str;
    }
    var enc_str = mcrypt.Encrypt('<?php echo $test_str ?>', '', 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb');
    alert(toHex(enc_str));
    alert(mcrypt.Decrypt(hexToString('<?php echo bin2Hex($enc_str) ?>'), '', 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb').replace(/\x00+$/g, '')); 
</script>
  • You cannot trim the output of the string_encrypt function. This will cause leading or trailing zeros to be removed, which will make it so that you cannot decrypt the output.
  • ECB mode is insecure and you really shouldn't use it. CBC is the way to go. CBC does require an IV, and the IV must be the same for both encryption and decryption.
  • Javascript encryption is not secure for various reasons, given your usage of it anyone could simply view the pages source or debug the running javascript to get the encryption key. Read the link posted by ntoskrnl in your question comments.

Your Base64 encoding issue occurs because the library you're using doesn't work with binary data. This is a fairly common issue for Base64 javascript libraries. I'd recommend using this library instead.

As for the trailing characters when decrypting with javascript, you need to trim the decrypted output. You're doing this in your PHP string_decrypt method, but not in your javascript. You can trim the decrypted output by doing a regex replace on all \0 characters at the end of the string.

mcrypt.Decrypt(dec_str,'').replace(/\x00+$/g, '')

I should have included this in my original post, but I didn't notice the \0 characters in the output because FF's alert box doesn't display them. Sorry about that.

Finally, I noticed another bug in the Mcrypt JS library. Lines 41 to 47:

var ciphers={       //  block size, key size
    "rijndael-128"  :[  16,         32],
    "rijndael-192"  :[  24,         32],
    "rijndael-256"  :[  32,         32],
    "serpent"       :[  16,         32],
    "twofish"       :[  16,         32],
}

Notice the comma at the end of the "twofish" line. Firefox and Chrome don't seem to mind this, but IE8 will report an error and fail to load the mcrypt library because of it. To fix the issue change:

"twofish"       :[  16,         32]

@hsuk: Updated, fixed a problem with putting the raw encrypted text directly in the page source. PHP now prints hex inside the mcrypt.Decrypt method call which gets decoded via javascript.

$key

Thank you so much again... Now, I could get the same encrypted value on both ends i.e. after enryption + base64 encoding, but its still different for unicode chars...

Mcrypt js encryption value is different than that produced by PHP mcry...

php encryption mcrypt mcrypt-js
Rectangle 27 3

PHP provides built-in solutions for encrypting and decrypting data. Use e.g. MCrypt, supports a good buffet of common algorithms. What exact cipher you choose is up to you, depending on the level of security required. Read up at Wikipedia on block ciphers for the general picture. Edit: Just learned that MCrypt is deprecated in PHP 7.1 and the use of OpenSSL is recommended in its place.

If the phone numbers must be inaccessible even if a hacker gains complete access to your system, then the decrypt key must obviously be off-server, ie. you can't store it in DB or the filesystem.

In the spec you have, this key would then be the e-mail address that's matched with the phone number. Then, at input time you'd one-way hash the e-mail, and use the unhashed e-mail as a key for encrypting the phone number. At request time, you'd find the matching phone number by using the e-mail hash, and decrypt it using the unhashed email as the key. Should get you sorted.

Update: When fetching a record that matches the phone/email pair in the database, if you've used password_hash() (which generates a new salt and a unique string each time), then your only option is to fetch all records and iterate them through password_verify(). That's not particularly scalable.

Unless this is an exercise in security, I'm not sure I'd bother with more than a simple sha1() hash for the e-mails. Or use e.g. crypt($email, '$2y$08$Some22CharsOfFixedSalt$'); -- see crypt() -- and generate a blowfish-based hash that uses a fixed salt string, resulting in an invariant hash. I'd also then truncate the leading salt-part of the resulting string from the database entry.

If you're feeling crafty, why not cook up an algorithm that derives a unique string from each email, and then use that for salt in your hashing function, instead of using the same salt for hashing all e-mails.

SHA2('email', 256)
INSERT
SELECT
INSERT INTO records VALUES (SHA2('email@what', 256), 'TheEncryptedTelNo');
SELECT * FROM records WHERE email = SHA2('email@what', 256);

thank you. what is the best way to find out if email exists in database when user wants to send him phone number? Emails are hashed in db. So, I get all hashed emails and for(...) { ... } cycle compare by password_verify()? Or there is faster way?

The e-mail is saved as a hash in the database, yes? Simply hash again the e-mail the user inputs in the request, then do a SQL query for an entry with a matching e-mail hash. I don't really see a need for the whole password_hash | password_verify hop here. I'd simply toss in a sha1() hash for the e-mail with some extra salt thrown in, and store that in the database for a reference. Of course if this is actually an exercise in maximizing security, then by all means go the long way, but I don't see how your case calls for it, unless you're storing CIA agent phone numbers or something.

In a common use-case, the user would input their username (plain) and their password, then a password hash matching the username would be fetched from the database and verified. In this case, the e-mail acts as the only point of reference. Since password_hash() returns a unique string every time, if you want to use it, your only option is to compare through the whole database for each request. Which really doesn't scale very gracefully. For a different approach, you could look into MySQL's hashing and encryption functions: dev.mysql.com/doc/refman/5.5/en/encryption-functions.html

php - Can I store hashed phone number and send it unhashed to email? -...

php mysql hash password-encryption crypt
Rectangle 27 53

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");
    }
}
$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
  • This class is not safe for use with binary data (which may end in NUL bytes)
  • This class does not provide authenticated encryption.

Will this work for binary data such as images?

That's correct. I ran this class on text files, and it worked great. For binary files it is necessary to encode the information before encrypting it. If the files are larger than 100MB or so, base64_encode will cause performance issues, so you may want to consider splitting the files into chunks for encrypting. From a security standpoint this isn't an ideal solution because it provides more opportunity for recovering partial plaintext. But, it works.

Can we add a strong disclaimer? Authenticated encryption is absolutely essential to defend against active attackers. There's really no way to negotiate it away unless you cripple your threat model below what the average script kiddie can pull off, which helps approximately no one.

"Authenticated encryption is not necessary." Wrong. Super wrong. Authenticated encryption is no longer negotiable.

php - Encrypting / Decrypting file with Mcrypt - Stack Overflow

php file mcrypt
Rectangle 27 2

If you encrypt the cookie, the server still has to decode it to read it (to check for same key), therefore any encrypted cookie is pointless, because if stolen (and un-edited) it will still lead the hacker right to your account. Its just as unsafe as no encrypted at all.

I believe the real issue of someone stealing your cookie is the connection between the server and client. Use SSL connection provided by your host.

As for your cookie, you need to make a long random id per user in the database, (have it change every log on) and just set that as the cookie or session. The cookie that contains the key can be checked via php and if it is equal to an account or table in your database, dump the data on the web page like normal.

Yes, but the contents of that cookie is still secure.

php - What encryption algorithm is best for encrypting cookies? - Stac...

php security cookies encryption remember-me
Rectangle 27 2

If you encrypt the cookie, the server still has to decode it to read it (to check for same key), therefore any encrypted cookie is pointless, because if stolen (and un-edited) it will still lead the hacker right to your account. Its just as unsafe as no encrypted at all.

I believe the real issue of someone stealing your cookie is the connection between the server and client. Use SSL connection provided by your host.

As for your cookie, you need to make a long random id per user in the database, (have it change every log on) and just set that as the cookie or session. The cookie that contains the key can be checked via php and if it is equal to an account or table in your database, dump the data on the web page like normal.

Yes, but the contents of that cookie is still secure.

php - What encryption algorithm is best for encrypting cookies? - Stac...

php security cookies encryption remember-me
Rectangle 27 2

Since you're using mysqli_fetch_assoc, the name of the columns are the keys of the $row array in each iteration. You can put that in the file in the first iteration:

echo "Starting Write to CSV \n";
$first = true;
while($row = mysqli_fetch_assoc($result)){
    if ($first) {
        fputcsv($fp, array_keys($row));
        $first = false;
    }
    fputcsv($fp, $row);
    // ..
}

Use PHP to get Column Names and Data for CSV export (MYSQL) - Stack Ov...

php mysql database csv
Rectangle 27 1

Sessions in CodeIgniter or any other application using HTTP protocol are best kept track of using cookies. Normally, the session data itself is not stored using cookies, but a key to access this data is, whether the actual session data is stored in server's filesystem or in a database.

PHP allows to set session ID through cookies, POST or GET, but it is preferable to always use cookie or you will be opening doors to session fixation using ini_set('session.use_only_cookies', true). Practically everybody do have cookies enabled.

Unless I'm mistaken, in CodeIgniter, the entire session data is stored in the cookie, and there is no filesystem component to it. This is unlike standard Sessions where only the key is stored in the cookie, and the value resides on the filesystem.

I checked CodeIgniter session library and yes, CodeIgniter does have an option to store session data using encrypted cookies. Personally I don't think it is a good approach and I would recommend to store sessions in a database unless you are really tight on storage. However, it does not make any difference as far as cookie-disabled browsers are concerned.

php - Codeigniter's session data, are they just cookies? - Stack Overf...

php codeigniter session cookies
Rectangle 27 3

HTTPS represents using an algorithm to secure transport layer communications that encrypts what the browser and server are sending each other. The encrypted form that is sent over the network is expensive computationally to turn back into it's original content, unless whoever is decrypting the ciphertext has the paired private key (or... a "computationally weak" algorithm was used).

This is point to point (client sends request to server, server accepts, determines response, sends response), so the browser and server first exchange public keys that represent what the data you are sending or receiving is going to be "scrambled" against. A private key is held by the public key issuer that allows that computer to "descramble" and obtain the original content that was sent to the receiver.

So when you create a private/public key pair, the private key is kept (in confidence and secret) on the server, and the related public key is sent to the browser. The browser, likewise, does the same thing and transmits a public key to the server.

"Protecting" sensitive information is not all that's going on; you may also use SSL/TLS to prevent tampering with data, for example, or even as an additional verification step.

To get HTTPS setup and available for you to use, you need to:

  • Procure a public/private key (signing by a certificate authority, or CA, is potentially optional depending on your end users).
  • Install it into the key store on the server that is available to your web server. OpenSSL is used for both of these steps in many cases.
  • Setup your system to use HTTPS URLs (for all resources, not just a <form action="https://...">s).

You can use WireShark to inspect what your computer is actually sending and receiving. This can be very illuminating, especially in combination with viewing the request/response in Firebug or Chrome Net consoles.

There's various tutorials on how to setup SSL on a LAMP stack. Here is a tutorial on WAMP2 HTTPS and SSL Setup if you just need a development environment.

If you have a shared hosting environment, you may not be able to do the SSL setup on the server yourself; that may be handled by the server administrator. Check with your host. DreamHost, for example, has extensive docs.

Thank you very much Jared. I read all the notes, and I gotta say brilliant. As for now, I setup the SSL on my web page and force the SSL to all pages. I guess I'm going to inspect what my computer sending/receiving by using wireshark. And for the SSL itself, it is a private one, not share. And it is already setup and working fine. All good for now. Thanks a lot.

@Fxdigi, note that point 3 of Jared's answer is often neglected, but is very important. See this answer for details about the problem: checking $_SERVER['HTTPS'], using SSLRequireSSL or using mod_rewrite to force SSL from the server side won't fix these issues. Sending requests of HTTPS is ultimately up to the client: you should make sure your server tells it to do so (using appropriate links/URLs).

php login with SSL - Stack Overflow

php ssl login
Rectangle 27 1

You're trying to decode a key that is not in any known format it seems. I presume it has been made by somebody that does not fully understand crypto, as it has a 1023 bit modulus.

If you look at the hexadecimal representation you'll find a modulus with a value of:

4D49BB0D2E0FA132D081A6338C178124AB2B1B61A57C6C30D05EAD179BE1040B235E0EE83F3BF29A8F19DC33B7E245FAE7BE96E35CC2DF49E8B519D4F53501E3566D693A66E69D8C812AF66AC6D5D86ED764ED27A91C5828CE860A8B01077C15142B77BF772AFF201577DF8FBE9E92168539480E024DFF51173CD26B65858ACA

(simply the last value within the structure with a size close to 1024 bits) and a public exponent with a value

010001

encryption - How to encrypt data using rsa public key in php - Stack O...

php encryption rsa public-key-encryption
Rectangle 27 3

Don't store plaintext passwords. If your game becomes popular, it might be a target to constant attacks of hackers especially if it will contain a monetary aspect (i.e - the case of world of warcraft, travian and others). In this case, you will need to assume that although you attempt to protect your system, Someone might hack it, and as a result get sensitive data. You should use standard encryption mechanisms in order to perform this task (i.e - send the password to forum system via HTTPS for example, in a secure way). I would also recommend you to explore the comment of @Joshua Kaiser - single sign on may be the key to answer your needs, and don't try to reinvent the wheel here. I can tell you I Work with kerberos for example, and Kerberos has a ticket mechanism in which the tickets can be re-used among applications. Unfortunately I dont know PHP, and don't know how pluggable the forum framework to use different authentication modules.

P.S - I posted this answer twice by mistake, and tried pressing "delete post" - I hope stackoerflow takes care of that.

java - Is it safe to store plaintext passwords in MySQL *temporarily*?...

java php mysql security phpbb3
Rectangle 27 1

But I am struggling with the idea of authenticated encryption with aes cbc. How do I basically authenticate when I am about to decrypt the data?

After you encrypt the data with a random IV, put both the ciphertext and IV into hash_hmac() with a second key.

If you're asking because you need to deploy into production, wait until version 2 of defuse/php-encryption is released and use that instead. (It's AES-256-CTR not AES-256-CBC, but CTR mode has less attack surface than CBC mode; i.e. no padding oracle attacks if you defeat the HMAC.)

RNCryptor is/was not written in accordance to cryptography coding standards, neither in PHP, nor in Python.

RNCryptor literally violates rule 1 of the cryptography coding standards consistently. There may be other issues that have yet been undiscovered. If you want portability across languages, use libsodium.

CTR mode is difficult to get correct with the nonce requirement, Microsoft has gotten that wrong at least a couple of times. CBC in itself does not have a padding attack, if the implementation does not return padding errors there is no Padding Oracle.

CTR nonce reuse vs CBC IV reuse is equally difficult to implement safely, although their impacts are drastically different. CTR mode turns your block cipher into a stream cipher, which reduces the attack surface by removing padding entirely.

Scott, you know the consequences of a reused nonce and key combination in CTR mode. Two messages with the same nounce and key combination can expose the xor stream and that can be used to decrypt any further messages encrypted with the same nonce and key combination. CBC mode does not have that problem and padding does not provide an attack vector, a Padding Oracle does.

And a padding oracle requires not authenticating the ciphertext. Or a timing side-channel that allows forgeries.

encryption - How to correctly encrypt data with proper authentication ...

php encryption cryptography aes
Rectangle 27 2

You do not use Encapsulation because it prevents people from touching your code but because you want to create a limited but dedicated public API for your objects to exchange messages through. This a key element in in OOP paradigm. The aim is not to security but maintainable and flexible applications. If people want to modify your code, let them. They are grown ups.

yeah i could allow them, but what if i have implement an e-commerce site which handles different types of payments systems. do i still ask other developers to come modify my source code? and the main question is can people access my source code which is hosted in remote server and can they actually modify it even if the file have 777 permissions?

@Ibrahim File permissions have nothing to do with the concept of Encapsulation. 777 is world writable. Fire your SysAdmin if s/he set any permissions to that. But it's completely unrelated to Encapsulation or Software Development at all.

api - What is the use of encapsulating data in case of PHP? - Stack Ov...

php api encapsulation
Rectangle 27 1

I've dealt with similar data conversion problems, though at a smaller scale. If I'm understanding your problem correctly (which I'm not sure of), it sounds like the detail that makes your situation challenging is this: you're trying to do two things in the same step:

  • import a large number of rows from CSV into mysql, and
  • do a transformation such that the person-marathon associations work through person_id and marathon_id, rather than the (unwieldy and undesirable) varchar personkey column.

In a nutshell, I would do everything possible to avoid doing both of these things in the same step. Break it into those two steps - import all the data first, in tolerable form, and optimize it later. Mysql is a good environment to do this sort of transformation, because as you import the data into the persons and marathons tables, the IDs are set up for you.

Step 1: Importing the data

  • I find data conversions easier to perform in a mysql environment than outside of it. So get the data into mysql, in a form that preserves the person-marathon associations even if it isn't optimal, and worry about changing the association approach afterwards.
  • You mention temp tables, but I don't think you need any. Set up a temporary column "personkey", on the persons_marathons table. When you import all the associations, you'll leave person_id blank for now, and just import personkey. Importantly, ensure that personkey is an indexed column both on the associations table and on the persons table. Then you can go through later and fill in the correct person_id for each personkey, without worrying about mysql being inefficient.
  • I'm not clear on the nature of the marathons table data. Do you have thousands of marathons to enter? If so, I don't envy you the work of handling 1 spreadsheet per marathon. But if it's fewer, then you can perhaps set up the marathons table by hand. Let mysql generate marathon IDs for you. Then as you import the person_marathon CSV for each marathon, be sure to specify that marathon ID in each association relevant to that marathon.

Once you're done importing the data, you have three tables: * persons - you have the ugly personkey, as well as a newly generated person_id, plus any other fields * marathons - you should have a marathon_id at this point, right? either newly generated, or a number you've carried over from some older system. * persons_marathons - this table should have marathon_id filled in & pointing to the correct row in the marathons table, right? You also have personkey (ugly but present) and person_id (which is still null).

Step 2: Use personkey to fill in person_id for each row in the association table

Then you either use straight Mysql, or write a simple PHP script, to fill in person_id for each row in the persons_marathons table. If I'm having trouble getting mysql to do this directly, I'll often write a php script to deal with a single row at a time. The steps in this would be simple:

  • look up any 1 row where person_id is null but personkey is not null
  • write that person_id in the associations table for that row

You can tell PHP to repeat this 100 times then end script, or 1000 times, if you keep getting timeout problems or anything like taht.

This transformation involves a huge number of lookups, but each lookup only needs to be for a single row. That's appealing because at no point do you need to ask mysql (or PHP) to "hold the whole dataset in its head".

At this point, your associations table should have person_id filled in for every row. It's now safe to delete the personkey column, and voila, you have your efficient foreign keys.

"on-marathon associations work through person_id and marathon_id, rather than the (unwieldy and undesirable) varchar personkey column" -- This is exactly the problem. There are about 20-40 marathons each month but this data is not received as a CSV... it's just kind of attached to the person_marathon data so it's fairly easy to deal with compared to everything else.

It seems like it might be a little hard to automate and I'm concerned about sticking extra info into person_marathon with new and old data and then removing (even unnecessary) data from the table.

OK, thanks for the comments and best of luck with this, Gloomy. It sounds like you'll continue to receive new marathon data in this unwanted format (where the varchar personkey is the only person reference)? I had been responding as though that was a one-time challenge. But even so, I think I'd still start by considering my proposed solution: set up the persons_marathons table to have an extra column for the varchar, though it sounds like you canNOT eventually remove that personkey column as it will continue to be needed. Mysql can handle the redundancy.

How to import "a lot" of data to MySQL with PHP and foreign keys? - St...

php mysql insert large-data
Rectangle 27 0

You could encrypt the customer data with the customer's password. Neither the PHP server nor the MySQL server know that password. But you can obtain it from the customer himself. So, when the customer logs in, they will send you the password for authentication. In that brief moment, you will have the password that you can use to decrypt his data while sending it to him.

This way, if your whole site is compromised and the hacker has your code, your passwords, database access and everything, he will still be unable to read or modify the customer's data.

Securing MySQL with PHP by separating them - Stack Overflow

php mysql security
Rectangle 27 0


Using mysql_fetch_field you can produce a more robust version of mysql_fetch_assoc.


When querying 2 tables with the same field names, generally you would need to use mysql_fetch_row to get an integer key'ed array rather than an associated key'ed array. This is because fields of the same name in the second table will over-write the data returned from the first table.
However this simple function will insert the table name prior to the field name for the key and prevent cross-overs.

ie SELECT *, 'test' AS test 4 FROM table AS T_1, table AS T_2 WHERE T_1.a=T_2.b

could produce:

mysql_fetch_assoc() returns
array(
'index'=>2,
'a'=>'pear',
'b'=>'apple',
'test'=>'test',
4=>4
)

mysql_fetch_table_assoc() returns
array(
'T_1.index' =>1,
'T_1.a'=>'apple',
'T_1.b'=>'banana',
'T_2.index'=>2,
'T_2.a'=>'pear',
'T_2.b'=>'apple',
'test'=>'test',
4=>4
)

<?php
function mysql_fetch_table_assoc($resource)
{
// function to get all data from a query, without over-writing the same field
// by using the table name and the field name as the index

// get data first
$data=mysql_fetch_row($resource);
if(!
$data) return $data; // end of data

// get field info
$fields=array();
$index=0;
$num_fields=mysql_num_fields($resource);
while(
$index<$num_fields)
{
$meta=mysql_fetch_field($resource, $index);
if(!
$meta)
{
// if no field info then just use index number by default
$fields[$index]=$index;
}
else
{
$fields[$index]='';
// deal with field aliases - ie no table name (SELECT T_1.a AS temp)
if(!empty($meta->table)) $fields[$index]=$meta->table.'.';
// deal with raw data - ie no field name (SELECT 1)
if(!empty($meta->name)) $fields[$index].=$meta->name; else $fields[$index].=$index;
}
$index++;
}
$assoc_data=array_combine($fields, $data);
return
$assoc_data;
}
?>
Rectangle 27 0

Just make your form gets submitted using the HTTPS protocol, and all of the data is transparently encrypted (this means you don't need to do anything to decrypt it in PHP, it just works)

You need to have an SSL certificate installed on your server and activated with your registrar. You can then make all elements within the form page (including the form's own action attribute) use https:// to open a HTTPS connection. This will encrypt the communication between the client and the server. Emailing SSN numbers or very sensitive personal information from PHP is still a bad idea however.

It's not possible to send encrypted emails without requiring the recipient to decrypt them (by themselves or using a special client). Actually here's a related question: stackoverflow.com/questions/3146847/ . Regardless, you definitely should not email SSN or other sensitive information in plaintext.

PHP secure form - Stack Overflow

php forms security