My Blog

contains PHP and other web related content. (Sometimes there are some off topic things - don't freak out!)

Archive for the ‘security’ Category

Easy MCrypt encryption class

Tuesday, August 17th, 2010

For whatever reason, I can never remember the exact coding of MCrypt. And maybe that is a good thing – so I stop doing so much code duplication and start using a class I wrote. For this reason, I’ll save you the same frustrations and share how I do my encryption.

easyMcrypt.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class easyMcrypt
{
	protected static $_openModules = array();
 
	public static function encrypt($string, $key, $type, $mode)
	{
		$module = self::_getModule($type, $mode);
		$iv = self::_alt_mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
		mcrypt_generic_init($module, $key, $iv);
		$data = mcrypt_generic($module, $string);
		mcrypt_generic_deinit($module);
		return $data;
	}
 
	public static function decrypt($string, $key, $type, $mode)
	{
		$module = self::_getModule($type, $mode);
		$iv = self::_alt_mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
		mcrypt_generic_init($module, $key, $iv);
		$data = trim(mdecrypt_generic($module, $string));
		mcrypt_generic_deinit($module);
		return $data;
	}
 
	protected static function _getModule($type, $mode)
	{
		if (!isset(self::$_openModules[$type][$mode])) {
			if (in_array($type, mcrypt_list_algorithms()) && in_array($mode, mcrypt_list_modes())) {
				self::$_openModules[$type][$mode] = mcrypt_module_open($type, '', $mode, '');	
			}
			else {
				throw new exception("{$type} is not a valid algorithm");
			}
		}
 
		return self::$_openModules[$type][$mode];
	}
 
	/** borrowed from http://www.php.net/manual/en/function.mcrypt-create-iv.php#54925 **/
	protected static function _alt_mcrypt_create_iv($size)
	{
		$iv = '';
	    for($i = 0; $i < $size; $i++) {
	        $iv .= chr(rand(0,255));
	    }
	    return $iv;
	}
}

This will be envoked by using the static functions encrypt() and decrypt(). It is very rare that I will use both encrypt and decrypt on the same program flow, but I may end up using encrypt() twice or more. This is the reason why I do some caching in the class. Notice, both the encrypt and decrypt methods will get the module from the protected _getModule() method. This sends in the type and the mode. This method checks to see if we’ve already set this module open. If so, we don’t need to open it again – just return it from the protected cache. Otherwise, a check is done to make sure that the type and mode exist. If so, it is stored in the cache. Finally the stored module is returned. If it does not exist, an exception is thrown. I did this check – even though it seems a little redundant because why would you check to see if you’re using say…tripledes – you should just know – I did it because I had some instances where a non found mode/type just returned a NULL result. The code executed fine. Bah!

At any rate then, a new Initialization Vector is generated. In this case, I used one of the functions I found on the PHP manual page. I was running into the delay on some OS’s / versions of PHP with the mcrypt version of IV_CREATE. Then, the module is init’d, the data is either encrypted or decrypted, and the module is de-init’d. The data is then returned (note: the decryption one trims spaces at the end that can sometimes happen).

For an example of how this is used:

1
2
3
4
5
6
7
8
9
10
$string = 'Please encrypt me';
$key = 'this is my encryption key';
$type = 'tripledes';
$mode = 'ecb';
 
$encrypted = easyMcrypt::encrypt($string, $key, $type, $mode);
var_dump($encrypted);
 
$decrypted = easyMcrypt::decrypt($encrypted, $key, $type, $mode);
var_dump($decrypted);

Restrict your .git directory on live site

Tuesday, June 15th, 2010

Do you use Git to manage your repository? If so, do you use it to check out code onto the server as well? If you do, you really should restrict access to your .git directory if it’s in your public root. (If you’re using things like Zend Framework, chances are your root directory is not your public directory, so you have less to worry about.)

Simply, add the following lines to your apache config:

1
2
3
<Directory /full/path/to/public/.git>
  Deny from All
</Directory>

This will simply rewrite the request to your home page. No more accessing things like your ‘config’ file that could potentially hold useful information about your Git repo configuration. (You may remember this topic being discussed with SVN here.

Scanning for Unfiltered Content Automatically with PHP

Tuesday, September 15th, 2009

A friend of mine posed a question: Do you know of any good PHP based vulnerability scanners? I told him I did not (add any in the comments, if you know! :) ) – but it wouldn’t be that hard to build one. He asked me to give him a code example, so here goes:
(more…)

Another example of CSRF – in CSS

Thursday, March 5th, 2009

Just saw this really cool example get submitted on one of my websites testing for CSRF:

1
#logo{background:url(deletepost.process.php?id=12345&userID=12345);

Just another great example of why you should
1) not use GET for irreversible changes
2) filter filter filter! (I edited that posting, it was a filtered by my script already…)

My Progression through Forgot Passwords

Monday, March 2nd, 2009

I thought I’d take some time to look at the 3 main ways that I’ve handled forgotten passwords on my websites, why I did them that way, and if there was anything wrong.

Disclaimer: there is a lot of bad code in here – and thats on purpose! This is a historical piece… :)

The n00b Times: send the password back to them

The very first ‘forgot password’ attempt I made was a long time ago on a website about computer security. This was really quite funny because this was the least secure way to do it. Users would request their password, and I’d send them their password, in clear text, to their email. Not very secure! Side note: this means I had to be able to decrypt their passwords to send it back to them – and newbie me actually skipped that step – just a plain varchar of their password. OOPS.

The non-scalable times: hash the time away

The next step in my programming mutuation was at least more secure: send the hash through email and let them reset their password. This way, I never send their password through the email – and never actually stored it in a decrypt-able (is that a word?) state. Of course, I did it wrong again:

Um, don’t do this:

1
2
mysql_query("Insert into resets (userID, key) values($userID, '" . md5(time()) . "');
mail($to, 'Password reset', "Please click this link to reset your pass: http://website.com/resetpass.php?key=" . md5(time()));

You DO see all the problems, right?

The biggest one is that if two users were creating a forgot-password request at the same time, they both would get the same ID – and you could end up resetting someone else’s password and gaining access to their account.

Of course, the next issue was that it was pretty easy to guess a password reset key for someone if you saw when they did it.

Then, I didn’t store the key – so theoretically, the first line could be a different time than the url that was sent to the user – especially if there was a high sql load!

Better Hash – not based out of Amsterdam

The next thing I realized was that I had to make this hash a bit more unique, so I ended up adding the userID to the time as a prefix… (should also point out that one time I also went with generating a hash based off of their userID and then sending a timestamp as a separate parameter… its relatively the same thing as this example)

still not good enough!

1
2
3
4
$time = time();
$key = md5("{$userID}{$time}");
mysql_query("Insert into resets (userID, key) values($userID, '$key');
mail($to, 'Password reset', "Please click this link to reset your pass: http://website.com/resetpass.php?key=$key");

At least I fixed the key – um – sorta. However, if you knew the user id – you could at least make a better educated guess at this hash – especially if you knew the time was. Point being, it was a step up, but not my final resting place.

Break: Some of you might wonder why I didn’t just use a uniqid() and md5 that… well… yah… but we all make mistakes when we first start out right? ;) Just trying to help out any new programmers not to make the same mistakes

What are you doing now?

Ok – so for something thats pretty secure like that, I wanted to have a very long, extremely random string. I thought of sending mt_rand()’s next to each other and hexadecimalling them – or md5ing them. But I settled on something hopefully with even more of a chance not to be guessed: base64 encoding.

What?

Well, let me show you.

1
2
3
4
5
$forEncode = '';
for ($i=0; $i<300; $i++) {
	$forEncode .= chr(rand(1,255));
}
$key = strtr(base64_encode($forEncode), '+/=', '-_.');

Granted, I left out the mailing and mysql storage, but you get the idea. Real quick, a run-down:

First, start out wiht my blank string. I plan to generate 300 random characters – so I create that for loop. Then, I choose a random number between 1 and 255, corresponding to the ASCII table, and generate the chr() value of it. Then that is added to my string. I now have a string that has 300 characters of any character from 1 to 255 on the ascii chart. Finally, I base64 encode it – and then replace the items in it that are not good to have in an URL.

How do YOU do it?

Disable md5 now – or you will die

Thursday, February 19th, 2009

I remember a while ago hearing about a few theoretical collisions of the md5 algorithm, but I thought nothing of them. Now, as more information emerges, Microsoft is issuing advisories, and people are proving more and more collisions with example code, and even md5 is out of vista, I figure its time to remind everyone not to use md5.

What should I do?

First of all – lets use sha1 instead – equally as easy of a function to use – but much more secure.

1
echo sha1('test');

Output:

a94a8fe5ccb19ba61c4c0873d391e987982fbbd3

Next, disable it in php using disable_functions in your php.ini

php.ini excerpt

disable_functions = md5

Finally, don’t accidentally use it in your db ;)

Password Complexity Class

Thursday, February 12th, 2009

After many times of coding relatively the same thing, it becomes prudent to have a standard library for certain sets of processes. Of course – that is why there are things like frameworks! At any rate, one of the biggest things I run into is password complexity. Each website has its own requirement for the security they want to implement. So, let’s talk about the requirements and then look at the code:
(more…)

How custom passphrases/pictures still don’t protect against phishing

Thursday, November 20th, 2008

As you probably remember, I have lots of interest in phishing techniques (I talked about one here, and preventing them here). I’ve noticed a new trend: a dual stage login form with a custom picture or passphrase. Users are to gain trust in the login page because their custom configured option is displayed. The more I started thinking about this, however, I kept seeing an issue – this still can be easily phished! I’m going to demonstrate a method of phishing the passphrase version. I don’t want to do a picture example because it a) takes more code and b) more people have moved to that thinking it is more secure. Lets go:


First off, all phishing starts with getting the user to a login page not at the respected domain. So, lets just skip that step, and examine our login page. This will be a duplicate of our real site’s login page – note the reminder that they will have to verify their passphrase.

login.php @ fakedomain.com

1
2
3
4
5
<form action="login2.php" method="post">
    <label>Username: <input name="username" /><br />
    <em>Remember, you will be asked to verify your passphrase on the next page.</em><br />
    <input type="submit" value="Login" />
</form>

Very simple login which sends it to another page – hopefully named the same as the real domain’s login page.

Lets look at the page we’ll be submitting to:

login2.php @ fakedomain.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    /** cutting out a lot of code - make sure its not empty, etc **/
    $args = array ('username'=>$_POST['username']);
    $uri = 'http://realdomain.com/login.do.php';
     $opts = array('http'=>array('method'=>'POST', 'header'=>'Content-Type: application/x-www-form-urlencoded', 'content'=>http_build_query($args)));
     $context = stream_context_create($opts);
    $page_with_phrase = file_get_contents($uri, false, $context);
 
    $doc = new DomDocument();
    $doc->loadHTML($page_with_phrase );
 
    $passphrase = $doc->getElementById('passphrase_node')->nodeValue;
 
    /** next login form page actually shows the $passphrase phrase and asks for password **/
    include('next_login_form.php');

Ok, first off, you’ll see we create a nice post with our stream context creation (detailed here) – so we basically send the username to the real domain as they had logged in. (Depending on the target site, you might also have to send referrers, cookies, etc – but we’re making it a really simple example here.)

We retrieve the page after a successful post of the username. This content should now contain the custom passphrase somewhere. For our example, there is a nicely named div or span with an id of ‘passphrase_node’. Probably, in real life, you’d have to use a complex xpath to get the actual value.

From then on, we just include our ‘second’ login page which shows the passphrase, and then requests the password from the user. From there, you can do whatever you want.

Ok so…
Its nice to see that people are trying to eliminate phishing – but there still is only one real solution IMHO – and that is to educate users on the address bar (or get them to install a plugin that validates the page they’re on.).

Finally – PHP has NoIndex on phpinfo output

Wednesday, June 4th, 2008

Security Issue?

A big issue with PHP security had been the developers creating a php info page and not removing it from a production site. As you may know, phpinfo() will dump a ton of useful information (for the developer – as well as the cracker) to the screen:

1
phpinfo();

I can’t imagine how many versions of that are out on various servers…

Actually, let’s take a look with this google query

More than a million returns (granted they’re not all phpinfo() calls… but it gives you a good idea…)

There is Hope

With the release of 5.2.1 of PHP, phpinfo() now outputs the following meta tag:

1
<meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" />

This will slowly but surely stop compliant robots (see: google, yahoo… not crackerMcCrackenstein.com) from archiving these… yes!

JS Tool – Security Auditing in Javascript

Thursday, April 24th, 2008

JSTool was a trial run of combining many different scripts from the open source community into a security and auditing script. Features would include history viewing, website status reporting and port scanning. Very little original code – just combinations of existing code. Check the comments for proper author attribution. This script really isn’t in working condition for production distribution. Download it and learn from it.

JS Tool

  • twitter loader

Follow me on twitter: @aaronsaray

The views on this website are my own and do not reflect the opinions of my employer or clients.
Creative Commons License Home | Open Source | Book | Music | Art | Bio | Resume | Contact
My Baby