Securing web applications can be tricky. One surefire way securing the system is to lock it in a box, where no-one can switch it on, much less enter data into it. Unfortunately, this is not the making of a useful CRM system. In Sage CRM 2017 R2, we’ve added a new feature that should allow partners and others customising CRM to do this a little bit more easily.
Let’s consider an example where we might be updating a Person associated with a Case, after saving the Case record on a screen hosted within a custom ASP page. There may be code in our ASP page that looks a little bit like this:
personId = Request.Form('Case_PrimaryPersonId');
CRM.ExecSql('UPDATE Person SET Pers_UpdatedDate = CURRENT_TIMESTAMP WHERE Pers_PersonId = ' + personId);
Of course, this would never be seen in production, since the Record object is far more useful. We discourage the use of the Query object, and recommend use of the Record object as it's easier to use, obeys the Sage CRM security model and does much of the hard work of auditing records for you.
This code sample is vulnerable to a number of different attacks, since there’s no checking of the value that’s been submitted by the client, and is being passed to the database. It’s entirely possible to submit an additional SQL query via the parameter value, using SQL injection.
Sage CRM 2017 R2 contains two new COM API methods that can help to mitigate against these kinds of attacks. They won’t cover all instances, since the best place to filter bad data is immediately after it has been submitted – the developer working on a screen or customisation will know best what sort of content they are expecting, and can filter it accordingly. However, these new methods can help to make the task of cleansing user input a little easier.
These methods can be used in lieu of Request.QueryString and Request.Form in COM ASP pages. Their usage has been intentionally kept similar to the methods they’re replacing. Their usage is as follows:
CRM.QueryString(sParameterName, sOptions) returns String;
CRM.Form(sVariableName, sOptions) returns String;
The sOptions value is optional, and can be used for several tasks. The switches available include the following:
- IntegersOnly - Returns only the integer characters from the parameter value.
- WordOnly - Returns only the word characters from the parameter value (same as \w in a regular expression: A-Z, a-z, 0-9 and the underscore are allowed).
- SQLInjection - Applies a SQL injection filter. If this is triggered, a blank value will be returned.
- XSS - Applies an XSS (cross-site scripting) filter to a string. Only content that passes the filter will be returned.
Assume that the parameter you're taking from a client should only contain word characters. In this case, the variable can be cleansed by doing the following:
myVar = CRM.Form('myParm', 'WordOnly');
More than one option can be passed at a time, by comma-separating them, e.g:
myVar = CRM.Form('myParm', "SQLInjection,XSS");
The IntegersOnly or WordOnly filters are the most restrictive, and are less likely than the other two options to trigger false positives.
If no sOptions value is passed, then the SQL injection and XSS filters will be used by default. Since the XSS and SQL injection filters make use of blacklists, they are not guaranteed to be 100% effective, and may be triggered by false positives. A more certain method of blocking malicious content is via whitelisting at point of usage, but since the methods do not know exactly what kind of content to expect, blacklisting is used.
These new methods will not be suitable in all use cases; it is suggested that they can be used as an additional level of security for use in Sage CRM customisations.
Additional or separate methods of input validation may be required in other circumstances.