29 Jan

CSRF

CSRF (cross-site request forgery) are hacks where a user on one system is tricked into doing something on that system while browsing another system.

Example

Let’s say you are logged into http://yoursite.example.com/ as an admin, and you can easily delete an object by clicking a link that sends a request to http://yoursite.example.com/a/delete.php?object=1.

You take a break and go read some websites.

Now, let’s say that one of those websites has had a little piece of code attached

<img src="http://yoursite.example.com/a/delete.php?object=1" style="display:none"/>

Other readers of the same site will not notice anything – they’re not logged into your site, and so have no delete rights. But, you are!

This vulnerability is called “CSRF” because the hack happens on a different website than your own, taking advantage of the fact that you are logged in, to delete stuff (or move money, etc).

Solution

On the server, you should create a CSRF token, send it to the client, and make sure that all actions that are requested include that token.

To set the token, just create a random string and save it to your session:

<?php
if (!isset($_SESSION['csrf'])) {
  $_SESSION['csrf']=md5(mt_rand().time());
}

Then, whenever an action is performed, make sure that the request includes that token before the action is performed.

<?php
if (!((isset($_REQUEST['_csrf']) && $_REQUEST['_csrf']==$_SESSION['csrf'])
  || apache_request_headers()['X-CSRF']==$_SESSION['csrf'])
) {
  header('Content-type: text/json');
  echo json_encode(array(
    'error'=>'CSRF violation'
  ));
  exit;
}

Note that my code above allows two ways to send the CSRF – as a request variable (GET/PUT/POST), or as a header.

For HTML forms, make sure that each form includes the CSRF token:

<input name="_csrf" value="<?=$_SESSION['csrf'];?>"/>

And finally, for AJAX, make sure that the token is included by default. Personally I use jQuery, so this does that:

  $.ajaxSetup({
    'beforeSend': function(xhr) {
      xhr.setRequestHeader('X-CSRF', window.csrf);
    }
  });

(make sure that window.csrf is set as inline javascript in the page)

Conclusion

Now what happens is that each time a request is made to the server, the CSRF token that’s sent is checked against the session’s CSRF token, and if they don’t match (or no token is sent), then the action is ignored.

It is not possible for any website to guess your CSRF token (we set it to a random MD5), so you are safe.

13 Aug

new API for WebME

As I said in the last post, an API would be required to make the system more testable and more consistent.

I started straight away and wrote up something quickly. Over the next week, it solidified into something that appears to cover any needs that I have.

So here’s how the API works:

Requests are sent to a URL which is generated like this:

/a
[/p=plugin-name]
/f=function-name
[/other-parameters]

The plugin name is optional. Leaving it out means you want to call a core function.

Parameters can be added by adding /key=value pairs to the URL.

An example URL might be this:

/a/f=login/email=kae@verens.com

Sending that, with a POST parameter for the password, will log me in.

To log out, I can use this URL:

/a/f=logout

Simple!

Function Names

mod_rewrite is used to direct a request through a script which tears the URL apart into parameters.

If a p parameter is given, the function is named after the plugin, rewritten to match the WebME coding standard.

For example, if the URL is /a/p=comments/f=editComment, then the “comments” part is rewritten as Comments, and ‘_’ and “editComment” are appended to form the function name “Comments_editComment”, which is called and the result returned to the browser.

For double-barrel plugin names, such as “image-gallery”, the name is rewritten to “ImageGallery”.

If no p parameter is given, then the request is a core function, and “Core_” is prepended to the function name.

For example, the login URL above, /a/p=login calls the function Core_login.

If a function name begins with “admin”, it is an admin function (see below for more on this).

File names

If no plugin name is supplied, then the core API file, /ww.incs/api-funcs.php is loaded. This contained common API functions that might be used by any core script or plugin.

If a plugin name is supplied, then the API file is expected to be located at /ww.plugins/plugin-name/api.php for common functions, and /ww.plugins/plugin-name/api-admin.php for admin functions.

For core functions, common functions are at /ww.incs/api-funcs.php and admin functions are at /ww.incs/api-admin.php

Security things

Having a central point for RPC means that we can apply security rules in one place and know that they cover all scripts. Before-hand, I would sometimes come across scripts and realise that they were open for abuse if someone knows that magic URL incantation. I would silently curse myself or whoever had written the script and fix it up. Now, though, having one single point of entry means I can secure everything at once.

If a function name starts with “admin”, then the script checks to see if the user is logged in and is a member of the administrators group. If not, the API will return an error. It’s as simple as that!

Of course, this doesn’t stop abuse by people that are logged in as admins or who are victims of XSS, but it helps stop a few problems caused by developers not noticing their scripts were open to use by anyone at all.

Conclusion

So now, when people are creating new plugins for WebME, the following could be used as a bare-bones directory structure:

/ww.plugins/plugin-name/plugin.php    details, server-side functions
/ww.plugins/plugin-name/api.php       common RPC functions
/ww.plugins/plugin-name/api-admin.php admin RPC functions
/ww.plugins/plugin-name/admin.js      admin scripts in JS
06 Aug

testing KV-WebME

I’ve been working on my CMS for about 10 years. It’s monstrously huge (41,000 lines, not including external libraries), and for most of those 10 years, I’ve been too busy building it to concentrate on niceties such as comments, testing, code formatting, etc.

This has caused problems in the past. As most programmers know, when you change any one thing, it has a ripple effect and can break things in places that don’t seem obvious at all.

Recently I’ve been remedying this. I’ve been religiously using PHPCS to make sure my code is neat and consistent, and I’ve started writing a test suite.

The most difficult part of the testing is that the CMS is composed of many separate technologies. If it was just a plain old HTML and PHP application, then PHPUnit would be enough, or maybe Selenium.

The problem is, though, that the system uses a large amount of AJAX – especially in the administration areas. No single testing system would do it all.

Another problem has to do with AJAX itself. In jQuery, you can speak to the server by writing something like this:

$.post('/a/server/script.php', {
  "id": 2
}, function(res) {
  // do stuff
}, 'json');

This makes it incredibly simple to speak to any server-side script at all on the server, and promotes it. It becomes tempting when writing new functionality to build new server-side scripts specifically for the new client-side stuff.

This has the effect that there is no single point for RPC (remote procedure calls) which can be tested, making it very difficult to be sure you have covered all potential problems.

To help solve this problem, I’ve recently started converting WebME’s coding style so all RPC is done through a single API (application programming interface) script.

This has a few extra effects which are beneficial:

  • Having a single point of entry into the system makes it easier to secure it.
  • Having an API promotes the construction of a solid method of adding functionality to it – there’s no need to start from scratch anymore, potentially building disparate scripts that are hard to abstract. Instead, it’s now easy to force the code to match a minimum spec.
  • APIs tend to have specific rules for how parameters are passed into it, making it easier to remember what the right parameters are when writing new client-side code. Also, it makes it easier to “guess” what the right parameters are if you’ve forgotten.

The main benefit, though, is that it makes it much easier to test. The URL of the API always stays the same, and the only thing that changes is the parameters sent to the URL. Previously, each separate script would have a different URL and could have any parameter scheme at all.

So, currently, I’m writing tests that use the API directly, speaking to the server directly through URL calls. After I’ve finished writing all of those (hah! if ever), I can get on to testing that JavaScript.

23 Jun

growing up

For a long time, whenever I did something that I thought was interesting, I would write it in an article.

I don’t write as much as I used to. Not because what I’m doing is not interesting, but because it’s taking a lot longer now to complete the interesting jobs, now that they’re basically full projects and not just little snippets here and there.

As an example, we’re doing some interesting work over in the KV WebME project. The most recent is an upgrade to image galleries allowing the gallery layouts to be defined using templates, instead of saying “you want layout 1, or layout 2?”. This work is then also used in the products plugin to let people sell images in their online stores.

There are a number of bits in that project that deserve full posts themselves, but as I basically commissioned the piece and got others to do the work, it’s no longer mine to describe. For me, the cool little tricks are now just a smaller part of a bigger picture.

The bigger picture right now is 20eu.com, where you can create your own online store within literally minutes for only €20 (compare that to the “free” getting business online project, which doesn’t have an e-commerce aspect).

It’s now harder for me to write about, because there are no longer single cool aspects that I can point to that can be re-used by other people.

I was walking to work today with Conor (an employee), discussing stuff along these lines, and these points stood out:

  • In the beginning, I was a programmer, and every task had something new to me but nothing I could write about that would interest experienced programmers.
  • After a few years, I was a good programmer, and I did less tasks, but they were larger, and there were often aspects to them that were brand new, so I ended up being one of the first to build them (it helps that my field is Ajax, which is basically new-born).
  • And now, I’m tired of being just a programmer and have started branching into managing other programmers. I’m more interested now in getting full projects done than in the nitty-gritty.

Unfortunately, this means there is less to talk about that is even vaguely techie. I feel like I’m shifting focus into marketing and project management.

Ick!

On the plus side (for me), it means that eventually, I’ll have enough resources that I can get the projects done that I’ve always wanted to do.

So, I plan on starting to write about the business end of my work.

Don’t worry – I’ll categorise it correctly, so if you’re only interested in my PHP or Linux posts, then just change your reader settings to only read from those RSS feeds.

12 Sep

saorfm: first visible results

The first visible project using SaorFM is a jQuery file-selection widget:


demo

Here is the source of that page:

<html>
	<head>
	</head>
	<body>
		<p>Select a file</p>
		<input id="file-select" />

		<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" type="text/css" />
		<link rel="stylesheet" href="/saorfm/jquery-widgets/jquery.saorfm/jquery.saorfm.css" type="text/css" />

		<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
		<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.js"></script>
		<script src="/saorfm/jquery-widgets/jquery.saorfm/jquery.saorfm.js"></script>
		<script>
			$(function(){
				$('#file-select').saorfm({
					"rpc":"/saorfm/core/rpc.php"
				});
			});
		</script>
	</body>
</html>

In the above demo, we link in the jQuery and jQuery-UI scripts, and load up the widget’s script.

The only content elements are a <p> telling the user to choose a file, and a normal text <input> box which the user can use as a combobox.

The magic happens with this little bit of JavaScript:

$(function(){
	$('#file-select').saorfm({
		"rpc":"/saorfm/core/rpc.php"
	});
});

That tells jQuery to convert the #file-select input box into a saorfm widget. The only parameter in this demo is rpc, which tells the widget where the SaorFM server is to be found online.

To install this on your own server, all you need to do is download the SaorFM engine from Google Code, and add a config.php file to the core directory. Here’s mine:

<?php
$SaorFM_config='{"user_files_directory":"\/home\/verens\/domains\/demo.verens.com\/saorfm","language":"en","json_errors":false}';

In that, a JSON-encoded hash object contains the configuration. Change the user_files_directory parameter to your own files directory, and you are done with the configuration!

I’ll be adding this to my WebME project next week, after I’ve finished writing the tests (Selenium and PHPUnit).

28 Aug

SaorFM, first few commits

Last Sunday, I announced that Conor and myself were starting a new file manager called SaorFM.

We started out slow, because we want to have absolutely everything in this to be checked by PHPUnit (for console-style testing) or Selenium (for GUI testing).

We’ve got enough written that you can do the following:

  • Write and save a config file.
  • Rename/Move files.
  • Delete files/directories.
  • List the contents of a directory.

All of the above have automated tests, so we’re fairly sure they’re correct. The main class’s source is readable here, and the test’s source is readable here.

Conor is currently adding support for SaorFM into his own CMS, Furasta. I’ll be working on adding it to my own tomorrow (got relatives coming over today – busy busy!).

We need to add some more functionality to make this basically complete:

  • Move directories.
  • Upload files.

After that, things get interesting, as we’ll be adding a plugin architecture.

Tomorrow, I’ll be finishing up the basics, and will write some simple jQuery plugins to select files, select directories, upload files, etc.

Soon, we’ll be catching up on KFM in functionality ;-).

27 Aug

keeping an admin session active

I had a call from a client who asked why, after logging into a CMS admin area and spending an hour or so writing a document, she was unable to submit it because it claimed she was not logged in.

The answer was that the session had expired.

On busy servers, one method of optimisation is to reduce the session-time. This makes it easier for the server to cope with a large number of visitors, but also has the undesired effect of logging people out if they take their time over anything.

One solution to this is to keep the admin session in a database table, tied to a cookie in the browser. Unfortunately, that means that every time the browser sends the cookie, it must be verified, whereas a session is usually trusted.

The workaround is to use some JavaScript to refresh the session every now and then.

I wrote a simple cyclical script which polled the server every minute to refresh the session.

Here it is, using jQuery to handle the AJAX:

function keep_session_alive(){
  setTimeout(keep_session_alive,60000);
  $.get('/ww.admin/keepalive.php');
}
setTimeout(keep_session_alive,60000);

And the server-side code is this:

<?php
session_start();

Very very simple trick. The polling could be enhanced, if you want, to alert the admin of anything interesting that’s happened on the server.

22 Aug

SaorFM

Last night, I spent four hours chatting with Conor MacAoidh.

We’re both the authors of CMSes, and both need file managers.

I’m the original creator of KFM, but recently, I’ve been getting annoyed at it. The project has grown too large to be easily managed, and it’s slow to start up because of the amount of database configuration involved.

We discussed this, and came up with a plan, which coincides with what I wanted to do for KFM2, but is probably much better.

We are going to reboot the whole thing – write a complete new file manager from scratch. It will only use code from the original KFM2 if the code is demonstrably better than any alternative we come up with.

The project will be properly documented, will have 100% test coverage, and will be completely free.

It will come in a number of separate parts, but only one, the core, will be absolutely needed.

The core of the engine is the bit which handles the actual file management. It will be designed to load in only two or three files for the most part, and as fast as possible.

Communication with the core will be done by either including the core as part of your own CMS, or by interacting with it via RPC.

The RPC will be very important – you send a command such as /rpc.php?action=move&from=/my-files/test1.jpg&to=/images/me.jpg, and results will be returned as JSON.

We decided on the name SaorFM. While this may be slightly confusing for non-Irish-speakers (“Saor”, pronounced half-way between “sair” and “seer”, means “Free”), we feel this is not very important. After all, Ubuntu is a household name, and that’s Bantu.

The main site will be SaorFM.org, the blog will be here, and downloads, issue tracker and SVN can be found here.

We’re still deciding on how to go about things, so there are no downloads yet. The decision to do this was made literally last night.

Having a co-developer on board from the absolute start will encourage me to get my arse in gear on this – if Conor does something cool, I have to beat that. And vice-versa, hopefully!

I’m starting the project off at the moment by working on a description of what it’s all about, and then will start writing some starter tests. This will use “test-driven development”, so every single line of code in this project will be repeatedly tested throughout the development.

We’re planning a load of features, such as desktop-/system- integration for Linux, Mac and Windows, and having totally external UI systems. We even considered going mad and creating a bump-top-like UI for it.

It’s taken me almost a year since planning KFM2 and getting to this.

Part of the reason for the delay is that this is so far removed from the current KFM, that I really didn’t know how to bring KFM1 up to the specification I wanted to reach.

Starting from absolute scratch with a brand new name is the right thing to do, I think.

27 May

happy birthday KFM

KFM is 4 years old today:

http://verens.com/2006/05/27/file-manager-for-fckeditor-day-one/

demos of older versions no longer available, but it’s interesting to know that it’s lasted four years!

of particular interest in my own case, is that there has been no major functionality change in the last two years or so. KFM today is basically the same as KFM two years ago; just faster, more secure, and better coded.