I feel a bit anxious to put my database username, password and encryption keys into a PHP file that everyone who hacked himself into the server could easily read. Is it maybe possible to load PHP and tell it the passwords it might need upfront?
My question is simple: Is there any other way to let PHP know about the DB password and key other than to put everything into one file?
Any ideas are welcome.
To my knowledge you will have to declare your variables at some point in order to initiate a DB connection. You can, however, do certain things to make it more difficult for someone to read it.
In addition to safe programming practices mentioned in another answer, you can be sure to only store your passwords in one file (such as Config.php) and include it in your database connection function. Moreover, you can store this variable outside the web root, meaning that people will not be able to access it unless they have server access.
You can also make it more difficult to be read for someone who gains access. This includes encrypting it with a Symmetric Key Algorithm, and then decrypting it when used. Note that the encryption/decryption key will have to also be stored on the server which, again, make this little more than an inconvenience.
Take proper security steps in securing your code, and restrict your database so it can only be accessed locally. This should provide ample security.
Technically speaking, nobody cant see your php scripts. Anyways, I just updated my DB macros to the newest standards. This is how I do things in my CMS:
config.php file:
<?
if (!defined('A52CMS')) {die('What are you looking for? 0.o');}
$config_mysql = array(
'database' => 'databasename1',
'user' => 'username1',
'password' => 'password1',
'table_prefix' => 'a52_'
);
db_macros.php file:
<?
if (!defined('A52CMS')) {die('What are you looking for? 0.o');}
class DB {
protected $default_config = array(
'server' => 'localhost',
'database' => '',
'user' => 'root',
'password' => '',
'table_prefix' => 'a52_',
'mysql_config_array' => 'config_mysql'
);
function __construct ($config_mysql) {
$this->config = array_merge($this->default_config, $config_mysql);
/* There are some secret connection related stuff here */
mysql_query("SET NAMES utf8");
unset($this->config['password']);
unset($GLOBALS[$this->config['mysql_config_array']]);
}
/* There are alot of mysql related functions here */
function __destruct () {
mysql_close($this->conn);
if (#mysql_ping($this->conn)) {
CriticalError('Closing mysql connection was unsuccessful!');
}
}
}
core.php file:
<?
if (!defined('A52CMS')) {die('What are you looking for? 0.o');}
require('config.php');
// Lets try to display the mysql raw data
print_r($config_mysql); // displays something
require('db_macros.php');
$DB = new DB($config_mysql);
// Lets try to display the mysql raw data AGAIN
print_r($config_mysql); // displays nothing
index.php file:
<?
define('A52CMS', true);
require('core.php');
// This where the content starts.. if some plugin is being included, then mysql data is already unset, so basically you cannot hack them..
You might get some ideas how to make your applications little bit more secure. Also you might get some new ideas on how to contruct your custom mysql class (if you havent done so already.)
make secure scripts. If your scripts are secure, there's no way to get hacked
make your scripts which contains password(s) only apache-readable
make sure access to your password files are out of web scope
I did not even see another method to "save" password(s) than these
Related
I want to cache the API responses so that number of requests to the API server are reduced. API's are written in PHP using zend framework.
My approach: I created a redis cluster and I used phpfastcache to connect to redis cluster. Using phpfastcache, we can only set the expiry time for the cached response.
Whenever the response is updated before expiry of cache, we get the older response with the approach mentioned above. Desired thing is that whenever response is updated, old cache must be cleared and new cache must be written with same key.
I have attached a sample script that I used.
It would be great if anyone can provide me solution for this.
Thanks in advance.
Code:
<?php
// phpfastcache is a package used for caching
use Phpfastcache\CacheManager;
use Phpfastcache\Drivers\Redis\Config;
require //path for composer autoloader;
#InstanceCache must be global
$InstanceCache = CacheManager::getInstance('redis', new Config([
'host' => 'IP_address',
'port' => 6379,
'password' => //password
'database' => //db_name
]));
public function function_name(parameter){
$key = "unique_name";
$CacheString = $InstanceCache->getItem($key);
if(is_null($CacheString->get())){
$sql="SELECT * FROM employees";//sql query for function_name
$res=$this->db_query($sql);
if($this->db_num_rows($res)==0):
$this->db_free_results($res);
else:
$row = $this->db_fetch_object($res);
$this->db_free_results($res);
endif;
$CacheString->set($row)->expiresAfter(/*time*/);
$InstanceCache->save($CacheString);
echo $CacheString->get();
}
else{
echo $CacheString->get();
}
}
?>
Like I told you on Github, I think you misunderstood the concept itself of caching.
The concept caching means that you cache your data for a desired TTL.
If you need the most fresh data then you must re-fetch from source (your database here ).
Cache is not means to be dynamic, it's means to be static and to help you cool down the request on your backend.
So in your case, just fetch from source without caching and it'll be good. It does not make any sense to ask Phpfastcache interrogate your database each time then compare the data to the cached data to check if your database data are fresher.
In fact the cost in time of the whole operation will be longer than only fetching from the source.
I am going to write scripts to make backup of mysql database using codeigniter.
However, here is one tricky issue.
I should convert all email into dummy letters not real email information.
Ex, Jon#gmail.com => aBdsEDd#test.com
So, the new dev can use all data, but not real email because of they are for real person's information.
But I am not sure It is possible or not.
Here, I dropped my approach, but can not change current database.
$this->load->dbutil();
$this->load->helper('file');
$this->load->helper('download');
$dumpOption = array(
'tables' => array(), // Array of tables to backup.
'ignore' => array(), // List of tables to omit from the backup
'format' => 'txt', // gzip, zip, txt
'filename' => 'mybackup.sql', // File name - NEEDED ONLY WITH ZIP FILES
'add_drop' => TRUE, // Whether to add DROP TABLE statements to backup file
'add_insert' => TRUE, // Whether to add INSERT data to backup file
'newline' => "\n" // Newline character used in backup file
);
if(ENVIRONMENT !== 'production') {
if ($this->dbutil->database_exists('db'))
{
$backup = $this->dbutil->backup($dumpOption);
try {
echo 'loading database .....';
write_file('./mybackup.sql', $backup, 'w+');
echo 'finish preparing dev data.';
} catch(Exception $e) {
printf('Message: ' .$e->getMessage());
}
} else {
echo 'database does not exist.';
}
}
Would you let me know If it is possible?
Most important thing is that the dump file should have updated content but we should not change original database, changes should be on dump only.
Is it possible?
Thank you very much.
Strictly speaking, then answer is no, you cannot change the contents of the data this way, since the backup method takes a list of tables and dumps those to a file as they are. Well, this is exactly what you would expect from a backup solution.
What you could do, however, is to create views that incorporate the replacement logic you require. Then you can use the backup method to back the views up, not the tables.
Otherwise, you need to write the logic either in plain queries or implement it in php code and create the dump files yourself.
Trying to change the data in-flight like this is going to be some blend of "inadvisable" and "impossible". What I do is first clone the DB to a staging location, and then run data scrub scripts for PII and other sensitive information. Then you can make that available to clone to dev environments.
However, for values like the email address that may be used in relations or otherwise be required to be unique you will want to take extra care, as we found out the hard way, when you change all the emails to example#example.com properly testing anything relating to that information becomes virtually impossible.
For something like this I would suggest deriving a dummy email from the real one, eg:
UPDATE users
SET email = CONCAT(
SUBSTRING(MD5(email),1,16),
'#',
SUBSTRING(MD5(email),17,16),
'.com'
);
Result:
foo#bar.com -> f3ada405ce890b6f#8204094deb12d8a8.com
bar#foo.com -> dc8a42aba3651b0b#1f088ef928ff3b1d.com
However, as your DB grows cloning like this becomes more and more of a problem, particularly for local development, as the DB size will likely only grow over time. As it stands our prod DB is approaching 300GB and some of our devs' machines cannot fit a copy in addition to all of their other requirements. Plus the cloning and scrubbing takes hours, and now with everyone remote it poses additional logistical problems to get those clones out to devs over already-constrained links.
The best solution, particularly for local dev, is to only dump the schema of the database and fill it with a representative set of mocked data for testing. For full-blown tests we maintain full-copy, scrubbed versions of the DB in our CI/CD pipeline environments.
How to securely authenticate a user without using any type of database.
authenticate.php?username={$_GET['username']}&password={$_GET['password']}
if ($_GET['username'] == "secret_username" && password == "secret_password")
{
$_SESSION['user'] = $username;
header("Location: password_protected_page.php");
exit;
}
This method seems to be an option. Is it secure?
Use a file to hold your data.
have a users.txt below your public html like so:
username:hashedpassword
then you use fopen
<?php
$filename = "/home/users.txt";
$file = fopen( $filename, "r" );
$display = fread( $file, filesize( $filename ) );
fclose($file);
?>
Then explode it by newline and then |, then check if the first is equal to username and the second is equal to md5(password).
Seems like the easiest way to me...
I would at least post for authentication but otherwise it should work fine.
Definitely, you can do that. But, just use POST.
There is nothing wrong with the process. Even when we use database, we actually do the same thing but just using some select command.
You might be thinking about password hash, but they are used so that, even if the 3rd party gets a hold of database dump(somehow), they can never actually decrypt the password, as hash are one way function. Now in you case you are not using database, so that's not a problem.
However the problem lies in scalability. Are you sure that there will always be just one user of the system. If yes, then its okay, else go for DB.
I learned PHP on my own. I never took a course nor had a mentor. I had issues "getting" datasbase calls, as they seemed so convoluted compared to other PHP which seemed natural. I started using this a long time ago.
You can create a text file (username.php) in a directory OFF the web server accesible folders.
(consider permissions!)
So you have /root/users and in that folder you have (by username)
/root/users.Joe.php
/root/users/Juan.php
/root/users/Tim.php
Tim.php contents
<?php
$userpath='/var/www/html/users/Tim';
$password='Timspassword';
?>
Now when Tim logs on wee have code that does this:
<?php
include '/root/users/'.$_POST[username].'.php';
if ($password == $_POST['password'])
{
$_SESSION['loggedin']='yes';
$_SESSION['expire']='<how much time you need?>';
}
?>
This way you can more easily create new users . BTW I use an index.php in each users folder that will do very little if not logged in as that particular user that matches the name of the folder. You should also use https. You could also use password encryption/decryption in these user passwords.
Truth be told, Database injection is a real vulberability. Daily I get hackers looking for databases on my sites. THERE ARE NONE, so they go away.
no databese required.
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()
Ok, i'm wondering if this code stored in user_login.php could be improved or if i'm doing it wrong. I'm confused because all my script in the application are long about 30-40 lines only and i was wondering if i am missing something.
This script is called with an ajax call like everyone else in my application except for template files.
<?php
# Ignore
if (!defined('APP_ON')) { die('Silence...'); }
# Gets the variables sent
$user_name = post('user_name');
$user_password = extra_crypt(post('user_password'));
# Check if the user exists
if (!user::check($user_name, $user_password)) { template::bad($lang['incorrect_login']); }
# Logging in
$id = user::get_id($user_name, $user_password);
$u = new user($id);
$u->login();
template::good($lang['correct_login']);
?>
I'm going to explain it:
# Ignore
if (!defined('APP_ON')) { die('Silence...'); }
This basically check that the file is not called directly. Each url is redirected to an index.php file that manage the url (es: www.mysite.com/user/view/id/1) and include the right file. In the first lines of this index.php file there is a define('APP_ON', true);. The index file also initialize the application calling some set of functions and some classes.
# Gets the variables sent
$user_name = post('user_name');
$user_password = extra_crypt(post('user_password'));
The function post() manage the recovering of $_POST['user_name'] making some checks.
The function extra_crypt() just crypt the password using sha1 and a custom alghoritm.
I'm using prepared statement for sql queries so don't worry about escaping post variables.
# Check if the user exists
if (!user::check($user_name, $user_password)) { template::bad($lang['incorrect_login']);
The user class has both static and normal methods. The static ones doesn't require an id to start from, normal methods does. For example user::check(); does check if the username and the password exists in the database.
The template class has just two static methods (template::bad() and template::good()) that manage fast dialog box to send to the user without any header or footer. Instead if you instantiate the class $t = new template('user_view'), the template user_view_body.php is called and you can manage that page with $t->assign() that assign static vars to the template, or with $t->loop() that start a loop etc.
Finally $lang is an array having some common strings in the language set by the user.
# Logging in
$id = user::get_id($user_name, $user_password);
$u = new user($id);
$u->login();
template::good($lang['correct_login']);
At the end we have the actual login. The user class is instantiated and an id is recovered. The user with that id is logged in and we return a template::good() message box to the user.
For any clarification write a comment above.
First of all a message digest function like sha1 is not an encryption function. So please remove crypt from the function name to avoid confusion. Further more, this whole idea of a "custom algorithm" for storing passwords scares the hell out of me. sha256 is a better choice than sha1, but sha1 isn't all that bad after all its still a NIST approved function. Unlike md5 no one has been able to generate a collision for sha1 (although this will happen in the new few years). If you do go with sha1 make sure the salt is a prefix, as to thwart the prefixing attack.
Input validation must always be done at the time of use. the post() function should not be responsible for any input validation or escaping. This is just an incarnation of magic_quotes_gpc which is being removed because its a security train wreak.
Parametrized Query libraries like PDO and ADODB are very good and I recommended using it. This is a great example of sanitizing at the time of use.
Your template assign() method can be used to sanitize for XSS. Smarty comes with a htmlspecialchars output filter module which does this.
With so much functionality behind functions that you haven't provided, it's hard to say whether anything needs to be fixed. Are you making sure the input is clean in post()? Are your database calls secure in user::check()? Are you using session cookies to simplify user prefs/info storage? Is your template class well-written? When you create a new user, are you logging all the necessary information (time of login, IP address if necessary, etc)? Are you ensuring that the user isn't doubly logged in, if that's important here?
The only concrete thing I can tell you about what you wrote is that the sha1 algorithm is completely broken, and you should be using something else instead; see the comments below for suggestions.