HTML Logo by World Wide Web Consortium ( Click to learn more about our commitment to accessibility and standards.

Moving forward with Composr

ocPortal has been relaunched as Composr CMS, which is now in beta. ocPortal 9 will be superseded by Composr 10.

Head over to for our new site, and to our migration roadmap. Existing ocPortal member accounts have been mirrored.

ocPortal Developer's Guide: Security

» Return to Contents

Security is treated very seriously by ocProducts. We have implemented a number of abstractions and policies to keep ocPortal as secure as possible and it is crucial that developers understand security issues and our policies.

There are a number of notorious ways PHP scripts are subject to attack:
  • SQL injection
  • XSS attack
  • Header poisoning
  • Globals injection
  • Code uploading
  • Config file accessing
  • Code inclusion
  • Jumping-in
  • Attack-by-proxy (form/URL direction)
  • Direct-URL-access
  • Eval-poisoning

SQL injection is prevented by the database abstraction layer. It is impossible to inject SQL through this layer as the SQL calls are made up by a formal conversion from PHP parameter syntax to SQL syntax by code that fully considers escaping strings and handling the-given data types accordingly.
Any manually assembled queries should be assembled very carefully using db_escape_string (to escape strings) and intval (to enforce integers not to not contain string data).
Don't give the hackers a chance by making any assumptions - even if a function is building up a query that inserts the output from the time() function, indirectly, the $time value should be intval'd… just in case somehow it could have been poisoned along the way.

Any perceived hack attack should be logged using log_hack_attack_and_die, so that site staff may detect when people are trying to hack or probe the site. Whilst hackers with the source code may avoid this, it is likely they will make mistakes and get logged, and it is likely potential hackers aren't consulting the code whilst doing so.

XSS is short-hand for "cross-site-scripting", and is hacking by poisoning of a users HTML output. There are two common ways this can occur:
  • through URL's
  • through submitted data
The general premise is that through some kind of invalid input, JavaScript was inserted into the HTML output. This kind of hack is indirect - invalid output is generated, then someone is tricked, or naturally directed, to view that output.
The JavaScript in the output usually does something particularly malicious, like redirecting someone's password cookie to a hackers website or user account.
ocPortal protects against this kind of attack in three major ways
  • escaped values in templates
  • parameter constraints
  • Comcode
Every value in a template that is not meant to contain HTML is marked as an escaped value ({VALUE*}). This means that 'html entities' are put in replacement of HTML control characters, meaning it is impossible for data to contain HTML tags.
By forcing input values that are meant to be integers to actually being integers, many potential attacks can be removed. You can't embed HTML if you can only pass a number into the output!
Comcode doesn't allow unprivileged members to get arbitrary HTML or malicious JavaScript into site data. By forcing members to give their submitted input in a way that cannot poison, that problem is eliminated.

Header poisoning is a technique whereby malicious header information is inserted to totally bypass HTML restrictions… because the attack operates at the header level. It is therefore important that headers with new-line symbols do not ever get inserted.

register_globals is the scourge of PHP security. Originally PHP was made easy, by allowing variables to be injected directly into the global namespace of a PHP script.. so parameters could be used directly in the logic.. and so hacker's could poison internal logic in unexpected/unintuitive ways.
Whilst register_globals does not have to be a problem if you code in the right way, programmers naturally think of their code environment as a 'black box', which the internal-workings-there-of can only be effected by the explicitly read input. With register_globals this is not the case.
For example…

Code (php)

if (is_staff($member)) $staff=true;
if ($staff) delete_all_files();
This seems a simple, intuitive piece of code. But if staff=1 was in the URL, then delete_all_files() would be called. This is because the programmer in my example assumed that their variable context was black-boxed, but it was not.
You should NEVER assume this… most PHP programmers, including me, has fallen victim to this, and you should check your code very carefully. Make sure all caches are initialised to blank. Make sure you have PHP notices turned on so you know when variables are referenced when they don't exist (this would have spotted out example): NEVER use variables that only exist according to one logic branch, but are used in another. In other words, never refer to variables you don't know have been explicitly and definitively set to a real value.
To top this off… you should turn register_globals off on your server. It is NOT necessary and only sloppy or ignorant programmers rely on it. For apache, ocPortal disables it automatically using a .htaccess file.

Code uploading is prevented by file type filtering, inside the upload abstraction code. Basically… file types are white listed, and so no transparently server or user executable containing files may be uploaded (e.g. .php, .js, .html). Never allow a file to be saved that you haven't white listed to be of a valid type.
Also, never allow file paths to be given for editing or viewing in a URL without inspecting them for validity. For example, the download system doesn't allow you to specify the local URL of a file that is not publicly downloadable, and the comcode page editing doesn't allow you to edit anything other than a comcode page.

Code inclusion is usually performed as part of a register_globals hack. Be extra careful when including code from the path given in a variable, in case that variable is poisoned. Remember that the code included by a poisoned variable might not even be on your own server!

Jumping-in is a term I just coined for this guide to refer to when a .php file is accessed by URL that was only designed to be included. If a PHP file contains code outside functions or classes, then detect if the file was called manually by checking the access path, and die on that manual-access-event.

It is easy to forget that we encode actions in URLs… potentially destructive actions, that are called by the automated click of a link. What if that link didn't come from ocPortal and get shown in it's proper context, but was instead passed by a hacker to an unsuspecting victim, to click out of context.
The hacker in this situation has just performed a destructive action by proxy. Sometimes these hacks are elaborate in that XSS was used to make a user not even know they were clicking a link in the first place (and thus the hacker staying potentially anonymous).
We can't completely prevent this type of attack… a user might be tricked into filling in a form on someone's website that directs to a manipulative URL on their own. It can only be advised that users be careful what they click… i.e. they don't go to dubious web sites or click links given in dubious circumstances, or click links with apparent malicious or strange intent.
It might be suggested that the REFERER be checked to make sure the REFERER for an admin action is always inside the users site, but the REFERER field is often changed by privacy aware users to be non-representative, and thus cannot be relied upon.
One action ocPortal does take, is to refuse to allow a user to click-through to an Admin Zone URL… it requires an explicit login for every new browser window. It might seem that this is an unnecessary hassle, but it truly is the only standard/environment-compliant way to prevent automated Admin Zone-action.

Some nosy or malicious users may choose to try and poke around in upload, or page folders, by manually specifying URL's. They may attempt directory browsing, or to guess the URL to a file. ocPortal prevents directory browsing by placing index.html files in folders that shouldn't be browseable, thus nullifying the browse-ability with a blank output. .htaccess files are placed in folders where files should not be accessed by URL at all, but this is Apache-specific. The upload system supports obfuscation of upload names in order to make URL guessing unrealistic, and thus requiring a hacker to invent a probing scheme that works upon another exploit (a hypothetical exploit that hopefully does not exist!).

It goes without saying, but I'll say it anyway, that any code that finds its way into an eval call should only come directly from hard-coded ocPortal code itself. When loading up modules (for example), I made very sure that module names could not be encoded in a way to allow extra-command-insertion.

We have a policy that even admin-only sections should not have unintended exploitable behaviour. If a user is hosted in a restricted ocPortal-only environment, their software should not allow circumvention of said environment. Also, if a user exploits their way into somewhere they shouldn't be, it is best that that somewhere shouldn't allow them to exploit themselves into somewhere even worse… for example, if ocPortal Admin Zone gets hacked, a user should not be able to get full PHP code execution access on a hosting account.

One cause of vulnerability that deserves mentioning is the false assumption that same member-id means ownership. This is not true for the guest post case, which must always be handled specially.

In general, you can never assume code is secure. Always try to not make assumptions about sensitive (destructive, revealing, disturbing, …) actions, try to log malicious action, and have an action plan for recovering from hacks and upgrading when exploits become apparent. Validating input is very important… invalid input is the cause of most hacks.