CakePHP Security

When I first started with CakePHP I didn’t know about those things, either.
Everything was new and I was overwhelmed by all the functionality of the framework.

The basic rule is: Validate input, escape/sanitize output

Input

I already wrote an article about Saving Model Data and Security. It covers some aspects of the saving process.
You are even quicker if you include the Security Component in the app controller. This automatically takes care of form manipulation and other things like Cross-Site Request Forgery (CSRF).

Cake already takes care of the usual risks by escaping all queries (except for the manually built ones).

Some use Sanitize or other things prior to the save to ensure that the content is stripped of any harmful strings. I dont think this is a good approach. At least not in general. Usually the user wants to post something which intentionality consists of some special characters. He would be quite upset if all kinds of things are stripped out just because the developer felt like it. It’s overkill and unnecessary for text input.
On the other hand, if you allow HTML (with a wysiwyg editor?), you cannot escape the output. Therefore it makes sense in this case to protect your site with something like "htmlpurifier".

My opinion is: ONLY validate, do not sanitize or strip things off the input. You have plenty of time to do that on output (see below).

Output

The other part is the view and potential risks from xss attacks etc.
It is actually simpler than you might think. All CakePHP helpers already escape by default ($this->Html->link() for instance).
If you output database content directly in the view you need to escape those text strings manually, though.
This is done with the h() function (conv. function of htmlspecialchars):

echo h($model['Model']['comment']);

No matter what the user posted, it is now harmless (javascript, xss stuff).

So to sum it up: Escape all stringish values coming from DB via h() (varchar, text, …).

Many forget that even image titles or alt attributes must be escaped. So if you don’t use the helper for it, escape manually. A good testing environment has always some of those "bad strings" in the database. Just insert some of the first examples on this XSS site. If you don’t get "alerts" in your views, you know that you did everything right.
I once wrote a lib + shell script that extracted all those xss strings (xml is available) and automatically added them to the specified model. After calling the "index" overview I found out that nothing happened – which is always a good sign in this case.

Flash messages are often forgotten, as well. If you use user input in them, escape it:

$name = $this->data['Model']['title'];
$this->Session->setFlash(sprintf(__('record \'%s\' saved', true), h($name))); // prints "record {title} saved"

POST/GET

To protect yourself from a very unpleasant attack which can make you delete all kinds of content, you should always ensure that database modifications (INSERT, UPDATE, DELETE) can only be triggered by POST actions (ajax or not ajax). This is not for no reason the specification for HTTP. "GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval".
Currently – using the default bake templates – you would allow DELETE simply by calling "/controllers/delete/ID".
So you might want to customize your scripts. Use a form submit button to post to the url (or jquery) and blackhole the request if it is a GET request.

What can happen if you don’t follow the specification? If you don’t allow any HTML probably nothing. But let’s assume you allow BBCode or HTML – both properly sanitizes/cleaned.
I will give you one example with "comments" on a blog post. The result of the user comment might be:

<b>Blablabla</b>, <img src="/admin/users/delete/3" /> FooBar.

Normal users will just see a scrambled image (because they don’t have the admin role). The url can only delete a user if you are currently logged in as an admin.
Note: An image tag ALWAYS assumes that the src is valid. It will call anything you put in there as url. Even urls that aren’t any image. So the url redirects inside the image tag (no access without admin role).

Ok, but now the admin logs on and checks out the post. Well, he DOES have the right to delete users. And therefore the img tag automatically deletes the user (because GET requests are allowed). The admin doesn’t even notice. Combine some of those delete actions and you can really mess things up. Especially if cascading is on and all kinds of user related content (users hasMany posts, …) is removed as well.

Miscellaneous

Be especially careful with user submitted data or $_GET params if they are about file names, urls or similar system related information.
They can have ../../ in them which could lead to vulnerabilities.

Webserver

If you host the website on a managed server or even a root server, you need to make sure that the server is secure. That includes the following:

  • Keep the packages up to date (“apt-get update” + “apt-get upgrade”)
  • Register Globals OFF, Magic Quotes OFF (Cake handles this!)
  • Hide your exact server version with ServerSignature Off

Spamming

Meaning: Overloading a system through excessive traffic can lead to denial of service for other users or system failure.
This can happen if you allow a bot to post public accessible forms (registration etc). He could sign up millions of users in just a few hours filling your database tables to the outer limit. You can use timeouts or captchas to prevent this (careful: try not to use session based stuff because bots change their session all the time).
Timeouts also prevent "brute force / dictionary attacks".

Another cake-related overloading attempt: "limit" on pagination. Anybody who knows that CakePHP is used, could simply append "/index/limit:1000000". If there actually are that many entries it will most certainly cause excessive traffic. Currently you have to implement this yourself. Ticket still not resolved!

Conclusion

That’s it, basically.
As you can see it is not that difficult to secure your app.

Tip: Make use of baking scripts and custom templates! This way you got h() and other security measures right in the baked views. That’s a real timesaver.

There are some other aspects I did not yet mension: session hijacking etc. Maybe I catch up on that later on.
Anything else I might have missed?

Security Update 2013-06

There have been some security vulnerabilities that have been discovered in the recent past. It is vital that you upgrade your core to at least version 2.3.6 to be on the safe side!
Also test your app from time to time against common issues like XSS, Form tempering, SQL injection etc to detect those flaws first before others can make use of them.

9 Comments

  1. thanks, the post helps me very much.
    I have developed with CakePHP since 3 years, from version 1.1. But when I read your site, I think I know…nothing about CakePHP. Thanks again.

  2. Ah, good to know – thanks for the info!

    I believe Rails recently changed their default behavior to html-escape in view. It used to be you had to call an h() function, but in Rails 3 (and I think newer Rails 2.x) it’s automatically escaped unless you call raw().

    That might be nice for CakePHP as well. Seems a little safer. So, it would be great if was escaped and if I wanted the raw data output.

  3. Is there a global method of sanitizing the HTML output? Having to worry about using h() with every "echo" statement is going to be missed eventually.

  4. well, but that’s just the way to go 😉
    of course you can do stuff like that by default. but its usually quite some overhead – especially if you need it plain at some point (and you need to reverse that again)

  5. Hello,
    I’m playing with CakePHP 3 since shortly. About your point "POST/GET", Bake now generates:

    $this->Form->postLink(__('Delete'), ['action' => 'delete', $user->id], ['confirm' => __('Are you sure you want to delete # {0}?', $user->id)])

    and in the controller adds:

    $this->request->allowMethod(['post', 'delete']);

    Thanks for this post, it’s always a good reading.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.