RSS
 

Posts Tagged ‘Cake1.3’

Maximum power for your validation rules

07 Oct

I already postet an article about custom validation rules some time ago here.
In this post I introduce some of my custom rules which come in handy quite often.

For being able to use it in different projects I do not use the app model for it but a plugin lib.
My app model then extends this lib (/libs/my_model.php):
App::import(‘Lib’, ‘PluginName.MyModel’) and AppModel extends MyModel.

I do not want to post the methods here in the blog because they might change over time. You can find the current source code at my github rep (test case).

Lets get started.
The following code fragments are part of the $validate array of the specific model you want to validate.

Validating urls

The core rule does not validate “deep” – meaning it cannot check if the url is actually accessible/correct.
My custom rule works like this:

'field' => array(
	'validateUrl' => array(
		'rule' => array('validateUrl', array('autoComplete'=>true)),
		'message' => 'Not a valid url',
	),
),

By default, it will check deep and make sure the url actually exists.
With the autoComplete param you can be more flexible with urls that are missing “http://” etc (note/todo: it would be even better if it would save the autocompleted string). Especially in combination with strict=>true.

If we want to allow only links on the same domain we could say

'field' => array(
	'validate' => array(
		'rule' => array('validate', array('autoComplete'=>true, 'sameDomain'=>true)),
		'message' => 'Please provide a valid url on the same domain',
	),
),

Now /some/link as well as http://samedomain.com/some/link works.
With deep=>false we can disable the deep check for availability.

Validating dates and times

The main improvements are the before/after params:

'end_date' => array(
	'validate' => array(
		'rule' => array('validateDatetime', array('after'=>'start_date')),
		'message' => 'Please provide a valid date later than the start date',
	),
),

There are also date and time only versions like

'time' => array(
	'validate' => array(
		'rule' => array('validateTime', array('allowEmpty'=>true)),
		'message' => 'Please provide a valid time or leave the field empty',
	),
),

I also had to hack around problems regarding empty strings or partially invalid dates. thats why this method is quite long compared to others.

Validating keys (primary/foreign)

'id' => array(
	'validate' => array(
		'rule' => array('validateKey'),
		'message' => 'Invalid primary key',
	),
),
'foreign_id' => array(
	'validate' => array(
		'rule' => array('validateKey', array('allowEmpty'=>true)),
		'message' => 'Invalid foreign key',
	),
),

The id can be either aiid (int10) or uuid (char36), the method will always validate correctly.
Same goes for foreign_id. But due to the allowEmpty it can also be left empty (or 0 for aiids).

Validating enums

Sometimes the enums are “dynamically” generated. You cannot use the build in “inList” validation rule then.
If you have a method set up, you can call this from within the rule:

'field' => array(
	'validate' => array(
		'rule' => array('validateEnum', 'methodX'),
		'message' => 'Invalid value',
	),
),

Somewhere in the model define your custom enum value generator:

function methodX() {
  return array(...);
}

Validating uniqueness

With this enhanced method we can check on other fields – dependent uniqueness so to speak.

'field' => array(
	'validate' => array(
		'rule' => array('validateUnique', array('user_id')),
		'message' => 'You already have an entry',
	),
),

In this case the field will only invalidate if this specific user has already an entry.
Two users can have the same field content without interfering each other.

TODO: test cases with fixtures etc in order to simulate DB content to validate against…

Validating identical

'email_confirm' => array(
	'validate' => array(
		'rule' => array('validateIdentical', 'email'),
		'message' => 'The two fields do not match',
	),
),

As you can see this rule comes in handy if you need to confirm a string to another in the post data. Simply pass the field name along as second param.

Last Words

Hopefully many other cake developers find those enhanced rules useful and maybe they set the core team thinking about implementing some of the features in the future versions of cake.

Please note:
Some of the internal methods, constants etc used might be not available without stuff from my tools plugin.
Those validation methods above might have to be adjusted to your application. Same goes for the test case which uses some of my own methods/libs to work with.

 
3 Comments

Posted in CakePHP

 

Forms and redirecting

20 Aug

Redirecting in your CakePHP app

My CommonComponent now contains three redirecting methods:

/**
	 * @param mixed $url
	 * @param bool $useReferer
	 * returns nothing and automatically redirects
	 * 2010-11-06 ms
	 */
	public function autoRedirect($whereTo, $useReferer = true) {
		if ($useReferer && $this->Controller->referer() != '/' . $this->Controller->params['url']['url']) {
			$this->Controller->redirect($this->Controller->referer($whereTo, true));
		} else {
			$this->Controller->redirect($whereTo);
		}
	}
 
	/**
	 * should be a 303, but:
	 * Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
	 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
	 * @param mixed $url
	 * TODO: change to 303 with backwardscompatability for older browsers?
	 * 2011-06-14 ms
	 */
	public function postRedirect($whereTo, $status = 302) {
		$this->Controller->redirect($whereTo, $status);
	}
 
	/**
	 * only redirect to itself if cookies are on
	 * prevents problems with lost data
	 * Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
	 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
	 * TODO: change to 303 with backwardscompatability for older browsers?
	 * 2011-08-10 ms
	 */
	public function prgRedirect($status = 302) {
		if (!empty($_COOKIE[Configure::read('Session.cookie')])) {
			$this->Controller->redirect('/'.$this->Controller->params['url']['url'], $status);
		}
	}

autoRedirect and postRedirect

Where do I use it? In pretty much every form (for instance edit action):

if (empty($id) || !($user = $this->User->find('first', array('conditions'=>array('User.id'=>$id))))) {
	$this->Common->flashMessage(__('invalid record', true), 'error');
	$this->Common->autoRedirect(array('action' => 'index'));
}
if (!empty($this->data)) {
	if ($this->User->save($this->data)) {
		$var = $this->data['User']['id'];
		$this->Common->flashMessage(sprintf(__('record edit %s saved', true), h($var)), 'success');
		$this->Common->postRedirect(array('action' => 'index'));
	} else {
		$this->Common->flashMessage(__('formContainsErrors', true), 'error');
	}
}
if (empty($this->data)) {
	$this->data = $user;
}

The autoRedirect automatically redirects back to the site the user came from if possible (if the user clicked a link). Otherwise it will use the provided fallback url.
The postRedirect is a wrapper for the future where one day 303 can be used without causing trouble (read further for details).

prgRedirect

Why is this necessary in some forms? Most search forms do a simply post. What they should do is a POST + GET afterwards. Thats called PRG pattern and is described here.
Especially after posting search forms or entering data you want to avoid a nasty message like some modern browsers produce if you then hit the back button. You want to graciously display the page prior to the post. Thats where this extra redirect comes into play. Always redirect after a post – quite easy to remember.

if (!empty($this->data)) {
	if ($this->Model->search($this->data)) {
		# save POST search to session, redirect and display the search result as GET
		$this->Common->prgRedirect();
	}
}
if ($search = $this->Session->read(...)) {
	...
}

As the comment in the method head as well as other sources explain one should NOT use 303 or you end up with broken forms for some users.

Note the failsafe with the cookie. If cookies are disabled this would result in empty sessions and therefore never work. For disabled cookies there cannot be a redirect and therefore needs the POST to display the data.
Therefore your forms should work with both POST and GET for this very same reason. The “prg” redirect is only an enhancement to provide better functionality in the normal use case (where users do use the back button).

Moved/deleted content

If you moved or deleted some content you can use the 301 redirect to tell search engines and browsers where to find the same content at the new location or where to go to instead:

if (empty($manufacturer)) {
	$this->Session->setFlash(__('Invalid Manufacturer', true));
	$this->redirect(array('action'=>'index'), 301);
}

In the example I use this to redirect from views back to index if no manufacturer (retrieved via slug) was found. This is necessary to avoid duplicate content for invalid slugs.

Complete list of browsers that are not capable of handling 303s

//TODO – does anyone have infos on that matter?

 
No Comments

Posted in CakePHP

 

Introducing two CakePHP behaviors

05 Jul

Today I want to introduce two new CakePHP behaviors.

Jsonable Behavior

This is not so new, of course. Already existed as a basic version in the Bakery.
I enhanced it to work with more than just plain arrays and added some more functionality.

First download the SOURCE FILE.

In my first scenario where I used it, I had a geocoder behavior attached to the model which returned an array. I wanted to save all the returned values, though, for debugging purposes in a field “debug”. By using the following snippet I was able to do exactly that.

var $actsAs = array('Tools.Jsonable'=>array('fields'=>array('debug'), 'map'=>array('geocoder_result'));

I could access the array in the view as any other array since the behavior re-translates it back into an array on find().
Note: the mapping option is useful if you want to rename certain fields. In my case the geocoder puts its data into $this->data['Model']['geocoder_result'].
I might need to access this array later on in the model. So I “jsonable” it in the “debug” field for DB input and leave the source field untouched.

Thats all very nice. But what if needed something more frontend suitable. I want to be able to use a textarea field where I can put all kinds of params which will then also be available as array afterwards (as long as you are not in edit mode, of course).

In our add/edit actions we need to switch to param style:

$this->Model->Behaviors->detach('Tools.Jsonable');
$this->Model->Behaviors->attach('Tools.Jsonable', array('fields'=>'details', 'input'=>'param', 'output'=>'param'));

The form contains a “details” textarea field. We can insert:

param1:value1|param2:value2

In our views we get our data now as array:

array('Model'=>array(..., 'details'=>array('param1'=>'value1', 'param2'=>'value2')));

And, of course, as third use case we can also simulate an ENUM by using:

$this->Model->Behaviors->detach('Tools.Jsonable');
$this->Model->Behaviors->attach('Tools.Jsonable', array('fields'=>'tags', 'sort'=>true, 'input'=>'list', 'output'=>'list'));

Note: The default value in the model itself still needs to be array in order to transform them into a list on find()!
That’s why we only override them in the form views.

In our textarea we can now type:

dog, cat, cat, fish

In our views we would result in (var $data['Model']['tags'])

array('cat', 'dog', 'fish');

Note the cleanup automation you can additionally turn on/off.
There are more things to explore. But I will stop here for now.

Yes – you could make a new table/relation for this in the first place. But sometimes it’s just quicker to create such an enumeration field. Bear in mind: It then cannot be sorted/searched by those values, though.
For a more static solution take a look at my Static Enums.

Confirmable Behavior

This I invented a while ago working on a registration page. After I needed the same functionatily on other sites, as well, I started to build a behavior out of it. Now I don’t have to repeat myself anymore.
On Github you can find the SOURCE FILE.

Example Usage in an action:

$this->Model->Behaviors->attach(array('Tools.Confirmable'=>array('field'=>'confirmation', 'message'=>'My custom message')));

In the corresponding view of this action:

echo $this->Form->input('confirmation', array('type'=>'checkbox', 'label'=>__('Yes, I actually read it', true)));

The user has to check the toggle field provided. Otherwise validation will fail.

Some notes on behavior development

Make sure you return the right boolean value in those beforeValidate() methods etc.
Usually this should be TRUE even if it fails.
The reason is you want to display all errors at once in the form. If you return FALSE too soon, the other validation methods won’t get triggered and you have a staged validation which is a bad thing.
So return TRUE. And don’t worry, as long as there are errors the save method won’t get triggered.

As you can see the settings are always “per Model->alias”. This way multiple models with the same behavior won’t intefere which each other (since they all use the behavior instance – yes, thats the CakePHP way of saving ressources, I guess).

 
No Comments

Posted in CakePHP

 

Cakephp Console on Linux systems

24 May

There are a lot of articles about that out there. Unfortunately, most of them are kind of crappy.
Who wants to add the -app path all the time or use the full folder name with it?
Its annoying!

Setup

Similar to Windows we need to add the path to the environment.
In the case of “debian” systems in the .profile file in the home folder.

nano .profile

At the end of the file simply add your console folder of cake:

PATH=$PATH:/var/www/.../trunk/cake/console

Thats it. Now you need to re-login (..profile might work, too) and it should work.

Result

Now navigate to the app dir of your project: :.../app#.
Then type “cake” or any other command:

cake bake ...

Everything else will work automatically.

CakePHP 2.x

See the updated windows post for the correct path here.
Also note the way more comfortable quicklinks if you need to use 1.x and 2.x parallel.

 
1 Comment

Posted in CakePHP

 

Unit-Testing in CakePHP

03 Apr

Some tips you might not have read about yet

Since the cookbook is quite detailed as far as basic testing is concerned I will not unroll that part too much. Please read those pages if you never wrote unit tests before.

Use your own wrapper

If you use your own testcase-wrapper you will be able to add some common functionality for all test cases – similar to AppModel, AppController or AppHelper.
I called it “my_cake_test_case.php” and put it in the vendors folder:

class MyCakeTestCase extends CakeTestCase {
	# added some time calc stuff - like calculating how long a test needed
	//
 
	# added some more custom stuff
	//
 
	# printing a header element like <h3> at the beginning of each test function
	function _header($title) {
		if (strpos($title, 'test') === 0) {
			$title = substr($title, 4);
			$title = Inflector::humanize(Inflector::underscore($title));
		}
		return '<h3>'.$title.'</h3>';
	}
 
	# overriding the assert rules in order to customize it (like adding detailed descriptions and before/after output)
	function assertEqual($is, $expected, $title = null, $value = null, $message = '%s', $options = array()) {
		$expectation = 'EQUAL';
		$this->_printTitle($expectation, $title, $options);
		$this->_printResults($is, $expected, $value, $options);
		return parent::assertEqual($is, $expected, $message);
	}
 
	function _printTitle($expectation, $title = null) {
		if (!$this->_reporter->params['show_passes']) {
			return false;
		}
		echo $this->_title($expectation, $title);
	}
 
	function _printResults($is, $expected, $pre = null, $status = false) {
		if (!$this->_reporter->params['show_passes']) {
			return false;
		}
 
		if ($pre !== null) {
			echo 'value:';
			pr ($pre);
		}
		echo 'result is:';
		pr($is);
		if (!$status) {
			echo 'result expected:';
			pr ($expected);
		}
	}
}

And much more

Now we can use it in the test cases

App::import('Vendor', 'MyCakeTestCase');
 
class ColorLibTest extends MyCakeTestCase {
 
	function testColDiff() {
		echo $this->_header(__FUNCTION__);
		$was = 'ffffff';
		$expected = 1;
		$is = $this->Color->colDiff($was, '000000');
		$this->assertEqual($is, $expected, null, $was));
	}
 
}

The header will be “ColDiff” – generated automatically using __FUNCTION__ (magic constant).
The detailed messages are only displayed if “Show Passes” (url: show_passes=1) is activated. Otherwise it behaves as usual.

Skipping tests based on conditions

Sometimes tests depend on the system settings, enabled modules etc. If it doesn’t make sense to test certain methods, put this at the top of the test function:

# example with "magic_quotes_sybase"
function testSomething() {
	if ($this->skipIf(ini_get('magic_quotes_sybase') === '1', '%s magic_quotes_sybase is on')) {
		return;
	}
	... // this code will not be executed anymore
}

Always include object integrity first

Cake has a lot of automagic. Sometimes this can hurt, though, if unknown to the programmer.
If a model cannot be initialized it will usually fall back to AppModel – silently! Same with other classes.
So make sure, they are what they are supposed to be.

# this model is not an AppModel instance, but the real deal (Album instance of class Album)
function testAlbumInstance() {
	$this->assertTrue(is_a($this->Album, 'Album'));
}
 
# for a controller
function testAlbumsControllerInstance() {
	$this->assertTrue(is_a($this->Albums, 'AlbumsController'));
}

startCase() or startTest()

Most of the time startCase() is usually enough. This is triggered ONCE at the beginning of all tests. If the class itself does not remember or change anything, this is more than enough.
But as soon as your class (or object) internally keeps information about the last operation, you will get unexpected results. Use startTest() then to ensure, that the object is always a “fresh” one:

function startTest() {
	$this->Color = new ColorLib();
}

Note: setUp() is the same as startTest() but not officially used because it is SimpleTest specific.

 
 

GoogleMapsV3 CakePHP Helper

21 Dec

Google Maps V2 is marked “deprecated”. V3 is supposed to be faster and more compatible to mobile browsers. Details
Also new: A googlemaps key is not necessary anymore.

UPDATE: v1.1 (March 2011) – not backwards-compatible

Usage

I wanted to keep it as simple as possible (otherwise you could use the maps with plain js right away).

You may set up configs/defaults in your config file:

$config['Google'] = array(
	'zoom' => 6,
	'lat' => 51.1,
	'lng' => 11.2,
	'type' => 'H', # Roadmap, Satellite, Hybrid, Terrain
	'size' => array('width'=>'100%', 'height'=>400),
	'staticSize' => '500x450',
);

Now you need to add the helper to the controller (or the action):

# globally
public $helpers = array(..., 'GoogleMapV3');
# OR in the action:
public function map() {
	$this->helpers[] = 'GoogleMapV3';
	# rest of your code
}

Since there are many possible ways to include the required javascript file I formed an own method for it:

<?php
# include Jquery 1.4
$this->Html->script('jquery'); # or wherever it is in your js folder

# include the map js code
$this->Html->script($this->GoogleMapV3->apiUrl());
 
# OR include it manually without cake (or use your own asset stuff)
echo '<script type="text/javascript" src="'.$this->GoogleMapV3->apiUrl().'"></script>';

You may also let the helper include it automatically – just pass the option “autoScript” => true to the map() method in the next step.

Now we can use it.

Interactive Maps

# init map (prints container)
echo $this->GoogleMapV3->map(array('div'=>array('height'=>'400', 'width'=>'100%')));
 
# add markers
$options = array(
	'lat' => 48.95145,
	'lng' => 11.6981,
	'icon'=> 'url_to_icon', # optional
	'title' => 'Some title', # optional
	'content' => '<b>HTML</b> Content for the Bubble/InfoWindow' # optional
);
# tip: use it inside a for loop for multiple markers
$this->GoogleMapV3->addMarker($options);
 
# print js
echo $this->GoogleMapV3->script();

You can also add windows and (custom) events. See the code for details on that.

Static Maps

These are just images. Very handy if you don’t need the js overhead.

# markers
$markers = array(
	array('lat'=>48.2, 'lng'=>11.1),
	array('lat'=>48.1, 'lng'=>11.2, 'color' => 'green', ...)
);
# paths
$paths = array(
	array(
		'path' => array('Berlin', 'Stuttgart'),
		'color' => 'green',
	),
	array(
		'path' => array('44.2,11.1', '43.1,12.2', '44.3,11.3', '43.3,12.3'),
	),
	array(
		'path' => array(array('lat'=>'48.1','lng'=>'11.1'), array('lat'=>'48.4','lng'=>'11.2')), //'Frankfurt'
		'color' => 'red',
		'weight' => 10
	)
);
 
# some options and image attributes
$options = array(
	'size' => '500x400',
	'center' => true,
	'markers' => $this->GoogleMapV3->staticMarkers($markers),
	'paths' => $this->GoogleMapV3->staticPaths($paths),
);
$attr = array(
	'title'=>'Yeah'
);
 
# now display the map image
echo $this->GoogleMapV3->staticMap($options, $attr);
 
# you can even add an url to click on
$attr['url'] = $this->GoogleMapV3->url(array('to'=>'Munich, Germany'));
echo $this->GoogleMapV3->staticMap($options, $attr);

As you can see, we can now mix lat/lng and normal addresses (which get automatically geocoded in the background).

Map Links

If you want to redirect to maps.google.com (for directions etc) you can use this method.

# leave "from" empty for the user
$url = $this->GoogleMapV3->url(array('to'=>'Munich, Germany'));
echo $this->Html->link('Visit Me', $url, array('target'=>'_blank')
 
# coming from a posted form or whatever
$url = $this->GoogleMapV3->url(array('to'=>'Munich, Germany', 'from'=>$from));
echo $this->Html->link('Directions to me', $url, array('target'=>'_blank')

Last but not least

Other updates:
- multiple paths and markers
- visible scope
- custom icons possible

For more examples check out the test case. It contains several more sophisticated examples.

Helper Code

While it is still under development (pending improvements), the code can be found in my github rep:
github.com/dereuromark/cakephp-google-map-v3-helper

 
41 Comments

Posted in CakePHP

 

Preventing Brute Force on Login

11 Nov

With default cake login procedures a user could try unlimited passwords to one specific account.
That means, if you write a bot that tries every possible combination (thousand times per minute!), this bot could eventually gain access to the account. To cloak it, he could use several bots on several different locations or proxies.

Some websites use the session to count the attempts and display a captcha after x failures. Or they use the IP address to block this user for a few minutes. Both is not very secure.
A user could change his session and his IP frequently (depending on the skills of the attacker of course).
Then he would still have unlimited trials.

More secure mechanisms

Just some ideas to discuss here – i didn’t yet test the ideas in real life scenarios.

I don’t like the captcha stuff. I would prefer the login timeout. Either way we need to make sure that everybody – no matter if human, bot or both – gets the same result.

Lets say, our “attacker” targets “benni123″ and wants access.
He tries several combinations and suddenly he cannot login for a few minutes. No matter what he does.
How?

My idea: use the database for it. Store the information based on the record the user wants to validate against.
Every failure the database stores it with the User.user_id of “benni123″ and the time-stamp.
If the amount is greater as 5 for example, the login is always aborted for this user account. The real user cannot login as well, of course. But after a few minutes everything is fine again – and he even knows somebody tried to access the account.

No bot would ever find the right key. Its just not reasonable to write a bot that only tries to access the account every few minutes. It would need hundreds of years with medium-sized passwords.

You could also display a captcha (google does it this way) after x failures. But they “could” be cracked using a specific captcha reader for your site. once this is in place, they provide not much of security anymore. Meaning: The captcha must be complicated enough for the bot but still readable for humans. reality shows that this is not easy to accomplish. Therefore I tend to use captchas only to slow too active users down (prevent spamming, message flooding, etc).

Additionally we want a php script timeout (wait x seconds to respond) after unsuccessfull attempts.
This way the user doesn’t really notice but any brute force script would need remarkably longer for a chain of attempts.

In the end we could do this:
Display a captcha after maybe 5 wrong trials. After another 5 correct captcha inputs but incorrect password inputs (important! we dont want this to happen if the catpcha is wrong – otherwise an attacker could easily shut down all accounts) shut the account down for a specific timeout. Of course, if the valid user tries to access the account in that time he needs to get a proper error message.

Implementation

Using the timeout method we extend the core auth component:
Component AuthExt:

/**
 * override auth login
 * @override
 */
function login($data = null) {
	if (!$this->loginExt($data)) {
		# security timeout?
		sleep(1); # 1 second for right now
		return $this->_loggedIn;
	}
 
	$model = $this->getModel();
 
	// do final stuff (getting roles and additional session infos, raise login counter, ...)
 
	return $this->_loggedIn;
}
 
function loginExt($data = null) {
	$this->__setDefaults();
	$this->_loggedIn = false;
 
	if (empty($data)) {
		$data = $this->data;
	}
 
	# check against database entries if user is temporary banned
	if ($this->floodProtectionActive($data)) { # TODO: implement
		return $this->_loggedIn;
	}
 
	if ($user = $this->identify($data)) {
 
		// here we can check if other reasons prevent returning true (suspended, account not active yet, ...)
 
		return $this->_loggedIn;
	}
	# seems like we need to log this attempt as a failure
	$this->floodProtectionUpdate($data); # TODO: implement

	return $this->_loggedIn;
}

Security Layers

Level 1: Javascript (Timeouts, …)
– LOW!
– Can easily be bypassed

Level 2: Captchas, IP/Session based login monitoring, Sleep-Timeouts
– MEDIUM
– Can be spoofed/hacked (with some expertise anyway)

Level 3: Database based monitoring (Errors per user and login timeouts)
– HIGH
– Can be used against members (blocking access for valid users)

Final words

You would need a garbage collector for the database table. The last x+1 records are more than enough (except you want to debug the information or transform them into statistics).

Using only captchas:
Assuming that captchas are still as secure as we would like them to be, this is the easiest implementation.
After x failures, an additional captcha has to be entered.
The real user would get a captcha right away, if an attacker just tried to access his account. So no delay at all.
The bot, though, should fail due to the captcha in place.

Using a script timeout:
As mentioned above, the script timeout (sleep command/function) is really helpful in order to slow down brute force attempts.
A few seconds are more than enough. Normally 100 attempts need 5 seconds, with this timeout they will need more than 200.
Assuming that the attacker doesn’t use 100 parallel connections, of course.

Using a block timeout:
The real user could be identified by cookies, specific browser fingerprint etc.
This in combination with the correct password in the first place could override the above protection mechanism. allowing the user to enter EVEN if the timeout is in place due to too many failures.
The attacker should not be able to guess the fingerprint, though.
Maybe the “browser user agent” combined with a cookie information (maybe even with the IP, although it could already have changed). Even if the attacker knew that both are used for the override, he would have too many combinations ABOVE the main problem: the correct password.
He would need to know the content of the encrypted cookie as well as the exact browser version used from the real user the last time he logged on.

Combining them:
This way it is most secure on application level. Even if a working captcha reader is found for your captchas, the attacker will not be able to brute force login.

One advantage of the database log tables:
You can easily monitor the fails and get a pretty good picture how many “brute force attacks” have actually been commited on your website.

What do you think? And is the possible “override” maybe the key for vulnerabilities again? At least for a specific target this might be plausible.

 
3 Comments

Posted in CakePHP

 

Frequent CakePHP problems and solutions

28 Oct

On Google Groups you read about some problems over and over. So i thought, why not putting the most frequent ones together.

Add/Edit Post Url

If you have an url like “/products/add/import:1/manufacturer:4″ and you want to preserve the url on POST, you will need this snippet:

echo $this->Form->create('Product', array('url'=>'/'.$this->params['url']['url']));

This way it will always post to the same url. The default behavior is to post to “/products/add” in this example above.

Adding user_id or other model related data on the fly

Some use hidden inputs for that. Very bad style! Never ever do that unless you have a damn good reason :) . It can easily be manipulated (the security component will not help here at all!) and is absolute nonsense.
Just add it to the array before passing it to the model:

if (!empty($this->data)) {
        $this->Product->create();
        $this->data['Product']['user_id'] = $this->Session-
>read('Auth.User.id');
 
        if ($this->Product->save($this->data)) {
                ...
        }
        ...
}

Uploading Files

echo $this->Form->create('Model', array('type' => 'file'));
...
echo $this->Form->input('field', array('type' => 'file'));
...
echo $this->Form->end();

Both form and input need to be “file” in order to upload files.

Productive Server: “Error: Page not found”

If you just upload your files to your webserver, you might result in this error message. Although everything is supposed to work.
Make sure, that you erase all tmp files after you uploaded/modified files (especially if model-changes are involved). Otherwise it will try to work with the wrong files, which of course fails.

UTF8

Warning (2): htmlspecialchars(): Invalid multibyte sequence in argument in [/var/www/html/cake/basics.php, line 207]
I assume your application runs with utf8 as app encoding.

Usually this happens, if you forgot to save this file as utf8. It will then pass an invalid string to h(). In most cases it is a view (.ctp).
So make sure all views (and maybe even controllers for session flash messages and models for validation rules) that contain special chars are saved as utf8.
Tip: If you don’t know where it happens, use a stack trace to find the file causing this. Either manually by adding the stracktrace to the file_log.php or by using my cakephp addon “Proper logging feature”.

Cannot modify header

Cannot modify header information - headers already sent by (output started at C:\...\cake\cake\basics.php:305) [CORE\cake\libs\controller\controller.php, line 646]
This usually happens if you have ANY character printed out before the view actually renders. It can be a single space after the closing php tag of a php file (controller, model, …). Therefore you should not use ?> at the end of php files (CakePHP took the same path in summer 2010). It prevents this from happening.

Not found

It is very important to clear the “models” cache after manually changing anything on the database (tables). Also clear the “persistent” cache folder.
Otherwise it tries to use the old cached files which result in errors. With debug > 0 this is done automatically.

Ajax File Upload

You cannot upload files with AJAX itself. This will not work. Everything except for binary data.
There are workarounds, though. Jquery, for instance, uses hidden iframes.
So don’t use the ajax helper, but Jquery plugins or any other JS package to upload ajax files. Those usually use the above technique to workaround this problem.

Misc

… coming up

 
No Comments

Posted in CakePHP

 

Complete CakePHP naming conventions

08 Oct

For details into some aspects take a look at my old “coding standards” summary.

Database

Database tables are plural: “comments” or “user_comments” etc.

I recommend to stick to conventions and underscore + lowercase all table fields: “last_login” (instead of “lastLogin” or even worse “last login”) etc.

Models

Model names are singular and camelCased: “Comment or “UserComment”
filename: “comment.php” or “user_comment.php”

Controllers

Model names are plural and camelCased: “Comments or “UserComments”
filename: “comments_controller.php” or “user_comments_controller.php”

Controller actions should be underscored! “function admin_import_from_xml(){}”

Views

Views have an own folder for each controller – plural: “comments” or “user_comments”.
Inside are the templates for the specific actions- underscored.
filenames for example: “index.ctp” or “admin_index.ctp” (with prefix admin) or “admin_order_entries.ctp”

Libs

As long as namespaces are not an issue, they might conflict with existing model classes (they have no “Model” appended!) and other cake core classes or even some vendor files.
I recommend to use the controller syntax here:
Name – camelcased: “GoogleTranslateLib”
filename: “google_translate_lib.php”

This way it won’t interfere with any Google class – or “FileLib” won’t interfere with core “File” class.

Behaviors

Name – camelcased: “GeoPlugin”
filename: “geo_plugin.php”

They should not be similar to model names. Otherwise they might interfere with existing models.
So use adjectives, verbs or plural names: “Ratable”, “Geocoded”, “Configurations”, …

Components

Name – camelcased: “GoogleTranslateComponent”
filename: “google_translate.php”

They could interfere with models (and maybe behaviors). So it might make sense to use plural forms or forms that are not likely to be a model name.

Helpers

Name – camelcased: “GoogleHelper”
filename: “google.php”

With the new 1.3 “$this->Helper” style they can now be anything you want. With a little modification of the core, at least.
I like to call my helpers “MyHelper” if there are specific to a controller/module and it is not yet a plugin.
Example:
Conversations controller + Conversation model + MyConversation helper (as it is only needed inside this specific controller).
Other helpers like “GoogleMap” might be used in several controllers, for instance.

Plugins

They should not be similar to controller names. Otherwise they might interfere with existing controllers.
So use adjectives, verbs or singular names: “Rating”, “Setup”, “Configuration”, …

Constants

No matter if they are class constants or global constants, they are always uppercase and underscored:
“MIN_USER_AGE” etc.

Variables

camelBacked: $userComment

The first letter is uppercase if the variable represents an object:
$File = new File();
$GeoPlugin = new GeoPluginLib();

Layout/Script

CSS classes/ids:
I use camelBacked style here, too: <div class="someClass" id="someId"></div>
But thats purely a matter of taste.
I like to use it in IDs because I can easily attach “-x” (the record id) and split it afterwards on “-” to get the record id in JS:
… id=”someId-2″…
comes in handy sometimes

Test Cases

Same as the original file – only with “.test” appended to it: “google_translate_lib.test.php” for instance.

 
No Comments

Posted in CakePHP

 

CakePHP Beginner Tips

07 Oct

Apache Server

For Windows use WAMPServer2 and select “Apache” => “Apache Modules” => “rewrite_module”.
Thats all there is. Cake should now work out of the box without any other modifications.

Database

Use UTF8 and utf8_unicode_ci (not “utf8_general_ci”!) for all tables. general might be a little faster, but is also more inaccurate in sorting.

Don’t use NULL as default value, if you can avoid it. There are some issues you need to work around, otherwise. So always try to use default ’0′ for int fields, default ” (empty string) for varchar/char etc. For a list of database related issues see problems-with-null.

Use tinyint(1) ONLY for toggle fields (0/1) – represented by checkboxes. For anything else (select box with 0,1,… and more elements) you need tinyint(2) or even int(x).
Always use “id” as primary key”. This convention helps in the long run. It should be int(10) UNSIGNED – a so called AIIDs. Another option would be to use UUIDs. They are unique across the whole database and are char(36).

“created” and “modified” are handled by cake automatically if present on every INSERT/UPDATE. So don’t call them anything else if you don’t have to.
I recommend to stick to conventions and underscore + lowercase all table fields: “last_login” (instead of “lastLogin” or even worse “last login”) etc.

Other automagic fields are of type text/mediumtext/longtext which result in a textarea.

Do not use “enums” as they are not supported. There are actually even better ways to workaround this.

The “length” of a CHAR/VARCHAR field gets read out by Cake with “DESCRIBE” and is stored in the cache.
It is used to set the “maxlength” attributes for <input type="text"> form fields, which are created by the form helper. If you want the username only to be 20 chars, the form will not allow 21. Check it out. You still need to validate this, though, in the model (as it can be hacked).

General tips

Always develop with debug > 0 (usually 2 for detailed sql queries at the bottom).

Find conditions

This doesnt work:

->find('all', array('conditions'=>array('user_id'=>'', 'user_id'=>NULL)));

same with

->find('all', array('conditions'=>array('OR'=>array('user_id'=>2, user_id=>3))));

You cannot use array keys twice in the same array. Either put them in a subarray inside the array or combine them right away:

->find('all', array('conditions'=>array(array('user_id'=>''), array(user_id=>NULL))));
->find('all', array('conditions'=>array('OR'=>array('user_id'=>array(2, 3)))));

For the second example CakePHP with automatically use IN (…) instead of =.

Forms

If you need to pass default values to select fields or any other input, do NOT use inline params like ‘selected=”xyz”‘. As soon as you submit your form it will restore extactly the same default value – no matter what the user changed. But thats not common sense. Usually, if an error occured and you have to correct your input you want everything to be same as before – only changing the required field.
So the correct approach is to pass it from the controller:

if (!empty($this->data)) {
	// validate and save
} else {
	// now thats the important part here
	$this->data['Model']['field'] = 1; // or whatever your default value is supposed to be
}

For details see here.

If you want to submit the form to the same url, use:

$this->Form->create('OtherModel', array('url' => '/'.$this->params['url']['url']));

It will preserv all params like “/controller/action/123/uid:xyz” (usually you would submit to “controller/action” or “controller/action/123″).

Links

Always use arrays for internal links:

$this->Html->link(array('controller'=>'x', 'action'=>'y', 'some_pass_var', 'some_named_var'=>'z'));

This way your application is flexible and you can always route the url. If you hard-code it as string this won’t be possible.

AppController callbacks

beforeFilter and beforeRender as well as afterFilter are available for app wide stuff to be handled.
Be careful though:
If you want to use whatever you do there in the layout (on all views), you need to use beforeRender() only. That’s because only beforeRender() is triggered if an error occurs. The other 2 not!
Especially if you need the variables passed to the view in some elements etc you will otherwise get notices and errors.

 
4 Comments

Posted in CakePHP