Building upon this great article regarding security, here are some more tips to help protect your OutSystems reactive and mobile apps.
One of the most security sensitive parts of an application are write operations. The best way to protect these is to centralize security as much as possible. As such, putting security logic in CRUD (Create, Read, Update, Delete) wrappers assures that all database writes are protected. This is also an excellent place to do auditing, recording who did what when.
If you need to use these actions in timers and asynchronous processes (which run anonymously by default) you may need to do a login (either impersonating a user, when it makes sense, or using a service account) at the start of the process.
Reading sensitive information is also a big no-no. To protect that, it’s important to make sure that only the data the user can access comes to the client-side. A good idea is to use GetUserId() inside aggregates’ filters.
An example is a ShowOrder screen, with a GetOrderById aggregate pictured above. It is filtered by the OrderId screen parameter. But if only some users should see some orders, you need to put another filter that uses GetUserId(). Inside an aggregate this is calculated on the server side and can not be tampered.
Another option would be to create a server action marked as a function, e.g. called CanUserSeeOrder(OrderId), that checks access. This can also be used to audit read operations, which is important in some compliance scenarios like healthcare.
Putting sensitive data in different database entities can also help to give more control of where it is being read. In the example above, that join condition assures only HR Managers can see the employee benefits. Check*Role() functions can also be used safely inside aggregates and help in these cases.
Build a Menu with Different Items per Role
The menu is an example of a UI that can show different things depending on the roles. You may want to show only some menu items to some users. Because there are no client actions to check roles, you need to fetch that info from the server, with a data action.
When you set that screen to only be accessed by Managers, all calls to the server (including screen aggregates) made from this particular screen will automatically be checked for that role before running.
That’s also why it’s not a very good idea to have screens (other than the login) with anonymous access, as all their aggregates and screen actions won’t be protected.
Fork Screens with Different Functionality per Role
When you have one screen with very different functionality depending on the role (e.g. only managers can see and click an Approve button), sometimes it can be better to create two versions of that screen. Otherwise you would need to make sure the Approve server action is only called by Managers, by putting a CheckManagerRole() in the beginning of that action. This is in fact a good practice anyway, but it can often be overseen.
And it may work fine for a single action, but it starts to get messy, especially when you have several actions and data that are role dependent, because you’ll need to put security checks in several places. Creating two similar screens might be more maintenance effort, but it can make security easier.
Consider Using GUIDs as Primary Keys
For an extra layer of security, you can make attacks more difficult if you use random text identifiers instead of sequential numbers. This will make it harder to guess other record identifiers, because all data that comes to the client side can be read and modified.
There are several shortcomings for this. These identifiers will be a pain to work with, impossible to remember and hard to verbalize. They will take more space in the database (these are stored as a text attribute). It’s not possible to sort them.
And, most importantly, as with any obfuscation technique, this will not be enough and you also need to use the guideline explained previously regarding protecting reads and writes.
So really think all this through if you wish to follow this approach.
Turn on Optimizer
OutSystems is launching a new technical preview feature that will optimize all calls to the server in version 11.10. This will minimize the amount of information sent to the client, which will improve performance and add extra protection. Be sure to turn this on by going to LifeTime>Environments>Technical Preview .
This will be enabled by default in the near future (it has already been on for some time in personal environments), but you can start taking advantage of it right now. There are no real downsides to it, we‘re just launching it progressively to be extra sure that existing applications continue to run smoothly.
Make Security Your Ethical Imperative
It is our collective responsibility, as the builders of a new digital world, to make sure people remain safe.
It may be tempting to leave it only to experts, but we can’t do that. Security is often dependent on the business problem you are trying to address (i.e. who can read or modify which data) so, to solve it, you need to dig deep into the security implications.
And act upon them. So you can contribute in creating a better and safer world.