Security checks every developer should follow — OutSystems Reactive

Samuel Marques
valantic LCS
Published in
7 min readOct 20, 2022

Introduction

Software security is a critical topic that should receive more attention than ever before. Companies of all shapes and sizes increasingly understand the need to continually improve competitive differentiation and avoid falling behind the digital. Having data on the cloud is great! Everyone can access it no matter where they are.
Although the use of the cloud implies many advantages, as is often the case with new technologies, security tends to be sidelined.

In this article, I will guide you through basic security checks that every developer should follow when developing applications with the OutSystems platform.

Security checks

Before we start, please remember that in OutSystems Reactive, you always need to validate input data on the server-side. Do not rely only on client side validations.

1. Validate URL parameters:

On a typical master-detail screen, it is very common to send the identifier of the record we want to create or update through URL parameters/arguments. Nonetheless, these same parameters are extremely easy to change, and without proper validations, users might be able to access data they shouldn’t.

Sample URL

Make sure your Aggregates or SQL Queries only return data taking the authenticated user into account. For that reason, take advantage of the built-in function such as GetUserId() and Check<Role>Role.

Use of the GetUserId() function inside an aggregate

For an extra layer of security, you can make attacks more difficult if you use random text identifiers(GUID/UUID) instead of sequential numbers as primary keys or auxiliary primary keys. This will make it harder to guess other record identifiers.

2. Validate request payload input parameters:

As you might know, for each and every aggregate and server action on the client-side, there is a REST POST method exposed that handles its request. You can easily check that through your browser developer tool network tab.

Header information from a server request

The name of your Aggregate/Data Action or Server Action in service studio will match with the name of the REST POST method in your developer tool console. As usual, with a POST request, you will be able to see the request headers, the request payload, the response and much more useful information.

How can I see my request’s payload data?:
Open your browser developer tools(F12) > Network Tab > Select a request > Payload Tab

How can I edit my requests payload data?
There are a few ways to do this. Let’s focus on two of them.

1. Firefox: Open developer tools(F12) > Network Tab > Select a request > Resend > Edit your request parameters and hit Send

Editing a request data through the Firefox developers tool

2. PostMan:

  • Open developer tools in your browser(F12) > Network Tab > Select a request > Copy value > Copy as cURL
  • Open PostMan > Import > Raw text > Paste
  • Edit your request parameters and hit Send
Editing a request data through the PostMan application

With the steps shown above, you are now able to see and edit any request headers and payload. This is a very good way to extend your security tests for server actions, aggregates and data actions by modifying any request parameter.

You might be sending more input parameters than you actually need, and that can lead to unauthorized changes to your data. Please take a look at the following article.

3. Double check screen User Roles:

User Roles will protect all aggregates, data actions and server actions within a screen. However, if you set a screen with the Anonymous role, everyone, including non authenticated users, will have access to the screen aggregates and server actions.
Fork screens with different functionality per role. It’s more secure and easier to maintain.

4. Sanitize untrusted input data:

Never trust user inputs! Sanitizing consists of removing any unsafe characters from user inputs. This is one of the best approaches to verifying, cleaning, and filtering data input given by users. It will help you avoid attacks related to it.
OutSystems already has built-in protection, although sometimes developers need to extend their applications in a way that may require extended security measures. For instance, you may want to render some HTML provided by the end-user and, you don’t know if it’s safe or not. For that, use the SanitizeHTML from the Sanitization API.
Not only for HTML, but you need to do the same for JavaScript, URLs, SQL and any other input data from the end-user.

Check here for more information on this subject.

5. Avoid having secret backdoors:

Whether you have a genuine reason or not, don’t add backdoors to access the applications you developed. Sometimes during development stage, screens or hidden actions are created to facilitate testing or even authentication. It may happen that these screens or actions are forgotten and end up being sent to the production environment and sooner or later someone else may find it.
Exploiting backdoors is a common tactic used by cyber attackers.

6. Secure your advanced SQL code customizations:

It is very common when developing applications in OutSystems to have to use an input variable with the “Expand Inline” property set to “Yes” on an Advanced Query, whether to inject a list of values in a “WHERE <column> IN (@valuelist)” or any other more complex scenario. In these situations, please take advantage of the built-in functions BuildSafe_InClauseIntegerList, BuildSafe_InClauseTextList and EncodeSQL. Each has its own functionality and I fully recommend that you take a look at documentation.

Note: EncodeSQL escapes the single quotes, which guarantees that an end-user cannot close the starting single quote and then add more SQL afterwards. But for this to work, you must enclose the @param in single quotes as shown in the image below.

EncodeSQL function in action

Check here for more information on this subject.

7. Handle Exceptions & Errors Properly:

Improper error handling can provide attackers with valuable information such as server IP addresses, database names, and stack traces that could contain information about the libraries and code that produced the error. One of the most common scenarios I see in OutSystems is when there is a database exception where the physical name of the entity is returned. To avoid this scenario, you can add an exception handler to your action to catch the database exception, provide a user friendly feedback message and log the original error.

The left hand side image shows an uncaught database error message, while the image at the right shows how to catch a database error.

8. Protection against brute force attacks:

Protection against brute force attacks is already implemented by the platform for all applications configured with Users as their User Provider. This only applies for the users authentication. In case you have for instance developed a public API or even a public page to validate registrations with a URL Token, you will need to implement this yourself.

There is a great example of the use of this implementation created by Rui Barbosa that you can adapt for your needs.

9. Focus on best practices and code review:

Best practises and code review should always go together. Code review is an essential stage in a software development life cycle and I cannot see any downsides to doing it. It is always better to find problems in the code during its development phase than at the end of the project, when it has already been delivered to the customer.

Here are a few reasons why code review is especially important:

  • Detection of errors and vulnerabilities;
  • Ensures project quality;
  • Improves code performance by sharing ideas between team members;
  • Minimizes mistakes and their impact;
  • Creates consistency with coding standards and project requirements

10. Tips:

  • Protect your reads. Take advantage of built-in functions like GetUserId()¹ and Check<Role>Role¹, you can use them directly on your aggregates. Don’t expose sensitive data on the client-side. Retrieve only the necessary data for the screen.
  • Protect your rights in the database. Please validate that the authenticated user can actually write into the database. Once again, take advantage of built-in functions like GetUserId()¹ and Check<Role>Role¹.
  • Do not rely on data from local variables. Double validate your data on the server-side.
  • Consider replacing aggregates with many joins by advanced queries, where only the data/fields you need are fetched from the database. Although OutSystems optimizes aggregates for not retuning data you don’t need, the entire entity structure is returned, exposing your data model and increasing your request payload.
  • Take advantage of the built-in functions from the Sanitization API.
  • Stay up-to-date on the platform changes and any external libraries, so as to not use obsolete code that may contain vulnerabilities.

Conclusions

Developing insecure code can damage the reputation of your organization forever by putting data and systems at risk, and you don’t want to be the responsible for that.
Read the platform documentation carefully, seek constructive criticism, stay up to date on security trends and do not hesitate to ask your experienced colleagues for help when needed.
You can also create a new post on the OutSystems community forum asking for help. But before doing so, check if there is not already a thread talking about your question.

Happy coding!

¹ In a server-side context, if you are using the value of the GetUserId() and Check<Role>Role functions, make sure they don’t come from the client-side — e.g., a local or client variable. The main reason for this is that they can be manipulated using tools that alter client-side code in the user’s browser.

Samuel Marques — Tech Lead at Do iT Lean

--

--