I'm working on a PHP project and I'm using a global settings file which I include where I need some global values such as database credentials for connecting to mysql.
For example:
settings.php:
<?php
const DB_ADDRESS = 'localhost';
const DB_USERNAME = 'johndoe';
const DB_PASSWORD = 'mypassword';
const DB_PORT = 7777;
?>
My question, is it safe enough?
For example, is there any way to see variables values while debugging in explorer/chrome?
Is there any alternative safer way?
Thanks.
PHP information is processed on the server before being sent to the browser so it can't be seen inside of a browser under normal circumstances. However, if your webserver is misconfigured the plain text version of your code may be sent to the browser thus rendering it visible to users. That's why important code should always be kept outside of your document root and included into files when needed.
Whilst this offers little protection in the event of a compromised server, should your source code ever become publically viewable, through a bug or other vulnerability (such as this: http://www.php.net/archive/2012.php#id2012-05-06-1) an increasingly common approach is to set various credentials and parameters as server environment variables.
E.g. in apache vhost/.htaccess you can set an environment variable such as the following:
SetEnv DB_ADDRESS localhost
...
And in your PHP code:
$DB_ADDRESS = getenv('DB_ADDRESS');
Of course you could assign this value to a class constant, global constant depending on your use case etc....
This also makes your source code more portable, allowing different configurations to be provided depending on the hosting environment (staging/production etc):
SetEnv APPLICATION_ENV development - .htaccess interacting with Zend Framework?
Your settings are never hard coded and not accessible in the source code. Heroku uses a similar approach to application configuration.
Variables are kept within a server, and aren't sent to the client. Unless your script has any vulnerabilities that allow users to output custom variables, then they'll remain secure to anyone without sourcecode access.
Your back-end code should never appear on the front-end, unless something goes terribly wrong with your setup. If that happens and your back-end source code is "leaked" -- unlikely but possible -- then your password will be visible in plain sight.
You can encrypt the password string with a symmetric encryption scheme, but you will have to store the encryption key somewhere. Then if the encryption key gets leaked, you are back to the starting point. It's still a bit better than having the password in plain text, but nothing will be 100% safe.
That's the standard way (look at phpmyadmin, mediawiki, etc.) : this php file is not accessible and if you don't make any error in your server settings, it's not readable.
Usually you'll add a test to check this settings file is included in one of your php files :
<?php
if ( !defined('IN_KP') ) die("Hacking attempt");
?>
Of course you define 'IN_KP' in your including files :
<?php
define('IN_KP', true);
include("sensitive_file.php");
?>
But the best protection overall is that those sensitive data aren't so sensitive because your mysql account is only accessible by localhost (if not fix it !).
For example, is there any way to see variables values while debugging in explorer/chrome?
If you never send them to the view (i.e. echo, var_dump, print_r, session etc) - then no. The browser will never know about them.
Related
I'm, not sure if this is a common use-case, but I'm a normal kind of guy, so I can't believe this is unusual:
I have a server running a LAMP stack. There are a few PHP applications on the server. I spotted the other day that two completely different apps are sharing session information. WTF?! I get that they do, but why? they trust the server, but why does the server assume that the two apps trust each other?
okay: #1 fix is for one or both use session_name(). That's superb and it does fix the issue if either or both of them do this and neither tries to get the others session by setting the name, but is there a fix where both apps are hostile to each other? Is there something at the PHP level that can make the sessions independent, regardless of anything the apps might try to do?
Essentially nothing that
https://www.server.com/app1/index.php does should get access to session info of
https://www.server.com/app2/index.php etc.....
I thought that setting the path using session_set_cookie_params() would sort this out, but naah, setting this variable to a limited path actually has no obvious effect which is interesting - both apps #can still get to the session stuff or the other - interesting!
I can't believe this is a new issue and yet I don't spot a fix....
Cheers,
turbotas
Example code:
<?php
session_set_cookie_params(3600,"/webapps/test1");
session_name("mysession");
session_start(); ?>
<html>
<head>
</head>
<body>
<?php echo session_id(); ?>
</body>
</html>
imagine this code in webapps/test1 and exactly the same in webapps/test2. I would not expect test2 to be able to use the test1 session state simply by stating a directory outside it's own installation point - I would expect PHP to protect against that. It doesn't - I get the same session.
As you're coding the different applications, you need to make sure that each one saves its session-information (server side) in different places. The session.save_path configuration variable, for instance, specifies the host-side directory if you are saving the sessions using files. If you're storing the session information in a database table, you should be using different tables or more likely different databases.
Thus, even if an identical session identifier is somehow produced, it will produce different results for each application because each one is referencing its own distinct host-side source.
I've read multiple comments about encrypting PHP session data, in case it is stored in a temp directory that is available on multiple accounts on a shared server. However, even if the data is encrypted, session_start() still generates filenames containing the session_id. For example,
sess_uivrkk2c5ksnv2hnt5rc8tvgi5
, where uivrkk2c5ksnv2hnt5rc8tvgi5 is the same session id I found in the cookie my browser received.
How is this problem typically addressed / could someone point me to an example? All of the simple examples I've found only address encrypting the data, not changing the filename.
Just to see what would happen, I made a SessionHandler wrapper that would do an MD5 hash on the $session_id variable before passing it on to its parent function, but that did not work. Instead, I ended up with two files: a blank one (with session_id as a part of its name) and a full one (with an MD5'ed session_id). Also, there was the problem of close() not accepting session_id as a parameter, so I couldn't pass it on to its parent.
EDIT: I 'm learning about php sessions, this isn't for a live commercial site, etc.
Yes, in some scenarios (i.e. a very incompetently configured server - although these do unfortunately exist) on a shared server your session data may be readable by other people. Trying to hide the session files by changing their names serves no useful purpose - this is described as "Security through Obscurity". Go and Google the phrase - it is usually described as an oxymoron.
If your question is how do you prevent other customers accessing your session data on a badly configured server then the sensible choices (in order of priority) are:
switch service provider
use a custom session handler to store the data somewhere secure (e.g. database) There are lots of examples on the web - quality varies
use a custom session handler to encrypt the data and use file storage. Again you don't need to write the code yourself - just scrutinize any candidates
If you want to find out if your provider might be a culprit - just have a look at the value of FILE. Does it look as if you have access to the root filesystem? Write a script which tries to read from outside your home directory. If you can't then the provider may have set an open_basedir restriction (it is possible to get around this - again Google will tell you how).
I've been reading through different secure ways to connect to databases and wanted to run this idea past you before I tie myself in knots trying it with my actual databases if it has no security benefits or simplification of switching between databases.
I was thinking that it would ensure the db_* variables are killed off after the function is used and the connection has been made to the relevant database:
<?php
function dbconn($db_hostname='localhost',
$db_username='',
$db_password='',
$db_database='database1'
$db_object='connection') {
if($db_username=='' && $db_password=='') {
switch ($db_database) {
case 'database1':
$db_username='user1';
$db_password='pass1';
break;
case 'database2':
$db_username='user2';
$db_password='pass2';
break;
default:
echo "No database defined to connect to";
break;
}
}
else if($db_password=='') {
switch ($db_username) {
case 'root':
$db_password='rootpass';
break;
case 'user':
$db_password='userpass';
break;
default:
echo "No password known for this user";
break;
}
}
$db_object= new mysqli($db_hostname, $db_username, $db_password, $db_database);
if ($db_object->connect_error) die($db_object->connect_error);
}
?>
It's unclear to me what you're trying to do, from the code you've provided. However, I can answer your question: "What is a secure way to connect to a database?"
With PHP working as a CGI-like language, the primary concern with connecting securely, is that your configuration file could be exposed. There are two possible solutions to this:
1. A PHP file as your configuration
This is probably the most common approach, and especially used often in redistributable software. By making your "configuration file" a PHP file that simply sets variables and is included in other pages, the PHP interpreter will parse this file, rather than returning its contents. The location of the file doesn't really matter - as long as it's somewhere where PHP files can be executed. In a typical application, this is anywhere in the document root except for the uploads directory.
An example configuration file (db_config.php):
<?php
$db_host = "localhost";
$db_user = "username";
$db_pass = "password";
$db_database = "database_name";
You would simply require('db_config.php'); in your initialization/header/etc. code.
You should only use the configuration file for configuring the connection, not for making it. This way, it makes it easy to change your connection code later on, without having to modify your configuration file on every server that your application runs on.
2. A serialized configuration file
Alternatively, you could use something like JSON or YAML to create a configuration file. The main advantage is that you can use the same configuration file for any auxiliary scripts or applications that need to connect to the same database, but aren't written in PHP.
However, you should never place this file in your document root. As far as your webserver is concerned, a JSON or YAML file is a 'viewable' file, and it will happily show it to anybody who has the URL.
The correct way to use a JSON/YAML/etc. file as your configuration file, is to make sure that it's placed outside of the document root, so that the webserver cannot possibly serve it up to a user. Trying to give it an 'unguessable' name is not sufficient.
You could also use something like .htaccess, but this is not recommended as it will make changing to other webservers harder, and a webserver misconfiguration could expose your database connection details. Only use this as an absolute last resort.
Other considerations
There are three other main points that I want to address here.
Multiple database credentials
In your original code, it looks like you're trying to add a feature where you can select the database you want to select to. In reality, this is almost never what you want. Every server/installation should only have its own database credentials.
If you follow the suggestions I gave for storing your configuration data, then it will be very easy to have a separate configuration file on each system, without having to ever change it. If you're using Git or another version control system that lets you ignore files, you can safely (and should) make it ignore the configuration file. You'll simply have a different configuration file on each server.
Overall security
Of course, you should make sure that your overall security is in proper working order. If you have a LFI vulnerability or somebody can upload a shell to your server, then no amount of putting files outside a document root is going to protect your database credentials. OWASP is a decent resource on general (web) application security.
PDO or mysqli_?
Judging from your snippet of code, you're using mysqli_. While this can be a valid choice if you use parameterization / prepared queries correctly, I would not recommend using it. PDO is a database-independent SQL library that focuses more heavily on parameterized queries. It's included by default in more or less every recent PHP installation, and as a bonus it'll let you switch between different SQL servers.
Using parameterized ("prepared") statements is absolutely critical - it is the only reliable way to prevent SQL injections, which seems to be the most important issue you'll want to protect yourself against here. A goood introductory guide can be found here.
I'm trying to connect to my database, but I changed my database's root password in the interest of security. However, in order to connect to the database and use PDO, I apparently have to pass my password in the php, which obviously is not good for security:
$hsdbc = new PDO('mysql:dbname=hs database;host=127.0.0.1;charset=utf8', 'root','passwordgoeshere');
$hsdbc->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$hsdbc->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Am I being stupid and that because it's PHP no-one but the person who views the actual file will be able to see the password, or is there some way to do it without passing the password in the file.
Generally speaking it's not bad practice to have connection strings in files that are not user facing. If you don't want to have your personal password in the php file, then you can create a new mysql user for php.
You can also restrict the user's IP address in MySQL to the server hosting your php scripts. This way if a nefarious person browsing the web somehow was able to see the database password, they would have more difficulty accessing it.
People are not able to just go and read into your files. They should be safe on the place where you host it. They are only able to get into to files if they are able to get into the place when you host your stuff. Which should not be possible if they don't have the info to get there.(which should only be known to you).
This is not just for PDO. but also my mysql and mysqli to do it like this
Going to extend SupSon (SC2 Select fan?)'s answer:
PHP itself is server coded language.
There are only 3 ways (maybe more if someone want to add to it) that code can be shown to an outside user:
By having an unsecure .htaccess file that shows php file as text
file (then you should move servers at that point because normally
this doesn't happen)
Somehow your operating on debug mode and something in your page
triggers this mode and you get a whole bunch of PHP code gets shown
FTP/SSH access to your .php file (then you have more than a PDO
problem in your hands)
So if one of these cases is happening, coding into a .php file your username/password won't be a breach in security.
I have seen websites that expose PHP code, when the Apache type handler for PHP becomes unconfigured by accident. Then the code in .php files is displayed instead of executed. There's also an Apache type handler to display PHP source deliberately, though this is not usually configured.
To avoid this vulnerability, it's a good practice to put your sensitive PHP code outside your htdocs directory. Instead, put in your htdocs directory a minimal PHP script that loads the rest of the code using include() or require().
An alternative is to put your MySQL credentials in a config file instead of PHP code at all. For example, the file format used by /etc/my.cnf and $HOME/.my.cnf is readable by the PHP function parse_ini_file(). It's easy to store your MySQL password outside of your code this way.
For example, read user and password from the [mysql] or [client] sections of /etc/my.cnf:
$ini = parse_ini_file("/etc/my.cnf", true);
if (array_key_exists("mysql", $ini)) {
$connect_opts = array_merge($connect_opts, $ini["mysql"]);
} else if (array_key_exists("client", $ini)) {
$connect_opts = array_merge($connect_opts, $ini["client"]);
}
$pdo = new PDO($dsn, $connect_opts["user"], $connect_opts["password"]);
Yes it seems insecure at first, but once you get the hang of it and know how to manage your files to minimize potential security breaches, you can minimize the risks associated with having passwords stored in plain text in potentially publicly exposed spaces. Yet AFAIK PDO doesn't even let you form a connection without supplying a password. The solutions are a combination of what everyone has said and then some. Here's my quick guide for what I do.
Separate SQL users for separate purposes (minimizes damage from SQL injection or hacked accounts):
There should be a PHP-specific user for each table you need to access. That user will be granted only enough rights to handle as much of that table that he needs to, if it doesn't need to delete then don't grant it delete. If it doesn't need to select then don't grant it select. It seems fussy but very quickly you'll have a copy-paste template to make the users, give them the right(s) they need, and document it. If there's a joined table, you'll want to also grant the user access to that table also, naturally.
-- a single user account for a specific purpose:
CREATE USER 'usermanager'#'localhost' IDENTIFIED BY '5765FDJk545j4kl3';
-- You might not want to give access to all three here:
GRANT SELECT, UPDATE, INSERT ON db.users to 'usermanager'#'localhost';
The purpose of this is so that if you have a bug in your code that lets people SQL inject, they won't be able to cause any harm beyond the scope of what that role can do.
Stupid mistake can reveal PHP code and files if left in-directory, move them out:
Never mind revealing the source code, even just trying to access php files "out of order" can be destructive.
Move as many files to an out-of-scope directory as possible. Then call them like so:
require_once('../lib/sql_connectors.php');
This should escape your html / webdir and you should hopefully be able to store all sorts of fun stuff outside the scope of what a stupid admin mistake could reveal.
You can even have a php file that gets pictures and videos from outside your webdir, that's how streaming sites protect their resources and also conduct php-based authentication to file access. To learn how to do that you'll want to look up assigning your own etag headers to make sure browsers cache your php-retrieved files otherwise you'll have a very busy server, here's a short introduction.
Block wrongful access to PHP that are left in-directory:
All of your in-directory PHP files can be protected by checking that the $_SERVER['REQUEST_URI'] isn't itself. If it is, you can have a function called show404() that loads the 404.php page and dies there or just directly call your 404.php with an include. That way, even if you have hackers trying to brute force your php files they'll never see them because they'll get 404 errors (fools the bots) and they'll see the 404 page (fools the humans).
I avoid using .php in any publicly visible paths, to do that, I make rewrite rules in my .htaccess files that look like this:
RewriteEngine On
RewriteRule ^login$ login.php [L,QSA]
The L makes it stop running other rules.
The QSA preserves the $_GET tags.
The first lines of code for every file (consider prepending) could be:
// they should be connecting via a redirect, not directly:
$fileName = basename(__FILE__);
if ($_SERVER['REQUEST_URI'] === '/' . $fileName) {
error_log('Security Warning: [' . $_SERVER['REMOTE_ADDR'] . '] might be trying to scrape for PHP code. URI: [' . $_SERVER['REQUEST_URI'] . ']');
include('404.php'); // should point to your 404 ErrorDocument
exit();
}
// redirect to actual file
include('../hidden/php/' . $fileName);
In this example, assuming you have the redirect in your .htaccess, a login.php with the code above, and a login.php in your hidden directory, the user would experience the following two scenarios: attempt to connect to '/login' and see the hidden '/login.php' page; attempt to connect to the visible '/login.php' directly and get a 404 error.
Those are the 3 big things, lots of small limited accounts to minimize damage in case of security failure, keep all possible files outside the web directory, and make all in-directory php files produce an error letting only non-php links access them.
My development servers are external "dev.site.com"
We have our database on an external IP address, but there is no DNS records for it. DNS has been set up locally using hosts files.
For example: database.site.com 55.444.33.21
Since my external development server cannot resolve database.site.com or 55.444.33.21 independently, nor can I add local DNS records to the server, how can I properly access it?
Is there some method in PHP I can utilize to essentially create an on-the-fly DNS for?
I do not have the ability to change how anything is setup, but I have full control of the codebase which is PHP. Essentially, I'm looking for a way to say 55.444.33.21 = database.site.com in my script so whenever I call database.site.com in code it works.
EDIT
I am not able to access it with just the ip, and i cannot add it to the local hosts file on the server. My goal is to find a workaround to these issues.
Don't call "database.site.com" in code.
Instead, define a constant in one place in the code and use that constant throughout the rest of the code:
define("DATABASE_SERVER", "database.site.com");
or
const DATABASE_SERVER = "database.site.com";
Then throughout the code you'd use that constant instead of the actual value. So when you need to move the code to another environment, you configure it for that environment by changing the value of the constant:
define("DATABASE_SERVER", "55.444.33.21");
or
const DATABASE_SERVER = "55.444.33.21";
In general, you want to use actual dynamic values as little as possible in the code. This gives all of your code multiple reasons to change. The value should be abstracted behind a constant or variable or symbol of some kind so that, if the value changes, only that one piece of code has a reason to change. The rest continues to function.
Quick hack, but you can define("database.site.com", "55.444.33.21");