117

I have a small PHP application storing data in a MySQL database. Currently username / password are hard-coded in the PHP code. A situation I do not really like, for example, since the code is also available in a repository.

The best idea I have is to move the data from the code to a configuration file (excluded from the repository), and somehow encode it, so is not directly readable (obfuscation). Is there any better and easy to use way to solve the issue?

$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) { 
    die('Could not connect: ' . mysql_error());
}
mysql_select_db('mydb');

Scope: I want to established a robust, but also easy-to-use solution. I want reasonable security, but I am not dealing with highly confidential data here.

Remark: It is no longer recommended to use the mysql_connect functions, see Stack Overflow question Why shouldn't I use mysql_ functions in PHP?*. I could have changed the code example, but since some comments refer to this, I did not. However, the original nature of the question remains valid.

Community
  • 1
  • 1
Horst Walter
  • 13,663
  • 32
  • 126
  • 228
  • 5
    +1 for nice question. – Yogesh Suthar Feb 26 '13 at 12:21
  • 7
    1) Use a config file, 2) add config.php to the repository ignore file, 3) stop using `mysql_*` (had to get it in there somewhere), as it's deprecated – Amelia Feb 26 '13 at 15:06
  • 1
    As of 3, you are right. I just added the 3 code lines as an example to clarify the question and did not pay proper attention to use the OO version instead. – Horst Walter Feb 26 '13 at 16:01
  • 4
    [**Please, don't use `mysql_*` functions in new code**](http://bit.ly/phpmsql). They are no longer maintained [and are officially deprecated](https://wiki.php.net/rfc/mysql_deprecation). See the [**red box**](http://j.mp/Te9zIL)? Learn about [*prepared statements*](http://j.mp/T9hLWi) instead, and use [PDO](http://php.net/pdo) or [MySQLi](http://php.net/mysqli) - [this article](http://j.mp/QEx8IB) will help you decide which. If you choose PDO, [here is a good tutorial](http://stackoverflow.com/a/14110189/1723893). – NullPoiиteя Feb 26 '13 at 18:53

9 Answers9

69

The easiest way is, like you said, to use a configuration file.

Many frameworks use this (Zend, CakePHP, Kohana, etc) and it's the most common way of doing things (even in a non-PHP environment such as ASP.NET with its web.config files). This allows you also to copy over configuration values from environment to environment by just copying the files for the site, which is a benefit over relying on server-setup environment variables (which can very quickly be lost and forgotten).

You shouldn't need to worry about obfuscation of the password since it's not a world-accessible file, it certainly shouldn't be web accessible. What I mean by this is that you would either a) Tell your web server not to serve your configuration file (IIS already does this with web.config files and serves a HTTP 404.8 status instead of the contents) or b) Move it outside of your web served directory. If somebody can see your configuration file, it's worse than having it in your source code.

It's also going to be a good idea to have a base (empty / default) version of the configuration file, and separate it out per environments, so that you could have a different configuration file for production, development, and testing platforms.

An environment variable is the most common way to differentiate between these environments, something like the below code:

// Check if it's been set by the web server
if (!empty($_ENV['ENVIRONMENT'])) {
    // Copy from web server to PHP constant
    define('ENVIRONMENT', $_ENV['ENVIRONMENT']);
}

if (!defined('ENVIRONMENT')) {
    // Default to development
    define('ENVIRONMENT', 'development');
}

// Load in default configuration values
require_once 'config.default.php';

// Load in the overridden configuration file for this environment
require_once 'config.' . ENVIRONMENT . '.php';

Another way that is pretty common is to use an XML configuration file and only read in the values that you need as appropriate (storing a cached copy of the config file in memory). This can very easily be restricted to only load in certain values, rather than allowing arbitrary inclusion of PHP files and is overall a better solution in my opinion, but the above should get you started in the right direction.

You'll probably want your VCS to ignore the file. On the other hand, you might want a skeleton of the file, or one with reasonable defaults (the latter does not apply to login data, of course), to be version controlled. A common way to deal with that is to have a checked-in template configuration file, and the installation procedure copies that file to the location of the real configuration file, where it is customized. This can be a manual, or an automated, process.

(Whilst somewhat unrelated to the main question, introducing a constant for your environment allows you to do some other cool stuff like deferring to a fake mail implementation instead of a live SMTP one, but of course this could also be done with a configuration file)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rudi Visser
  • 21,350
  • 5
  • 71
  • 97
  • This is pure opinion but I feel it should expressed here: I personally believe that constants should be avoided in favour of nice, injectable variables, thereby avoiding global state. Of course you can do this with constants as well, but in order to truly avoid global state you should be injecting them from the top scope down, rendering the constants pointless and variables are (again, IMO) syntactically nicer for config files. – DaveRandom Feb 26 '13 at 12:46
  • @DaveRandom Feel free to edit away if you think it's appropriate to provide another example/alternative? Personally I've always preferred XML configuration files that are cached, and don't see the constant/environment variable to be too "evil", since it's only one and is controlled by the host server, rather than a change in code per environment. All config files in my example are free to be deployed side-by-side, and the one used is chosen as appropriate. – Rudi Visser Feb 26 '13 at 12:49
  • 5
    Little note: I have seen companies that really split code and config. This means, the config is not even found in the project directory; instead it's somewhere in /etc/ in the linux filesystem. This might be useful to keep your production passwords a little bit more secure. – Sliq Feb 26 '13 at 12:50
  • 1
    @RudiVisser No what I was saying was more of a general comment about constants vs variables and the use of constants in anything other than the top scope implying global state. I can't find fault with your answer (you have a +1) it's just my opinion that's not directly related to the question but I thought worth mentioning with this answer. In your example (defining an `ENVIRONMENT`) I would probably be injecting dev mocks that implement the same interfaces as their production counterparts but with the desired alternate behaviour rather than littering code with environment checks. YMMV. – DaveRandom Feb 26 '13 at 12:56
  • @DaveRandom I fully see where you're coming from yes, this whole practice can be wrapped into something nicer, but as an overriding concept I still think that setting the environment constant on the physical server is pretty top level and a nice way to get information in without a code change! Your comments are somewhat related too, so like I say if you want to edit any of these notes in, feel free to do so :) – Rudi Visser Feb 26 '13 at 13:00
47

One pretty nice solution, if you are on Apache, it to store the information in the virtualhost configuration

SetEnv  MYSQL_USER     "xx"
SetEnv  MYSQL_PASSWORD "y2wrg435yw8"

The data can easily be fetched using $_ENV[] for use in the code.

hank
  • 3,748
  • 1
  • 24
  • 37
  • 2
    this is how some cloud hosts do it, too – nurettin Feb 26 '13 at 14:32
  • 2
    Yep, Adam Wiggins (Heroku co-founder and CTO) mentions storing creds in env. vars in 12 factor app methodology here: http://www.12factor.net/config Though, that isn't vhost config specific. – Gary S. Weaver Feb 26 '13 at 16:02
  • 2
    $_ENV['MYSQL_USER'] didn't work for me, had to use getenv('MYSQL_USER') - php.net/manual/en/function.getenv.php – jobwat Apr 28 '14 at 21:42
  • Having the pass in config file outside root, is safer that exposing it to getenv() that could be injected and executed from anywhere in the website. – DeepBlue Jan 22 '17 at 22:22
  • Just make sure that users can't access the site configuration files. – frodeborli Jul 03 '18 at 09:24
11

As others have mentioned, put it in a separate configuration file outside of source control (obviously this will be mentioned in code which is under source control).

It also is a good idea to name the file config.php rather than config.ini in case the directory is ever accidentally exposed, meaning that the file won't be downloaded, rather it returns nothing.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
acutesoftware
  • 1,091
  • 3
  • 14
  • 33
  • 1
    +1 for the idea with config.php - nice to know trick anyway – Horst Walter Feb 26 '13 at 12:35
  • 2
    and/or keep the config OUT of the DOCUMENT_ROOT and/or specifically protected by a .htaccess file or web server config. Personally I like to keep that configs and include files OUT of the DOCUMENT_ROOT. Using Apache Magic (my term, not a piece of software) you can hide an entire Code Igniter project without exposing any files to being downloaded directly. – ChronoFish Feb 26 '13 at 17:51
6

If you feel that the 12factor way is valuable, they recommend storing config in the environment.

This has the added advantage of allowing you to write the exact same code when you're testing or otherwise in a non-production environment. If you want (or need) to change the database, or anything else, there's no need to modify your code - you just change the environment variables and you're good to go.

Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
2

What I would do is to store only example config file in repository, like config.php.dist and don't put actual config.php under version control.

Serge Kuharev
  • 1,052
  • 6
  • 16
2

It is a good idea to move the password from code to a configuration file, so the password will not be in the repository. The idea to encrypt it in a configuration file is questionable, because the one who has the configuration file + PHP code that decrypts it can always run the code and get a clear-text password. Probably it would be better to keep the password in a configuration file as plain text and think how to protect the configuration file from unauthorized access.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mikhail Vladimirov
  • 13,572
  • 1
  • 38
  • 40
2

Good question. You could move the most sensitive pieces (e.g. keys/passwords) out as environment variables, but that would then just defer the problem to your server configuration (Which presumably is also in a repository).

You could also try to avoid passwords for stuff like the database, by making it password less and secure it behind a firewall instead. None of these are perfect solutions, but they are the approaches that I know about.

troelskn
  • 115,121
  • 27
  • 131
  • 155
1

You can always create a configuration ini file using the function parse_ini_file.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dawid Sajdak
  • 3,064
  • 2
  • 23
  • 37
1

I don't see all this problem here. If you are sharing a repository you don't probably want to hard code passwords and configuration. You should provide defaults one instead:

host: localhost
user: root
pass: root
name: db

If you really want you can use and parse a .ini file but it's likely to be very slow compared to an array being set in the source code and doesn't really make that much sense to me.

Remember: the only thing you can trust is your source code, if they can get it, you are screwed anyway.

Shoe
  • 74,840
  • 36
  • 166
  • 272