Nobody Told Me Securing APIs Was My Problem in OutSystems

Wait 5 sec.

I have been developing enterprise applications with OutSystems for several years already. Long enough to be critical. Long enough to be sure that some mistakes will not be repeated again. And long enough to know that the most dangerous belief you can take into a project is that OutSystems takes care of your application's security. It doesn't. At least not always. And when it comes to APIs, this issue can become especially acute.\Let me share with you the experience I had to deal with while reviewing an application. This was a large enterprise-level organization with its own IT security department, compliance checks, and everything else that follows the name. The application was already running in production for a while, and it seemed fine at first glance. Then I decided to look into the exposed REST APIs and found a particular API open for everybody to use. Without any authentication or token verification. An available URL that any user on the Internet could call and get access to the customers' personal information. It wasn't done intentionally. \Just a developer forgot to implement the OnAuthentication callback in the testing phase, marked it as something to return to later, and got distracted in the process of preparing a release. I came across such cases more than once in my career with OutSystems enterprise projects. The really scary thing here is not the fact itself but the way it can go unnoticed.The Platform Does a Lot. Just Not ThisOutSystems lives up to its reputation. It deals with HTTPS out of the box on the cloud, it keeps you safe from SQL injection via the ORM, and it gives you role-based access control on screens and server actions. If you are a developer coming from more traditional stacks, this is a very comforting experience, and it is deservedly so. However, exposed REST APIs do not play by the same rules. \When you create one, you are creating an interface to the outside world. OutSystems will provide you with the interface itself, will make sure it is properly installed, and the hinges are nice and strong. But it won't secure it automatically. You will have to do that yourself.\What is the most important distinction when it comes to security? Exposed REST API and consumed REST API. The former refers to cases where your OutSystems application is the server, whereas the other system calls your API. The latter is where you are the client and are calling another API. Both require security measures, but the approaches are different. And most of the trouble comes from the exposed side.Authentication Is Not Optional, It Is the Starting PointThere are a number of options in OutSystems, and I have used all of them on various projects. API keys are probably the easiest. The consuming application includes some kind of random string in a header; you compare it to what you have in your database, and you accept or reject the request. \Easy to implement via OnAuthentication, easy to monitor and audit, easy to understand. When you have a server-to-server integration, it makes sense to use an API key since you can keep it completely secret. You hash the keys, and you generate a new key for each consuming application. You might think this is unnecessary until you have to revoke access to one application and discover that you don't want to revoke access to others at the same time.Choosing the Right Authentication StrategyBasic AuthenticationBasic Authentication is part of the core of OutSystems, and I know why people tend to reach for it. They can just use the basic auth component, and it will work. A username and password are Base64 encoded and sent with each request. The problem is, Base64 is an encoding algorithm, not encryption. Whoever manages to intercept that traffic can read that password without much trouble. But HTTPS takes care of that. And on OutSystems Cloud, HTTPS is enforced. But in self-hosted instances, it might be assumed and not always confirmed. In any case, Basic Auth is good enough for non-sensitive internal applications. Anything involving sensitive enterprise data is another matter.\JWTJWT is usually where I end up for most of my enterprise projects. Client authenticates once, obtains a token, signs it, and then includes it with every subsequent request. Your API verifies the signature, so there is no need for a database lookup on every request. OutSystems has no native JWT implementation, but there are several solid implementations on Forge. My advice to every developer working with JWT: verify the signature every single time. Do not assume anything from the token payload until you verify the signature. That is the whole point of having a signature.\OAuth2.0OAuth 2.0 becomes relevant when you need to integrate your application with an external platform. Think Microsoft, Salesforce, Google. OAuth is the appropriate choice in situations where your application needs to access data on behalf of it. When you utilize OAuth within OutSystems, it is your responsibility to manage the token exchange, securely handle the storage of your access tokens, and refresh them before they expire. OutSystems provides components for common providers. \However, it's still recommended that you learn how the basic flow works because there will be times (like every time something goes down unexpectedly) when you will have to troubleshoot or fix an issue rather than just restarting the component and hoping for the best.Choosing the Right Authentication StrategyIt's an important distinction that I need to be clear about because I've seen it go unnoticed on projects that seemed well-constructed. Otherwise, authentication will tell you who is calling your function. Authorization will tell you what they are permitted to do. These are two different things that are not solved by one another.\In OutSystems, the problem lies in the fact that it is so easy to restrict access at the UI level. If a user can't see the button, he or she can't trigger the action. This is easily defeated as soon as someone starts calling your API endpoints. It doesn't take more than thirty seconds in Postman with a valid token. At this point, your UI is no longer relevant; only your API endpoint is. Each exposed method needs to have its own authorisation mechanism. It's not enough for it to be a valid token. Your system must confirm whether or not the caller is authorised to perform this action on this particular record. \Practically, this translates to checking for permissions and ensuring the caller's ownership of the record in question. I put all that in a reusable server action that I always use.The Mistakes I see, Every TimeThe first one is pretty straightforward – and I lead with it for a reason. Always test your API without any credentials and ensure you receive a 401 response. Not a 200. No data. A 401. Because the OnAuthentication callback is trivial to set up wrongly and OutSystems will not give you any warning. Input validation is another one that always seems to come as a surprise. The ORM is SQL-injection safe – that is a platform advantage. But it does not handle your business rules. For example, if your API requires an account ID of the user making the request, make sure this condition is met before proceeding with further processing. If your API expects an input to fall within a specific range, validate this range. The unvalidated input at the API level is one of the most common sources of issues in enterprise systems.\Logging is another thing I am particularly concerned with. OutSystems logs are genuinely useful, and I rely on them heavily. However, they are also available to a broader audience than many developers realize when writing their debug statements. Never store your API keys, tokens, personal information – anything that is sensitive – in your logs. I have encountered credentials in application logs on otherwise quite secure enterprise systems. \It is an easily made mistake and potentially a very costly one. Another thing that I see too often in enterprise systems is sharing credentials between integrations. Using one set of credentials for all integrations is equivalent to compromising all integrations if one becomes exposed. On enterprise systems, there is simply no excuse for it.\Finally, remember to return generic error messages. Any information that could be used to compromise your system – such as a stack trace in a 500 response, names of tables or paths in your application – is essentially free knowledge to those trying to find vulnerabilities in your system. Log the details internally and send nothing outwards.What I Check Before Anything Goes LiveAlthough I am not an individual who would normally rely on checklists, in this case, I have become one since the cost of oversight is much greater than the price of being thorough. Prior to making any exposed API live, I would like to make sure that an unauthenticated request results in a 401 response, which I do by testing instead of assuming. Authorization checks should be made for all methods that involve data access. Inputs need to be verified prior to reaching any business logic. \Sensitive information must not be found in the logs. Each consuming application must use unique credentials. Tokens must have expiration dates established. HTTPS needs to be in place. And lastly, error messages cannot give any clues to the inner workings of the system.\This is actually faster than you might imagine once it becomes a habit. Much faster than the alternative of having to explain to your client that his/her customer data was available.ConclusionOutSystems allows you to create quickly. This is where it adds value, and the velocity of business operations in the enterprise environment can be very valuable. However, there is an alternate definition of creating quickly, which is simply deferring costs by taking shortcuts in API security and hoping that no one will notice. These concepts are not difficult; authentication, authorization, input validation, and credential management are all fundamental elements. \All OutSystems developers who are creating APIs should understand these principles as their default method for building, and not as a checklist. If you currently have APIs in production, take the time to test your OnAuthentication callbacks. Test them without credentials. Examine the error response that your API returns. I found someone else's mistake in that enterprise review and fixed it before it became a headline. You would rather find your own.