440

When a PHP application makes a database connection it of course generally needs to pass a login and password. If I'm using a single, minimum-permission login for my application, then the PHP needs to know that login and password somewhere. What is the best way to secure that password? It seems like just writing it in the PHP code isn't a good idea.

AviD
  • 12,944
  • 7
  • 61
  • 91
user18359
  • 4,673
  • 5
  • 19
  • 11

17 Answers17

262

Several people misread this as a question about how to store passwords in a database. That is wrong. It is about how to store the password that lets you get to the database.

The usual solution is to move the password out of source-code into a configuration file. Then leave administration and securing that configuration file up to your system administrators. That way developers do not need to know anything about the production passwords, and there is no record of the password in your source-control.

Farray
  • 8,290
  • 3
  • 33
  • 37
user11318
  • 9,273
  • 2
  • 25
  • 25
  • 12
    Thanks. If I understand this correctly, the php file will then have an include to the config file, allowing it to use the password. e.g. I create a file called 'app1_db_cfg.php' that stores the login, pword, & db name. Then my application.php page includes 'app1_db_cfg.php' and I'm in business! – user18359 Sep 19 '08 at 00:40
  • yep, I usually do DEFINE("DB_NAME","mydb"); etc it is also handy to define anything you'll be using on more then page, that you might want to change later – Matthew Rapati Sep 19 '08 at 03:33
  • Just saving the password in a config file is not much more secure. It needs to be properly protected, using strong ACLs and strong encryption with properly protected keys... See my post below. – AviD Sep 21 '08 at 19:23
  • 32
    I agree that the config needs to be properly protected. However knowing how to do that is the business of system administrators, not developers. I disagree on the value of strong encryption in this case. If you can't protect your config file, what makes you think you can protect your keys? – user11318 Sep 21 '08 at 20:04
  • 11
    I prefer using a database account that is only allowed to acces the database from the web server. And then I don't bother encrypting the configuration, I just store it outside the web root. – gnud Apr 25 '09 at 08:01
  • @bentilly, that's where strong Key Management comes in, and this should definitely be the developer's responsibility, not the admin's - though the admins also have some tasks here too. – AviD May 16 '09 at 18:38
  • 1
    @bentilly so when you dont expose sensitive data theres no need for encryption at all? Not encrypting because you dont think anyone unprivileged would get access sounds odd for me - it makes the PIN on my VISA useless, since nobody should get access to it. edit: sorry, someone dug out an old thread – atamanroman Sep 10 '10 at 11:20
  • 1
    @gnut: " I just store it outside the web root. ". How string it outside webroot would help? I am asking this as I have seen in even modern open source scripts that the config file which contains the db password is in root. I dont prefer this and I wanna know if I sould bother. – C graphics Aug 25 '12 at 22:59
  • @MatthewRapati ... I'm actually solving problem with password in constants, because other developers can simple do `get_defined_constants()`. – dmnc Oct 23 '13 at 11:19
  • 14
    I use an apache environment variable to set the path so that even the path to the file is unknown in the source code. This also nicely allows having a different password for development and production based on what Apache settings are on the serverm – geedew Mar 12 '14 at 13:07
  • 21
    Please keep in mind that even files stored outside of the web accessible directory must be read by the script that uses them. If someone includes that file, then dumps the data from the file, they will see the password. – Rick Mac Gillis Nov 08 '14 at 19:33
  • 2
    For all the people who are asking how to protect from all other than me, the answer is either manage the whole company by yourself, otherwise you have to trust at least someone. – Ankit Feb 08 '16 at 17:03
  • 2
    @henk There is no solution for this. You have to trust or be careful about the contents of all the scripts that are going to the production server. – Ankit Feb 08 '16 at 17:05
  • 1
    @RickMacGillis `If someone includes that file, then dumps the data from the file, they will see the password` how do you deal with this? – tonix May 22 '16 at 10:16
  • 3
    @tonix The issue is more of an issue with open source software, as you'll have knowledge of the variables or layout of that file. For that reason, `define` is a bad idea for setting those variables. (Ex. WordPress uses that insecure method and it has it inside of the web-accessible directory.) The issue I was pointing out is that if someone is able to upload a file, they can discover the passwords in that file. However, even though someone can easily dump the password created with define, Laravel's .env or array-based config files are only mildly better. Every file is susceptible to attack. – Rick Mac Gillis May 24 '16 at 17:30
  • 3
    To avoid this issue in the first place, use a password server and query the server for the information you need. AWS KMS could be used to help secure your passwords further by keeping them encrypted in the file-system, and only decrypt them in memory with Redis. https://aws.amazon.com/kms/ – Rick Mac Gillis May 24 '16 at 17:37
  • 2
    Then again, if someone can place a file on your site and run it, you have bigger issues than just losing your passwords. That file can do anything. – Rick Mac Gillis Jun 06 '16 at 19:11
  • Except administrators almost never secure them because they usually aren't even aware they are there. lol. – Robert Talada Jun 16 '20 at 21:03
  • Using a KMS is not enough: some libraries, like PDO, show the password in error log. For PDO, use the Example #2 or #3 here: https://www.php.net/manual/en/pdo.construct.php – Marco Marsala Jan 30 '23 at 14:08
104

If you're hosting on someone else's server and don't have access outside your webroot, you can always put your password and/or database connection in a file and then lock the file using a .htaccess:

<files mypasswdfile>
order allow,deny
deny from all
</files>
kellen
  • 6,963
  • 3
  • 22
  • 10
  • Useful, if it's truly secure, though it seems like a shell login would still have access. – Kzqai Apr 14 '10 at 16:10
  • 30
    Definitely, but if someone has shell access, your entire account has been compromised anyway. – kellen Apr 16 '10 at 18:59
  • There is no real solution for this. Anyone can use dump functions in file, upload the file to production server and execute it. Yayy you got all the secrets of production server ! – Ankit Feb 08 '16 at 17:07
  • 4
    This is bad practice because you might accidentally commit your credentials to a repository. – Porlune Jun 07 '16 at 02:54
  • 2
    @Ankit: If it's possible for a non-friendly to upload a file to the server and execute it, then the server is not properly configured. – kellen Jun 07 '16 at 15:38
  • 6
    @Porlune: Developers should make their version control system ignore the password file, i.e. by using a .gitignore. But yes, care should be taken with files that contain sensitive data. – kellen Jun 07 '16 at 15:41
  • @Porlune Ok, but you might also accidentally type the password into a user field and have it stored in a plaintext log file. At some point, people have to be expected to do their job correctly. You cannot eliminate every risk. It's a fallacy that you can make any entirely system safe from error. – Robert Talada Jun 16 '20 at 21:09
  • 1
    @RobertTalada W/r/t this particular solution, it's important that those with a limited background in security practices are given a heads up of potential pitfalls. User fields in a browser are out of scope, but repository commits are not. – Porlune Jun 21 '20 at 20:34
56

The most secure way is to not have the information specified in your PHP code at all.

If you're using Apache that means to set the connection details in your httpd.conf or virtual hosts file file. If you do that you can call mysql_connect() with no parameters, which means PHP will never ever output your information.

This is how you specify these values in those files:

php_value mysql.default.user      myusername
php_value mysql.default.password  mypassword
php_value mysql.default.host      server

Then you open your mysql connection like this:

<?php
$db = mysqli_connect();

Or like this:

<?php
$db = mysqli_connect(ini_get("mysql.default.user"),
                     ini_get("mysql.default.password"),
                     ini_get("mysql.default.host"));
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
Lars Nyström
  • 5,786
  • 3
  • 32
  • 33
  • 1
    Please check the proper values of ini_get('default values') http://www.php.net/manual/en/class.mysqli.php – Val Jun 15 '12 at 13:08
  • Could one use this in their .htaccess file? –  Aug 18 '12 at 17:49
  • 7
    yes, but any user (or a hacker abusing badly written php script) can read the password via `ini_get()`. – Marki555 Mar 21 '16 at 17:50
  • 1
    @Marki555 `but any user (or a hacker abusing badly written php script) can read the password via ini_get()` How do you deal with this? – tonix May 22 '16 at 10:20
  • 2
    Marki555 is saying that an attacker who can run PHP code can also call PHP functions, which is obviously true and impossible to do something about. I would also like to add that I no longer follow the advice I give in this answer myself, but instead use environment variables. The concept is similar though: Don't store your credentials in the code, but inject them somehow. It doesn't really matter if you use `ini_get()` or `getenv()`. – Lars Nyström May 23 '16 at 08:32
  • @tonix as already written, if "good" script has access to db password, then also "bad" script does. So your only option is to detect such scripts and don't allow them to run, for example using `mod_security` apache module, although it is not trivial to fine-tune the rules correctly. – Marki555 May 23 '16 at 10:59
  • @Marki555 I can think of using a single DB connection entry point where I read the password and the username of the database and pass them to the connection engine in order to get the database connection and then unset those variables. At this point I need to beware everyone's push to the codebase and assure that there are no changes to that entry point file and no one is trying to dump the password of the database somehow. Could it be enough? – tonix May 24 '16 at 06:00
  • Having the pass in config file outside root, is safer that exposing it to ini_get() or getenv() that could be injected and executed from anywhere in the website. – DeepBlue Jan 22 '17 at 22:21
  • 4
    @DeepBlue If you can inject ini_get() you can inject file_get_contents(anypath) as well. As long as php has a way to get to the password, so will any malicious code. – varesa Apr 16 '17 at 04:46
  • 2
    I think this is a very __bad idea__, if the server has a phpinfo() floating around, this will leak your password. – Chris Seufert Dec 06 '17 at 09:14
45

Store them in a file outside web root.

da5id
  • 9,100
  • 9
  • 39
  • 53
  • 35
    And also, as mentioned elsewhere, outside of source control. – Frank Farmer May 26 '09 at 20:46
  • 6
    we would be able to include it? e.g. in PHP can we then do `include('../otherDirectory/configfile.conf')` ? – mtk Jan 05 '13 at 17:23
  • 2
    You're all suggesting to store credentials outside wwwroot. Ok, I understand the security background. But how should it be stored in version control then (sample config)? Usually wwwroot is the root of git repo, so if there is anything outside - it will be outside of VC. Imagine new developer trying to set up a local instance for development - how should he know magic like "take this file, copy it outside and fill in"? – The Godfather Aug 10 '18 at 18:17
  • 5
    @TheGodfather The idea is that a new developer should have their own credentials for their own development environment. Although is a good practice to have a readme with instructions or comments in the code indicating how you should configure it (but not the actual data). – PhoneixS Nov 26 '19 at 11:01
  • Storing in external file is not enough: some libraries, like PDO, show the password in error log. For PDO, use the Example #2 or #3 here: https://www.php.net/manual/en/pdo.construct.php – Marco Marsala Jan 30 '23 at 14:05
39

For extremely secure systems we encrypt the database password in a configuration file (which itself is secured by the system administrator). On application/server startup the application then prompts the system administrator for the decryption key. The database password is then read from the config file, decrypted, and stored in memory for future use. Still not 100% secure since it is stored in memory decrypted, but you have to call it 'secure enough' at some point!

pdavis
  • 3,212
  • 2
  • 29
  • 31
  • 39
    @RaduMurzea that's ridiculous. When have you heard of Sys Admins dying? They're like McDonalds, they just appear/disappear out of nowhere! – ILikeTacos Jan 23 '15 at 18:43
  • 14
    @Radu Murzea Just have 2 or more admins, then you have parity like a raid array. Chances of more than one drive failing at a time is much lower. – element11 Sep 24 '15 at 15:23
  • 7
    what about when the servers restart? What about the time it takes to wake the admin up to get them to type the password in..etc.etc. lol – John Hunt Jan 18 '16 at 11:52
  • iam 100% agreed, that was oracle weblogic done with boot.properties – nurulhudamustaqim Aug 31 '16 at 17:24
  • 1
    Not sure what you mean by 'stored in memory'. PHP web apps don't generally store anything in memory for longer than the time it takes to respond to an individual request to see a page. – bdsl Aug 12 '17 at 10:16
  • @bdsl Redis or Memcache – user1119648 Mar 23 '18 at 12:12
  • @element11 Just be sure they never drive to McDonald's for lunch together in the same car! – David Sep 05 '18 at 00:21
16

This solution is general, in that it is useful for both open and closed source applications.

  1. Create an OS user for your application. See http://en.wikipedia.org/wiki/Principle_of_least_privilege
  2. Create a (non-session) OS environment variable for that user, with the password
  3. Run the application as that user

Advantages:

  1. You won't check your passwords into source control by accident, because you can't
  2. You won't accidentally screw up file permissions. Well, you might, but it won't affect this.
  3. Can only be read by root or that user. Root can read all your files and encryption keys anyways.
  4. If you use encryption, how are you storing the key securely?
  5. Works x-platform
  6. Be sure to not pass the envvar to untrusted child processes

This method is suggested by Heroku, who are very successful.

Neil McGuigan
  • 46,580
  • 12
  • 123
  • 152
11

if it is possible to create the database connection in the same file where the credentials are stored. Inline the credentials in the connect statement.

mysql_connect("localhost", "me", "mypass");

Otherwise it is best to unset the credentials after the connect statement, because credentials that are not in memory, can't be read from memory ;)

include("/outside-webroot/db_settings.php");  
mysql_connect("localhost", $db_user, $db_pass);  
unset ($db_user, $db_pass);  
Bob Fanger
  • 28,949
  • 7
  • 62
  • 78
  • 13
    If someone has access to the memory, you're screwed anyway. This is pointless fake-security. Outside the webroot (or at the least protected by a .htaccess if you don't have access above your webroot) is the only safe option. – uliwitness Sep 23 '12 at 13:48
  • 2
    @uliwitness - That's like saying that just because someone can cut through your network operation center's lock with an acetylene torch means that the door is also fake security. Keeping sensitive information bound to the tightest possible scope always makes sense. – Luke A. Leber Jan 09 '17 at 06:22
  • 1
    How about echo $db_user or printing the $db_pass ? Even developers on the same team should not be able to figure out the production credentials. The code should contain nothing printable about the login info. – Mohammed Joraid Jun 28 '17 at 15:40
  • @LukeA.Leber With proper security in place, the lock should add no further security. The lock is only there to make it less likely equipment will get stolen but just in case the equipment is stolen, the equipment should contain no sensitive and/or unencrypted data. – Robert Talada Jul 16 '20 at 19:27
9

Previously we stored DB user/pass in a configuration file, but have since hit paranoid mode -- adopting a policy of Defence in Depth.

If your application is compromised, the user will have read access to your configuration file and so there is potential for a cracker to read this information. Configuration files can also get caught up in version control, or copied around servers.

We have switched to storing user/pass in environment variables set in the Apache VirtualHost. This configuration is only readable by root -- hopefully your Apache user is not running as root.

The con with this is that now the password is in a Global PHP variable.

To mitigate this risk we have the following precautions:

  • The password is encrypted. We extend the PDO class to include logic for decrypting the password. If someone reads the code where we establish a connection, it won't be obvious that the connection is being established with an encrypted password and not the password itself.
  • The encrypted password is moved from the global variables into a private variable The application does this immediately to reduce the window that the value is available in the global space.
  • phpinfo() is disabled. PHPInfo is an easy target to get an overview of everything, including environment variables.
Courtney Miles
  • 3,756
  • 3
  • 29
  • 47
  • "This configuration is only readable by root" - although the environment variables that are set are presumably readable by _everyone_? – MrWhite Oct 13 '20 at 17:04
  • @MrWhite, the env variable would only be set for user Apache runs as. So it is definitely not readable for *everyone*. – Courtney Miles Oct 14 '20 at 03:49
9

If you are using PostgreSQL, then it looks in ~/.pgpass for passwords automatically. See the manual for more information.

Jim
  • 72,985
  • 14
  • 101
  • 108
  • PDO supports reading password directly from a file with any database, see example #2 here: https://www.php.net/manual/en/pdo.construct.php – Marco Marsala Jan 30 '23 at 14:21
8

Your choices are kind of limited as as you say you need the password to access the database. One general approach is to store the username and password in a seperate configuration file rather than the main script. Then be sure to store that outside the main web tree. That was if there is a web configuration problem that leaves your php files being simply displayed as text rather than being executed you haven't exposed the password.

Other than that you are on the right lines with minimal access for the account being used. Add to that

  • Don't use the combination of username/password for anything else
  • Configure the database server to only accept connections from the web host for that user (localhost is even better if the DB is on the same machine) That way even if the credentials are exposed they are no use to anyone unless they have other access to the machine.
  • Obfuscate the password (even ROT13 will do) it won't put up much defense if some does get access to the file, but at least it will prevent casual viewing of it.

Peter

Vagnerr
  • 2,977
  • 3
  • 33
  • 46
6

We have solved it in this way:

  1. Use memcache on server, with open connection from other password server.
  2. Save to memcache the password (or even all the password.php file encrypted) plus the decrypt key.
  3. The web site, calls the memcache key holding the password file passphrase and decrypt in memory all the passwords.
  4. The password server send a new encrypted password file every 5 minutes.
  5. If you using encrypted password.php on your project, you put an audit, that check if this file was touched externally - or viewed. When this happens, you automatically can clean the memory, as well as close the server for access.
Asi Azulay
  • 71
  • 1
  • 1
4

An additional trick is to use a PHP separate configuration file that looks like that :

<?php exit() ?>

[...]

Plain text data including password

This does not prevent you from setting access rules properly. But in the case your web site is hacked, a "require" or an "include" will just exit the script at the first line so it's even harder to get the data.

Nevertheless, do not ever let configuration files in a directory that can be accessed through the web. You should have a "Web" folder containing your controler code, css, pictures and js. That's all. Anything else goes in offline folders.

Bite code
  • 578,959
  • 113
  • 301
  • 329
4

Put the database password in a file, make it read-only to the user serving the files.

Unless you have some means of only allowing the php server process to access the database, this is pretty much all you can do.

Chris
  • 4,852
  • 1
  • 22
  • 17
4

If you're talking about the database password, as opposed to the password coming from a browser, the standard practice seems to be to put the database password in a PHP config file on the server.

You just need to be sure that the php file containing the password has appropriate permissions on it. I.e. it should be readable only by the web server and by your user account.

Jason Wadsworth
  • 933
  • 1
  • 10
  • 18
  • 2
    Unfortunately the PHP config file can be read by phpinfo() and if someone happens to leave some test script behind a lucky attacker would be able to read the password. It's probably best to leave the connection password in a file outside the web server root instead. Then the only way to access it is either with a shell or by executing arbitrary code, but in that scenario all security is lost anyway. – MarioVilas Apr 27 '14 at 18:51
  • @MarioVilas "the PHP config file can be read by phpinfo()" - I think the answer is referring to an arbitrary PHP script that contains the config information, not the `php.ini` (config) file (which I assume is what you're referring to). This won't be "readable by phpinfo()". – MrWhite Oct 13 '20 at 17:17
  • @MrWhite you are absolutely right of course. I misunderstood the answer to mean storing the database credentials in php.ini itself. – MarioVilas Oct 16 '20 at 13:46
4

Just putting it into a config file somewhere is the way it's usually done. Just make sure you:

  1. disallow database access from any servers outside your network,
  2. take care not to accidentally show the password to users (in an error message, or through PHP files accidentally being served as HTML, etcetera.)
Raptor
  • 53,206
  • 45
  • 230
  • 366
Marijn
  • 2,056
  • 2
  • 13
  • 11
  • Beware PDO shows passwords in error messages if connection fails, because PHP prints parameters of function calls in error messages that could be logged. PDO can read password directly from a file, see example #2 here: https://www.php.net/manual/en/pdo.construct.php – Marco Marsala Jan 30 '23 at 14:19
3

Actually, the best practice is to store your database crendentials in environment variables because :

  • These credentials are dependant to environment, it means that you won't have the same credentials in dev/prod. Storing them in the same file for all environment is a mistake.
  • Credentials are not related to business logic which means login and password have nothing to do in your code.
  • You can set environment variables without creating any business code class file, which means you will never make the mistake of adding the credential files to a commit in Git.
  • Environments variables are superglobales : you can use them everywhere in your code without including any file.

How to use them ?

  • Using the $_ENV array :
    • Setting : $_ENV['MYVAR'] = $myvar
    • Getting : echo $_ENV["MYVAR"]
  • Using the php functions :
  • In vhosts files and .htaccess but it's not recommended since its in another file and its not resolving the problem by doing it this way.

You can easily drop a file such as envvars.php with all environment variables inside and execute it (php envvars.php) and delete it. It's a bit old school, but it still work and you don't have any file with your credentials in the server, and no credentials in your code. Since it's a bit laborious, frameworks do it better.

Example with Symfony (ok its not only PHP) The modern frameworks such as Symfony recommends using environment variables, and store them in a .env not commited file or directly in command lines which means you wether can do :

Documentation :

Bruno Guignard
  • 196
  • 1
  • 5
2

Best way is to not store the password at all!
For instance, if you're on a Windows system, and connecting to SQL Server, you can use Integrated Authentication to connect to the database without a password, using the current process's identity.

If you do need to connect with a password, first encrypt it, using strong encryption (e.g. using AES-256, and then protect the encryption key, or using asymmetric encryption and have the OS protect the cert), and then store it in a configuration file (outside of the web directory) with strong ACLs.

AviD
  • 12,944
  • 7
  • 61
  • 91
  • 3
    No point in encrypting the password *again*. Someone who could get at the unencrypted password can also get at whatever passphrase is needed to decrypt the password. However, using ACLs & .htaccess is a good idea. – uliwitness Sep 23 '12 at 13:46
  • 2
    @uliwitness I think you may have misunderstood - what do you mean by "encrypt *again*"? It's just the one encryption. And you don't want to be using passphrases (intended for human use) to encrypt it, rather strong key management, e.g. protected by the OS, in such a way that simply accessing the file system will not grant access to the key. – AviD Sep 23 '12 at 15:29
  • 4
    Encryption is not magic - instead of protecting the AES key with ACLs you could just store the password there. There is no difference between accessing the AES key or the decrypted password, encryption in this context is just snake oil. – MarioVilas Apr 27 '14 at 18:47
  • @MarioVilas whaat? If the password is encrypted, with the encryption key protected by the OS, how is there no difference? Encryption is not magic - it just compacts all the secrecy into the smaller encryption key. Hardly snakeoil, in this context it is just *moving* all that secrecy into the OS. – AviD Apr 28 '14 at 06:25
  • 6
    @AviD how come the OS can protect the key but not the data itself? Answer: it can protect both, so encryption doesn't really help. It'd be different if only the data was stored and the encryption key was derived, for example, from a password that had to be *typed* by a user. – MarioVilas Sep 22 '14 at 10:45