I need to overcome limits on my ISP's PHP environment - php

I'm stuck with PHP 5.4 and a single database userid because of defects in Comcast's business hosting implementation. The userid problem means that the same credentials I use to manage a MySQL database must also be used while handling requests from ordinary users. I have control over who can login as an ordinary user by delegating the creation of ids to a group of trusted users who will communicate to the ultimate end users the URL they will need to login to the app. And, although the server is running Apache, it ignores an Authorization header.
Positing that some hacker might defeat the HostWays Unix server's file system protection and read all of my PHP source code, I need to avoid having the database credentials appear in the MySQL connect calls in my PHP source. I've come up with the following scheme for encrypting the database credentials, and I want to be sure that the scheme will actually be effective.
I will define a global variable named $credentials in a configuration file. I'll generate the value in my development environment with this code:
$cipher = "AES-128-CBC";
$secretkey = …; // it's a secret!
$secret = openssl_encrypt("$dbuser:$dbpass", $cipher, $secretkey);
Remember, I'm stuck with PHP 5.4, and I wasn't able to get past mysterious errors in openssl_decrypt when I used an initial-value in the encrypt/decrypt calls. Anyhow, this much of the code at least works.
My PHP app can be accessed two ways: one is via an XML payload in a POST transaction coming from a C++ desktop app; the other is via an ordinary URL in a GET. Both methods will use HTTPS. The XML schema calls for an authentication parameter that supplies the $secretkey.
The URL for the GET transaction will be used by general users from a browser. It will include a secret= parameter that encodes both the user's login id and the $secretkey. I will generate the secret= parameter during the transaction that adds the user to the list of authorized users:
$cipher = "AES-128-CBC";
$key = "Fortune42Cookies!";
$secret = openssl_encrypt("$username:$secretkey", $cipher, $key);
In this code, the $secretkey is left over from when the trusted user who is adding this end user to the list logged in. The corresponding decryption code used when the end user logs in is as follows:
$cipher = "AES-128-CBC";
$key = "Fortune42Cookies!";
$clearsecret = explode(":", openssl_decrypt($secret, $cipher, $key));
$username = $clearsecret[0];
$secretkey = $clearsecret[1];
$this->db = new mydatabase($secretkey);
. . .
class mydatabase extends mysqli {
public function __construct($secretkey) {
public $credentials; // set by the configuration script
$cipher = "AES-128-CBC";
$cred = explode(":", openssl_decrypt($credentials, $cipher, $secretkey);
parent::__construct(<database path>, $cred[0], $cred[1], <dbname>);
}
}
To summarize, the __construct call uses $credentials that are stored in encrypted form in a configuration file that my putative hacker can read. We decrypt them using the $secretkey that comes to us either in a secure XML packet or in the URL for a secure GET transaction.
The one flaw I see in this scheme is that a hacker who learns the login id of a registered user and who reads the PHP source code to learn the silly Fortune42Cookies! key could construct that user's unique $secret and thereby impersonate the user. Because there's only one set of credentials for the database, the hacker would then have the keys to the city.
Still, the extra layer of protection (hacker needs to defeat the UNIX file system protection AND learn the name of a real end user) seems worth it.
Fifty years of software development experience tells me I've missed something major. What is it?

Related

Encrypt mysql database

I'm trying to build a platform for users who will store confidential data about their clients. The context is quite simple: french laws prohibit me from having access to the data that my users will store (for example medical records of patients).
So when a user submits data which will be stored in the database, he should be the only one having access to that information. For example by encrypting it with his password. That way, if I log into mysql, I would only see encrypted nonsense and no readable data.
My philosophy, which might be wrong, is to learn by doing it. Hope you guys are ok with my approach.
Problem is: I have no idea where to start, how to do that... and actually not even what to search for on google. I even tried to find something suitable on codecanyon.net for example and couldn't fond any relevant scripts.
Thanks in advance :) !
PS: I will actually have the same problem with files (jpg, word, pdf, xls... that should be enough for the users). But that's another story.
Although I'm not familiar with the French data protection laws, I do have some experience with the general EU legislation. Probably you have to encrypt personal data in your system such way, that the person cannot be identified. This means, that technical data, such as a system specific id in a table can stay unencrypted. This is the only reason I believe you can actually make this work. Sort of. You have to work with lawyers to determine what data can stay unencrypted to comply with the laws and still to be able to provide a valuable service to your clients.
However, I believe that you are missing an important point: if you cannot have access to certain data, then that data MUST NOT arrive to your organisation in a plain format. Period. If it does, then you already have access to it. So, php (or any kind of server based encryption) or mysql based encryption solutions are out of the picture.
The only solution I can think of is it to team up with a 3rd party PKI provider, who will provide your clients with certificates ( perhaps on chip cards), and the client side of your application encrypts the sensitive personal data items on the client BEFORE they are sent to your server and also decrypt these data items on the client. This also means that you will have to use some plugins on the client side if you want this system to be web based. Probably you will need some signed java app to manage the card reader and the certificate.
The setup has 2 drawbacks for your clients:
It's not a single provider they deal with, since the PKI provider must be an independent third party. You must not manage the certificates.
In case the stored data gets corrupted, the encrypted data will be lost. So, you will have to implement some crazy backup and restore solution.
So assuming the problem is as follows:
You need to encrypt the data before you store it.
You shouldn't have the keys to decrypt it, only encrypt it.
There's actually a tool for this: It's called a sealing API, and it can be accomplished through OpenSSL or Libsodium.
Sealing/Unsealing Data in PHP with Libsodium
$store_me = \Sodium\crypto_box_seal(
$plaintext,
$recipient_public_key
);
$visible = \Sodium\crypto_box_seal_open(
$store_me,
$recipient_keypair
);
Sealing/Unsealing Data in PHP with OpenSSL
/**
* A human-usable variant of openssl_seal()
*
* #param string $plaintext Your message
* #param string $publickey_string PEM-encoded RSA public key
* #param boolean $encode Hex-encode the output?
*
* #return string
*/
function easy_seal($plaintext, $publickey_string, $encode = false)
{
$pubkey = openssl_get_publickey($publickey_string);
if ($pubkey === false) {
throw new Exception('Could not load public key');
}
$sealed = '';
$ekeys = [];
$result = openssl_seal($plaintext, $sealed, $ekeys, [$pubkey]);
if ($result === false) {
throw new Exception('openssl_seal failed!');
}
if ($encode) {
return json_encode([
bin2hex($sealed),
bin2hex($ekeys[0])
]);
}
return json_encode([$sealed, $ekeys[0]]);
}
/**
* Inverse operation of easy_seal()
*
* #param string $ciphertext (the output of easy_seal())
* #param string $privatekey_string PEM-encoded RSA private key
* #param boolean $encoded Do we need to decode from hex?
*
* #return string
*/
function easy_unseal($ciphertext, $privatekey_string, $encoded = false)
{
list($sealed, $ekey) = json_decode($ciphertext, true);
if ($encoded) {
$sealed = hex2bin($sealed);
$ekey = hex2bin($ekey);
}
$open_data = '';
$privkey = openssl_get_privatekey($privatekey_string);
if ($privkey === false) {
throw new Exception('Could not load public key');
}
$result = openssl_open($sealed, $open_data, $ekey, $privkey);
if ($result === false) {
throw new Exception('openssl_open failed!');
}
return $open_data;
}
Usage Example
$public_key = file_get_contents('/path/to/publickey.pem');
$plaintext = 'Something something dark side';
$store_me = easy_seal($plaintext, $public_key);
// Elsewhere:
$secret_key = file_get_contents('/path/to/secretkey.pem');
$visible = easy_unseal($store_me, $secret_key);
Demo: https://3v4l.org/BNavp
Actually, I'm also on a similar kind of project where I'm trying to build a secure database in MySQL Server that is also useful to run all the valid SQL Queries. It's still in the progress and too many difficulties are there; I accept.
But, for your problem , it seems you only need to encrypt and decrypt the values.and you don't want to store the key also there in the database. For me, there are two ways that comes in my mind:
First way is, that you decide a fixed secret key to encrypt and decrypt the values and use it for every data, that is being stored on the database.
But, that's not practical I guess, since the security gets weak with this approach and a person can identify your key with brute force approach.
Second way is, that you generate a random key for every different user, like, at the time of registration. And data of different user can be viewed by only the user who has the key to decrypt it, which here only the user has. And then you apply the first approach after this. i.e., you decide a key that will be used to encrypt these keys of different users. and then store this encrypted key in the database in a separate table. So that, next time user will try to access his/ her data, his entered key (could be his password), will be encrypted with your decided static key, if this encrypted key is found in the table of your database your will fetch the data of that user, decrypt it with his/ her key and display to him/ her.
All you need is a,
(i) programming platform to select, JAVA is best.
(ii) learn how to use database with this programming language, MySQL Server is a nice choice to work with.
(iii) And a good encryption algorithm to implement.
Hope, I didn't make you angry with this answer :) Cheers.

What part does the "token" play in password reset?

I'm using laravel's Auth password reset method and not sure i fully understand what part plays the token in all of this.
I'm sending the user an email with Password::remind('email#email.com') , which generates a token in my password_reminders table. The token is fully visible in the url.
The user goes to a url that looks something like: mywebsite.com/remindpass/xxxxxx[token] .
Then he fills out a form with his email and a new password , sending it trough post to a controller - which uses Password::reset('email','password','xxxxxx') .
The question is how is this secure? What does the generated token do to prevent someone just going to mywebsite.com/remindpass/xxxxxx[token] and change the email & password as he likes?
Can someone please clarify the proccess?
I'm sure someone could answer this question better than I could.
Short answer:
The token makes it more difficult for someone to guess the credentials needed to reset the password while making the reset link in the email available.
Long answer:
In the file vendor/laravel/framework/src/Illuminate/Auth/Guard.php, you'll see the method createRememberTokenIfDoesntExist. This method actually references another method right above it called refreshRememberToken to set your token.
It uses the laravel helper function str_random. If you trace this function back to it's source, you'll find it uses the vendor/laravel/framework/src/Illuminate/Support/Str.php class' random method.
public static function random($length = 16)
{
if (function_exists('openssl_random_pseudo_bytes'))
{
$bytes = openssl_random_pseudo_bytes($length * 2);
if ($bytes === false)
{
throw new \RuntimeException('Unable to generate random string.');
}
return substr(str_replace(array('/', '+', '='), '', base64_encode($bytes)), 0, $length);
}
return static::quickRandom($length);
}
Now we finally get down to where the token is built. This method uses the function openssl_random_pseudo_bytesto generate the token. You can read about that function in the PHP manual page for openssl_random_pseudo_bytes, but basically it generates a cryptographically strong random string.
Laravel then takes this string (still in the random method), base 64 encodes it, replaces some characters, and takes a slice of that string based on either the default setting of 16 (seen in the parameter definition $length = 16) or whatever length is passed into the method by the caller.
So, you get a string that is cryptographically strong and then manipulated as your token.
If you look at the file vendor/laravel/framework/src/Illuminate/Auth/DatabaseUserProvider.php and find the method retrieveByToken, you'll see that laravel uses both the user record ID and the token to find the user who's password needs to change.
For someone to guess that string AND the id of you user record that has that token would be incredibly difficult and would require knowledge of your application's business logic.
What does the generated token do to prevent someone just going to mywebsite.com/remindpass/xxxxxx[token] and change the email & password as he likes?
Because only you and the person you sent the email to (i.e. the account holder) know what the token is.
A strong implementation will takes steps to make it hard to guess tokens:
Long (harder to guess) tokens
Time limited tokens
IP based rate limiting for access to /remindpass/*

How to access to an external database securely?

I'm developing a mobile app which has to access to an external webapp (PHP + Codeigniter) to administrate the actions queried by ajax.
So by this way, there is a problem. If anyone see the urls used, could delete rows, or modify the user's info from the database. So I thought in this system to aboid this:
After a sucessful login I would do this:
// getToken : https://stackoverflow.com/a/13733588/2154101
$this->session->set_userdata('private_token', getToken(50));
$public_token = getToken(50);
$this->session->set_userdata('secure_token', md5("$private_token:$public_token"));
$data['token'] = $public_token;
// some stuff ...
// send $data in JSON
Then the client would the public token in the next query I would do this on the server:
$public_token = $this->input->post('token');
$data['token'] = get_public_token($public_token);
// some stuff ...
// send $data in JSON
Where get_public_token is within a helper with this code:
public get_public_token($public_token) {
$last_secure_token = $this->session->userdata('secure_token');
$private_token = $this->session->userdata('private_token');
$actual_token = md5("$private_token:$public_token");
if ($actual_token === $last_secure_token) {
$public_token = getToken(50);
$this->session->set_data('private_token', getToken(50));
$this->session->set_data('secure_token', md5("$private_token:$public_token"));
return $public_token;
} else { // you are cheating me ...
$this->session->sess_destroy();
redirect('/');
}
}
So only the user of this session could modify the data of the database.
I'm just trying to do the same explained here: https://stackoverflow.com/a/17371101/2154101
The session are encrypted, and I store them in a database too.
Do you think this method will work ok? Am I missing something important?
You should create an API for your mobile application. Create a authentication mechanism.
If your database holds user specific data, then you should create account for each user. So if the user sniffs the network and tries to call the api manually, then he could only change he's own data.
There are some API libraries for php out there, you should look into that.
Actually your solution is doing more than necessary. The only token of interest is the public_token sent back and forth. So you can throw away private_token and secure_token from session data, keeping only public_token for checking. Your current check is something like (X + 5)/2 == (14 + 5)/2 (is [received_token + 5]/2 equal to [14 + 5]/2 ?) when you can simplify to X == 14.
However if someone is sniffing the network, he can get the last token sent to a client and use it to hijack into that session. He can execute anything while the original client doesn't send a request with the outdated token, killing the session.
A better solution would be creating a secure_key after login and keep it at both ends (client and server). Then server would keep sending a new public_token at each response, but the client would send a md5(secure_key + public_token) at requests. This would narrow even more the hijacking window to the exact point where the session started. Without the original key, attackers can't create a valid md5.
However we are talking about minor hacking fans here. Anyone more zealous could hack that anyway. If you are concerned about that, then throw away all that stuff and simply use a HTTPS connection. With a trusted connection your sessions and access control rules are protected.
The better way is create API using SOAP or SAML2.
OAuth can be a very good solution: http://oauth.net/. It takes care of token and has a very secured API! If you wish to support secure authentication of web application + mobile application then it can be a good/proven solution!
On the other hand, it really depends on how complex your current system is and how the system is going to be in future.

How to encrypt a website cookie

I have read about users being able to manipulate website cookie and use it to exploits security loopholes. I did a search and came across an idea posted online. Here is the code below that is, after the username and password of the user are authenticated;
$Separator = '--';
$uniqueID = 'jhlhgjh12u0#345';
$Data = $userID.' '.md5('65748');
$expire=time()+60*24;
setcookie('verify-user', $Data.$Separator.md5($Data.$uniqueID), $expire);
The code above will set the cookie using a uniqueID, the userID, a MD5 hash numbers and a separator. The uniqueID, md5 hash numbers and separator are set by the developer. The idea is that a user won't be able to manipulate the cookie because the don't know the UniqueID, and the md5 hash numbers. The code below is used to test each cookie if they are manipulated or not
if ($_COOKIE) {
$Separator="--";
$uniqueID = 'jhlhgjh12u0#345';
$Cut = explode($Separator, $_COOKIE['verify-user']);
if (md5($Cut[0].$uniqueID) === $Cut[1]) {
$_COOKIE['verify-user'] = $Cut[0];
} else {
echo "fake cookie";
}
}
else {
echo "fake cookie";
}
I am wondering if this method is security tight or if there are loopholes too. criticism and corrections are welcomed
This is known as message signing. You hash the message together with a secret and attach that "signature" to the message itself. This allows the recipient to verify that the creator/signer of the message is in possession of the secret, without revealing the secret itself.
The problem with your particular implementation is that
the secret is too small
the hashing algorithm is unsuitable for the task
the cookies never change and never expire; if a cookie is stolen there's no recourse
You should use a longer secret, the longer the better. You should also use a hashing algorithm that is suited for the task, namely something like HMAC (hash-based message authentication). E.g.:
hash_hmac('sha512', $data, $secret)
You can see an implementation of a similar thing, including expiration of values, here.
The most important thing though: think thrice about whether a signed plain text message is the best way to go here in the first place. Perhaps you want a session-like system, in which an entirely meaningless random string is used as an id for data that is stored on the server. This completely eliminates the problem of users manipulating the cookie.

Using a PHP multidimensional array as a database

I'm building a private CMS for my own use and am at the point where I will start building out the username and password storing features. I am considering the possibility of storing all admin username, password, and user details in a multidimensional array within a PHP file, rather than using SQL to store them in a database.
My reason for wanting to use this non-traditional approach of storing user info is the belief that this will make it harder for attackers to gain unauthorized access to user info (usernames, passwords, IP addresses, etc.), because I will not be connecting to a MySQL database.
Rough Outline of Code:
add_user.php
// set the last referrer session variable to the current page
$_SESSION['last_referrer'] = 'add_user.php';
// set raw credential variables and salt
$raw_user = $_POST['user'];
$raw_pass = $_POST['pass'];
$raw_IP = $_SERVER['REMOTE_ADDR'];
$salt = '&^${QqiO%Ur!W0,.#.*';
// set the username if its clean, else its false
$username = (is_clean($raw_user)) ? $raw_user : false; // is_clean() is a function I will build to check if strings are clean, and can be appended to an array without creating a parsing error.
// set the salted, sanitized, and encrypted password if its clean, else its false
$password = (is_clean($raw_pass)) ? $salt . encrypt($raw_pass) : false; // encrypt() is a function I will build to encrypt passwords in a specific way
// if username and password are both valid and not false
if( $username && $password ) {
// set the users IP address
$IP = sanitize($raw_IP);
// create a temporary key
$temp_key = $_SESSION['temp_key'] = random_key();
// random_key() is a function I will build to create a key that I will store in a session only long enough to use for adding user info to the database.php file
// add user details array to main array of all users
$add_user = append_array_to_file('database.php', array($username, $password, $IP));
// append_array_to_file() is a function I will build to add array's to the existing multidimensional array that holds all user credentials.
// The function will load the database.php file using cURL so that database.php can check if the temp_key session is set, the append_array_to_file() function will stop and return false if the database.php file reports back that the temp_key is not set.
// The function will crawl database.php to read the current array of users into the function, will then add the current user's credentials to the array, then will rewrite the database.php file with the new array.
// destroy the temporary session key
unset($_SESSION['temp_key']);
}
else {
return false;
}
database.php
$users_credentials = array(1 => array('username' => 'jack',
'password' => '&^${QqiO%Ur!W0,.#.*HuiUn34D09Qi!d}Yt$s',
'ip'=> '127.0.0.1'),
2 => array('username' => 'chris',
'password' => '&^${QqiO%Ur!W0,.#.*8YiPosl#87&^4#',
'ip'=> '873.02.34.7')
);
I would then create custom functions to mimic SQL queries like SELECT for use in verifying users trying to log in.
My Questions
Is this a bad idea, and if so, why?
Am I correct in thinking that this will reduce the number of possibilities for hackers trying to gain unauthorized access, sniff/steal passwords, etc., since I'm not connecting to a remote database?
I don't see any advantage: Whether you use a text file, a mysql database or a php file ( === text file), they are all "databases" in the sense that they are files where you store your information. The difference is that an sql database is made for that stuff;
I do see disadvantages as there are more potential holes you would have to think about. Some examples (apart from the stuff mentioned in the comments):
You need to take care that the password file is always out of the web-root in case php dies on you;
You need to avoid passing around your password file in for example source control.
These are not things that are hard to solve, but using a normal database you don't even have to worry about them.
Apart from that are misunderstanding the purpose of the salt: If you just prepend it to the encrypted password, there is really no point in using a salt, you need to send it to your encrypt function to hash it with your text-password so that rainbow tables would have to be generated for each password instead of just one for your whole database. And for that reason you should also not use a single salt for all your users, each should have a different, unique salt.
If you plan to store any kind of config data in a text file of any sort, as opposed to a traditional database, consider using an .ini file. If I'm not mistaken, you can also take advantage of storing it outside of your web root, just like the php.ini file.
Here's a great post explaining exactly how to go about this: Using ini files for PHP application settings
PHP Manual: get_cfg_var()

Categories