Related
What is the best approach to storing a group of global settings for a custom PHP application? I am working on a personal project (first major one really), and need a method of storing key-value pairs for recording overall settings for the application.
Things to store as...
Website's Global Name.
Theme (just a variable, or path to theme)
etc
Should I just keep them in one table? If so what is the best way to query them from a boostrap? Besides doing a single query for each desired setting.
UPDATE:
Yes a .ini or parsing an include file would be nice, and I know how to do it that way. But I wanted to know what would be the best approach to storing them in MySQL with everything else.
UPDATE2:
The reason I ask this also is I plan for a lot of these settings to be changeable through the Administrator interface. So if you were to change the Title of the site, it would be updated right away, which I figured would be best to do through SQL, thus needing setting inside the DB.
For a single, small, simple site, I'd just put config in a PHP file. Keep it simple. PHP probably doesn't parse anything faster than it parses PHP. If you use APC, the compiled bytecode is even cached -- although the bytecode is then re-executed for every request. For a small config file, this bytecode execution should take very little time; for a very large file, it might take a bit longer.
For high-traffic sites with large configs, caching your config data in APC (e.g. as a single array) is a good idea -- at the very least, you save the overhead of actually executing the statements in your config.php file. Notably, facebook does this. When you're serving many requests per second, hitting the disk to read a config file (using parse_ini_file, an XML parser, etc.) on every request is out of the question.
For my current project, we host many sites, each with their own config. Each site had both a database and a config file; however, making sure you're always using the right config file with the right database can become a headache. Additionally, changes would require changing things in two places -- the db and the config. Forgetting one or the other always caused problems, and it happened far too frequently.
We moved the config into the database, so that you can't possibly separate a db from it's correct config, and any code changes only require updating the database. The data from the config table is also aggressively cached in APC, so we query it rarely.
So, to recap:
Small site: just use a config.php file
Very large site: cache in APC
Multiple sites: store config in database to reduce administration overhead; cache in APC to reduce database hits
Have you thought about putting them in a .php file and including it on the pages you need to use them? Give the variables a unique name so avoid naming conflicts.
Since you'll be using them repeatedly in your PHP application, this would be most ideal. This also avoids the need to make database calls if you were to store them in a database.
AppSettings.php
<?php
$_SITENAME_ = 'MyWebsite';
$_THEME_ = 'Theme/Path';
?>
UPDATE:
I assume you want these settings to be editable via a web page and don't want multiple DB Queries since these settings will change, but not too often?
One approach I personally took was to serialize the AppSettings table and store it in a XML file. Of course, every time the table is updated, the table would be reserialized and stored in the XML file. Then I created a separate class that parses the XML file and returns the specific values I needed.
We just use
$siteConfig['db_name'] = 'database1';
$siteConfig['site_name'] = 'Stackoverflow';
In a included php file. Putting the values in a array helps with name conflicts.
i understand you want to keep things in a mysql table, however, that likely means storing required configuration in multiple places. for example, i'm sure you'll want the database server and name stored in a string somewhere. that means putting those in an include or .ini file since you can't read them from a database (how can you connect to the database without knowing those things). so, you'd be keeping the db connection info in an include or .ini file and the rest of the settings in the database? that works, i suppose, but i like to keep all of the settings in one file (config.php or application.ini or whatever). it makes it easier to maintain imo.
-don
Just got done chatting with a few people on IRC about this. I looked at how Wordpress handled this after I pulled up a SQL dump of one copy. I think I'll use this layout and rename the columns a bit. But the idea is...
option_id | option_name | option_value | autoload
int | varchar | longtext | varchar
(PRIMARY) | (UNIQUE) | |
I liked Microsoft.Net's web.config ConfigurationManager.appSettings and how that worked. So I mimicked it and kinda made it better. It
Works in any environment without having to swap files (which I always forget, especially when deploying on Friday at 4:59 p.m.)
Is self documenting on what the function call is
Can handle global settings with *
<?php
namespace Library {
// the config depends on the environment, and the environment depends on the website url
class Configuration {
private static $environment;
public static function GetEnvironment(){
if(empty(Configuration::$environment)){
// returns 'dev' or 'prod'
switch($_SERVER['SERVER_NAME']){
case 'innitech.com':
Configuration::$environment = 'prod';
default:
Configuration::$environment = 'dev';
}
}
return Configuration::$environment;
}
private const settings = [
"dev" => [
'dbserver' => 'localhost',
'database' => 'mydb',
'dbuser' => 'myuser',
'dbpassword' => 'mypass',
'dbdebug' => false,
'trace' => true
],
'prod' => [
'dbserver' => 'sql1.innitech.com',
'database' => 'blahinc',
'dbuser' => 'proddb',
'dbpassword' => 'ButIWasToldiDgETaStApLer',
],
'*' => [
'adminemail' => 'admins#innitech.com',
'adminphone' => '123456789',
'dbdebug' => false,
'trace' => false
]
];
public static function Setting($name){
return self::setting[self::GetEnvironment()][$name] ??
self::setting['*'][$name];
}
}
}
?>
Usage
$conn = new mysqli(Configuration::Setting('dbserver'), Configuration::Setting('dbuser'), Configuration::Setting('dbpassword'), Configuration::Setting('database'));
What has mentioned before, is true. I like the one more than the other, but I mostly use another way of storing my configuration.
I never use a database as the place to store my settings, because that would create a lot of data transfers, which can make the application a little more insecure- in my opinion. Besides, some application hosts (like Amazon's AWS and Google's Cloud Platform) limit the read/write actions to a database.
Therefore, I mostly use this method:
Firstly, I create a file config/settings.php with the following contents:
<?php
return [
'database' => [
'host' => 'localhost',
'port' => 3006,
'user' => 'username',
'password' => // your secret password
],
'application' => [
'name' => 'Your site\'s name',
'version' => '1.0-dev'
]
]
When you want to use this in you index.php file, add the following line in it:
$config = include('./config/settings.php');
I hope this can add some information for you or others.
I generally within my index.php file set up the "required" settings so:
<?php
session_start();
ob_start();
define('BASEPATH', $_SERVER['DOCUMENT_ROOT'].'/_setUp/siteSetup/'); // CHANGE TO THE PATH OF THE SITE.
define('URIPATH', 'http://localhost/_setUp/siteSetup/'); // CHANGE TO THE URL OF THE SITE.
define ('DEBUGGER', true); // CHANGE TO FALSE TO HIDE DEBUG MESSAGES
include(BASEPATH.'system/lib/config.lib.php');
?>
and within my config file:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// public
/*
example:
<img src="<?php echo IMG ?>my_image.jpg">
http://localhost/public/images/
<img src="http://localhost/public/images/my_image.jpg">
*/
define('CSS', URIPATH.'public/css/'); // DEFINE DIR: css
define('IMG', URIPATH.'public/images/'); // DEFINE DIR: images
define('JS', URIPATH.'public/scripts/'); // DEFINE DIR: scripts
// system
define('INC', BASEPATH.'system/includes/'); // DEFINE DIR: includes
define('LIB', BASEPATH.'system/lib/'); // DEFINE DIR: lib
define('SQL', BASEPATH.'system/sql/'); // DEFINE DIR: sql
if (DEBUGGER) {
ini_set('log_errors',TRUE);
ini_set("error_log", BASEPATH.'system/'."error_log.txt");
}
else {
ini_set('log_errors',TRUE);
ini_set("error_log", BASEPATH.'system/'."error_log.txt");
}
$db_info = array(
'host' => 'localhost',
'username' => 'root',
'password' => 'root',
'database' => 'my_db'
);
/*
to use:
$db_info = unserialize(DB_INFO);
echo $db_info['host'];
echo $db_info['username'];
echo $db_info['password'];
echo $db_info['database'];
*/
define('DB_INFO', serialize($db_info));
?>
A decent approach would be to fetch commonly used settings once per page, via database. Something like keeping a autoload bool field that checks whether the setting should be loaded with the page. For other, much less commonly fetched settings, you can fetch them over the air.
If you decide to cache them all instead of fetching for every page, you might want to think of a way to notify the script to reload the settings -- or you'd have to manually tell it to do so, so you wouldn't get stuck with old settings after changing some.
I'm working with a system that does store its settings in the database.
My advice in short: Do not do it!
Storing the settings in the database means, whenever we have to move the database, e.g. from production to development, we also have to update all the settings, or the dev system might start sending e-mails (did happen--made front-page news...) or interact with production systems (also happened--saved by backups...)
So, no, never store the configuration in the database!
When you store the settings in a file, local to the environment (dev, test, prod) you can move the database around at your leisure, always assured the settings will be picked up from the file in the respective environment.
Update: Giving it some more thought I'd probably go for a combination of a table (non-lethal information without server info or integration info or anything else that will kill you if it isn't environment specific) and a .ini-file (or several).
The rule would be that a key in a .ini-file would always override anything stored in the table (to prevent above disasters, maybe even make that key "read-only" from any UI).
If you want to get extra fancy you might even add value types; boolean represented as a checkbox, dates with a date selector, even select boxes with separated option values, and of course ints that would have to be numbers even if the table might store them as strings.
Then I'd look into using some form of memory-based caching if reading the settings got slow.
I know Magento stores the database connection details within the local.xml file, however our firm is trying to avoid passwords and other sensitive data being stored within our git repo's for security purposes.
I know you can create Environment Variables easily via an .htaccess file, but I'm hoping to find a workable solution that will enable me to set this database information dynamically from a environment variable.
As the local.xml is an XML file and as this is a non dynamic/server-side filetype we cannot use it to read environment variables.
Would there be a way to somehow add in some hook/custom behaviour to Magento in which I could replace the local.xml with a PHP file that will allow me to pull in these environment variables?
So in a sense, the local.XML would become a local.PHP file with the ability to read my own custom environment variables such DB_HOST, DB_USERNAME, DB_PASSWORD rather than having them already set in the xml file as localhost, root, password123 etc.
Any ideas on how best to achieve this, or are there any existing Magento add-ons/extensions/mods that will allow me to do this?
I would suggest git ignore your local.xml and dynamically create it with your deploy script. your deploy script should have your sensitive data variables.
I found an alternative solution to the problem. I extended Mage_Core_Model_Config_Element and overrode the 'xmlentities' function to check if the configuration value it is returning starts with a dollar sign, and if so substitute it with the equivalent environment variable.
If it helps anyone else, here it is...
https://github.com/rossigee/magento-config-envvars
Please try this solution:
copy app/code/core/Mage/Core/Model/App.php to app/code/local/Mage/Core/Model/App.php and replace the _initBaseConfig() method with the following one:
protected function _initBaseConfig()
{
Varien_Profiler::start('mage::app::init::system_config');
$this->_config->loadBase();
/* Read DB connection config from environment variables */
$connection = $this->_config->getNode('global/resources/default_setup/connection');
$connection->setNode('host', $_ENV['DB_HOST']);
$connection->setNode('username', $_ENV['DB_USERNAME']);
$connection->setNode('password', $_ENV['DB_PASSWORD']);
Varien_Profiler::stop('mage::app::init::system_config');
return $this;
}
This must help.
* EDIT
protected function _initBaseConfig()
{
Varien_Profiler::start('mage::app::init::system_config');
$this->_config->loadBase();
/* Read DB connection config from environment variables */
$this->_config->getNode('global/resources/default_setup/connection')
->setNode('host', $_ENV['DB_HOST'])
->setNode('username', $_ENV['DB_USERNAME'])
->setNode('password', $_ENV['DB_PASSWORD']);
Varien_Profiler::stop('mage::app::init::system_config');
return $this;
}
Have you considered simply adding local.xml to .gitignore and creating/updating it as part of your deployment process? Note that local.xml typically stores more than just database credentials. For example, it might also store the configuration for the caching backend(s) as and session storage. These are usually also server specific, and will make things very messy if you try to avoid using local.xml.
Where do you put the connection settings for a database connection (things like host, dbname, user, password)? Is it placed in the database class or file, or outside in a config file, or somewhere else?
Ideally, you should place it in a config file, which can be something as simple as a PHP array.
For example: db_config.php
$db_config = array(
'host' => 'localhost',
'user' => 'username',
'password' => 'qwerty'
);
You should then place this file outside your document root for maximum security. This way, if your webhost fails you and begins serving PHP files as text files (happens), no one can get your DB credentials.
It can be done in many ways, but what is common is to put it in a settings file, and keep that file outside of the webroot, so that information about the database password can not accidentally leak into the web.
For PostgreSQL, I really like to use pg_service.conf. It allows me to put all connection specific settings (hostname, database name, username, password, etc) into ~/.pg_service.conf or to /etc/postgresql-common/pg_service.conf and give them common name (service name).
Now, any program (Perl, PHP, etc) that wants to connect to database can simply specify "service=name" as their connection string - nice, clean, secure and easily maintainable.
As far as I know, MySQL has similar mechanism for ~/my.cnf or /etc/my.cnf files - you may want to look into that.
There are a lot of ways doing it, but I do it this way by defining CONSTANTS:
Create a config/db.php
<?php
define('DB_INFO','mysql:host=localhost;dbname=test');
define('DB_USER','root');
define('DB_PASS','');
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to secure database passwords in PHP?
Recently I was given a website project which was supposed to be done in PHP but I don't have much experience in PHP. Anyway, it is up and running but there is a big room to improve. The one thing that I am not feeling well is the way I am dealing with database: I put the database connection information in a separate db.php file and include it where needed. But I remember seeing PHP source files returned by the server many a time.
So now my question is: what is a better or the best way / place to put database sensitive data?
By the way, how NOT to let PHP show error messages on web pages when things are gone wrong? A custom error page or settings somewhere in php.ini? Thanks!
Note: I am using PHP in it's old flavor not object-oriented way. But I am open to object-oriented or MVC way if there are better approaches that way to prepare for future projects
I don't know if this is what you are looking for:
You can put your sensitive data in your db.php, but outside the web root directory (public_html or www).
For example, you could have a directory called config which is a sibling of your web root directory, and store your db.php file there.
You can include your db.php file like this:
require_once('../config/db.php');
I hope this helps.
Its fine to put it in a db.php file, just use require_once() just after the opening <?php tag of each document.
If basedir restriction is not in effect, move db.php file outside of your web/ftp root that way its definitely not accessible via http/ftp. Make sure permissions are set properly on this file though.
Since you aren't using OOP or an MVC structure for your code this is the best route to go.
I would personally create a file called db.php and place this above the public_html folder on your server
for example
<?php
error_reporting(0);
$link = FALSE;
$link = mysql_connect('hostname', 'username', 'password');
if ( ! $link)
{
die("Couldn't connect to mysql server!");
} else {
mysql_select_db('databasename');
}
?>
This turns off error reporting at the same time as connecting to your database, from your index.php you would include the file like so:
<?php require('../db.php'); ?>
I am working on my first PHP based website, and I was wondering what solutions were out there for a username/password system? I have tried using a .htaccess file for basic security, and while it works, I want something a little easier for a layman to administer. Are there any other solutions out there that I could try? I don't have a database server available, so it would have to support flat file databases...thanks!
Edit I have determined that I do have SQLite support, so I do have a database option available. Also, I feel I should mention a little further some requirements that I have. I originally looked to using .htaccess to protect my website, since I need security over the entire directory. Most of the files I am trying to protect are .pdf and .doc...any solution would have to allow me to secure those files as well as any web pages in the directory.
If I could find a good solution to more or less "skin" the .htaccess method of locking a directory, so that I could do things like have an actual login/register page, etc. then I would just stick to the .htaccess method. I would however like something that is more manageable, I just need the directory security.
I wrote up this code quickly, it is syntacticly correct but I have not tested it.
There are 2 things that I did not do here, first, I did not provide a function to remove a user and second I did not provide a function to change a users password, these you'll have to write yourself.
However this should provide for a good place to start.
These functions will store your usernames/passwords in a file called passwords in the following format
username0:password0
username1:password1
username2:password2
...
.
function authenticate($username, $password)
{
//ALWAYS use a salt to secure the encryption of your passwords, this can be any value of any
//length, the longer and the more characters the better
//I like to use a "perfect password" from Steve Gibbson's https://www.grc.com/passwords.htm
//This must the exactly the same as the salt in theaddUser() function
$salt = 'voDeaFWckErOPPGwiapYBwEoc4O2d1M60m2QsYc7A15PUshrLamoVioG1wUmEgF';
//First we need to get the contents of the file that has the usernames/passwords in it.
//we don't want to use fopen() or we may end up with a locked file error if another access is
//attempted before we've closed it.
//this line will get the contents of the file named passwords and store it in the $fh variable
$fh = file_get_contents('passwords');
//Now lets take the file and split it into an array where each line is a new element in the array.
$fh = split("\n", $fh);
//Now lets loop over the entire array spliting each row into it's username/password pair
foreach($fh as $r)
{
//Every time this loop runs $r will be populated with a new row
//Lets split the line into it's username/password pairs.
$p = split(':', $p);
//Since we don't need all the usernames/password to be in memory lets stop when we find the one we need
if($p[0] == $username && $p[1] == sha1($salt . $password))
{
//We've found the correct use so lets stop looping and return true
return true;
}
}
//If we've reached this point in the code then we did not find the user with the correct password in the 'database'
//so we'll just return false
return false;
}
function addUser($username, $password)
{
//ALWAYS use a salt to secure the encryption of your passwords, this can be any value of any
//length, the longer and the more characters the better
//I like to use a "perfect password" from Steve Gibbson's https://www.grc.com/passwords.htm
//This must the exactly the same as the salt in the authenticate() function
$salt = 'voDeaFWckErOPPGwiapYBwEoc4O2d1M60m2QsYc7A15PUshrLamoVioG1wUmEgF';
//We need to parse out some preticularly bad characters from the user name such as : which is used to seperate the username and password
//and \r and \n which is the new line character which seperates our lines
$username = preg_replace('/\r|\n|\:/', '', $username);
//Now lets encrypt our password with the salt added
$password = sha1($salt . $password);
//Lets build the new line that is going to be added
$line = $username . ':' . $password . "\n";
//Lets open the file in append mode so that the pointer will be placed at the end of the file
$fh = fopen('passwords', 'a');
//Write the new entry to the file
fwrite($fh, $line);
//Close the file
fclose($fh);
//Typicaly one would write a bunch of error handling code on the above statments and if something
//goes wrong then return false but if you make it this far in the code then return true
return true;
}
Have a look at Zend_Auth. It's open source, so you can sniff around to get a feel for how an authentication module should (or could) be implemented. From the doc:
Zend_Auth is concerned only with
authentication and not with
authorization. Authentication is
loosely defined as determining whether
an entity actually is what it purports
to be (i.e., identification), based on
some set of credentials.
Sure, there are plenty of flat file database PHP security systems available. Doing a quick Google search will pull up many results. Here is a tutorial:
http://www.devshed.com/c/a/PHP/Private-Pages-with-PHP-and-Text-Files/
Check if you have support for sqlite, it doesn't require a server so it might work for you.
And don't forget to hash your passwords. ;)
To check create a file (ex. php_info.php) add:
<?php
phpinfo();
Then upload the file to your host, load it in your browser (example.com/php_info.php) and do a search for sqlite.
You should see several references to sqlite in the page that shows if you have support. The line with "SQLite Library" will tell you the version of sqlite you have (if you have it).
Also once you are done you should delete the php_info.php file from your site, since it does give some information on your setup which can be helpful to crackers.
have you seen if you have SQLite available? It is PHP's built in database. If not you could just use read/write to a file hope this helps a bit
According to this page from the Apache website:
In general, you should never use .htaccess files unless you don't have access to the main server configuration file. There is, for example, a prevailing misconception that user authentication should always be done in .htaccess files. This is simply not the case. You can put user authentication configurations in the main server configuration, and this is, in fact, the preferred way to do things.
Its easy to see why this is so, too. Its far preferable to have centralized control, rather than digging through EVERY SINGLE DIRECTORY when debugging a faulty configuration.
I urge you to transfer your .htaccess file config to your main configuration file ASAP, for your own good!