fastCode provides a strong and fine-grained authentication and authorization model for securing enterprise applications. Additionally, in the commercial Team edition, fastCode provides mechanisms to encrypt application data stored in the database and sanitize the input from external clients to its ReST APIs.

Authentication

Authentication is the process of verifying who the user says he/she is. This verification may be done using a username and password supplied by the user, which are then compared with the username and password stored in the application’s database or an external provider such as an organization’s Active Directory.

In this current release, fastCode supports authentication using the application database or an external LDAP-compliant directory service such as Active Directory. In future releases, we will be supporting OAuth/OpenID Connect, and SAML.

When using an LDAP-based external directory service for authentication, you may choose to also store a user’s roles as LDAP Groups. Once you generate an application and run it for the first time, the following tables are created in the database.

  1. User – Contains just the user name field

  2. Role

  3. Permission

  4. User-Role Join Table

  5. User-Permission Join Table

  6. Role-Permission Join Table

The generated application also provides a LDAP Synchronization tool. You need to run this LDAP synchronization tool as soon you run the application so that the application’s authorization tables are populated from LDAP. Thereafter, we recommend running this tool as needed or on a periodic basis to synchronize any changes you make in LDAP to users, groups, or group membership with the application’s database.

Authorization

Once a user is authenticated, without authorization, the user has access to the full application and can perform any action. However, in the real-world, most applications use the concept of roles and permissions to restrict what actions the user can perform on the application. For example, if you have a customer table in your application’s database, one user may be able to create, update, and delete a customer, whereas another user may only be able to read the list of customers. Each of these create, update, delete, and read actions can be considered a separate permission.

A role is a group of permissions. The reason we have roles is for convenience - to group a set of permissions and apply a role to a user instead of assigning individual permissions to the user. Some applications also implement the concept of groups. A group is a set of users. You could assign a single permission or a role to a group, thereby assigning it to all the users in the group.

fastCode supports authorization using Role and Permission entities that can be assigned to a User. The relationships among these entities are as follows:

  1. User – Role: Many-to-Many. A User can have one or more Roles and a Role can be assigned to one or more Users.

  2. Role – Permission: Many-to-Many. A Role can have one or more Permissions, and a Permission can be assigned to one or more Roles.

  3. User – Permission: Many-to-Many. A User can directly be assigned one or more Permissions and a Permission can directly be assigned to one or more Users.

When two or more roles are assigned to the user, the assigned roles can have one or more common permissions. In such a case, only the unique permissions are assigned to the user.

A permission can also be assigned directly to a user without assigning a role to the user. When a permission is directly assigned to a user, the permission is added to the user, assuming that the permission is not already assigned to the user through a role.

A permission can also be assigned to the user using the revoke property set to true. In such a case, if the user is already assigned this permission either directly or through a role, this permission will be revoked from the user.

Jwt Tokens

Once a user is successfully authenticated, the application calculates the unique set of permissions that should be assigned to the user based on the role(s) assigned to the user and the permission(s) directly assigned to the user.

The application creates two Jwt tokens, one with the user’s authentication information (authentication token) and the other with the unique set of permissions assigned to the user (authorization token).

The authentication Jwt is stored in a httpOnly cookie with the secure flag turned on. On every request from the front-end to the back-end, this cookie is passed along in order to seamlessly authenticate the user. Storing authentication information in a httpOnly cookie prevents Cross-site Scripting (XSS) attacks.

The application front-end needs information about the user’s permissions in order to restrict what actions the user can perform from the UI, but if we store this information in the authentication Jwt toke, the Angular framework will not be able to read the Jwt token and the associated permissions because the token is stored in a secure httpOnly cookie. Therefore, we are storing the user’s permissions as Jwt claims in a separate Jwt token (authorization token) that is sent back from the server to the client and stored in the client’s local storage.

Spring Boot enforces CSRF on any methods except GET, HEAD, and OPTIONS. In order to prevent Cross-site Request Forgery (XSRF) attacks, the Spring Boot back-end automatically creates a XSRF token, stores the token in a cookie, and sends it with the OPTIONS pre-flight request The client reads this cookie and passes it back to the server as a header (X-XSRF-TOKEN), along with the cookie. The server validates that the X-XSRF-TOKEN value in the header matches what's passed back as the XSRF cookie value and allows the request only if they match.

Jwt token storage in database

The generated application stores one or more pairs of authentication & authorization tokens for each user in the database.

In our current implementation, every time a user logs into the systems with a user name and password and is successfully authenticated, the application creates a pair of Jwt tokens – one for storing authentication information and the other for storing authorization information. As previously mentioned, the authentication token is stored in a httpOnly cookie and the authorization token is stored in a regular cookie.

If the user logins in again, for example, from a different browser, another paid of Jwt tokens will be created for the user. Therefore, it’s possible that multiple pairs of Jwt tokens may be associated with a single user.

Token Invalidation

There are scenarios when the Jwt authentication and authorization token pairs assigned to the user need to be deactivated or replaced. Such scenarios include the following:

  1. User deactivated. In this scenario, for example, the application administrator deactivates the user’s account. Therefore, the user with already issued and valid Jwt authentication tokens should not be able to login into the system.

  2. User’s set of permissions changes. In this scenario, for example, the application administrator changes the role(s) and/or permission(s) directly assigned to the user, effectively changing the user’s set of permissions. Therefore, the Jwt authorization tokens of this are no longer valid and a new pair of Jwt tokens should be issued to the user.

  3. The permissions assigned to a role change. In this scenario, for example, the application administrator changes the set of permissions assigned to a role. This would mean that the set of permissions for each user who is assigned this role also changes, invalidating the Jwt authorization tokens of these users. These users need to be assigned new Jwt token pairs.

We need to note that if we invalidate a Jwt authentication token in case of user deactivation, the corresponding Jwt authorization token also needs to be invalidated because if the user is re-activated at a later date, his permissions could be different and a new Jwt authorization token need to be issued along with the new Jwt authentication token.

Similarly, if a user’s Jwt authorization token is invalidated due to permission changes, the corresponding Jwt authentication needs to be invalidated as well because the user needs to re-authenticate in order to obtain a new Jwt authorization token.

There are two ways we can manage the pairs of Jwt tokens assigned to each user.

In the first approach, which is our current approach, we store the token pairs associated with each user in a database. When the user first authenticates with the application, a pair of Jwt tokens is issued. On subsequent requests, this pair of tokens is passed to the application’s back-end from the front-end. The back-end ensures that the Jwt authentication token is well-formed, has the right signature, and hasn’t expired. It then checks whether or not this Jwt authentication token exists in the database. If the token is well-formed, has the right signature, hasn’t expired, and exists in the database, the user is successfully authenticated. For authorization, the back-end authorizes the user based on his permissions stored as claims in the Jwt authorization token.

This first approach of managing user Jwt tokens is reliable, but not highly scalable. It is reliable because as soon as a user has been de-activated or a user’s effective permission set changes, the application can immediately prevent the user from logging into the system. It is not highly scalable because every request made from the client to the back-end involves a database lookup for authenticating and authorizing the user’s request.

An alternative approach to managing the Jwt tokens is to issue short-lived Jwt authentication tokens. In case we want to deactivate the user, if we have short-lived authentication and authorization tokens such as 15 minutes, this is the max time that a deactivated user can continue to use the system. Similarly, if the user’s permission(s) are changed, they won’t take effect for a maximum of 15 minutes. Before this 15-minute period, in order to not force the user to login in case their token are still valid, we need to refresh these two tokens.

This approach allows us to not have to look up the database for every user request to check whether or not the Jwt tokens are valid - this increases app scalability and preserves the reason for using Jwt tokens in the first place as a stateless security mechanism.

In a future release, we will during the application generation, we will allow users to choose their application security strategy - want immediate user deactivation with less scalability or within (X) minutes de-activation with higher scalability.

Enforcing API-level Permissions

fastCode provides granular access control at ReST API method level. The ReST API Method for entities are secured through Spring @PreAuthorize annotation. The generated application has a standard set of ReST operations for creating a new entity, updating an existing entity, deleting an entity, finding an entity by its id, and performing a Boolean search for entities. Each of these operations is annotated with a separate permission.

For example, if we take an Actor entity, the following annotations are used on the operations exposed by the ReST API for the Actor entity.

@PreAuthorize("hasAnyAuthority('ACTORENTITY_CREATE')")

@PreAuthorize("hasAnyAuthority('ACTORENTITY_UPDATE')")

@PreAuthorize("hasAnyAuthority('ACTORENTITY_DELETE')")

@PreAuthorize("hasAnyAuthority('ACTORENTITY_READ')")

In general, the format for each annotation is @PreAuthorize("hasAnyAuthority('{entityName}ENTITY_[CREATE, UPDATE, DELETE,
READ]')").

Here, 'ACTORENTITY_CREATE', 'ACTORENTITY_UPDATE', 'ACTORENTITY_DELETE', 'ACTORENTITY_READ' are permissions. These permissions are then assigned to one or more Roles or are directly assigned to a User.

Therefore, for example, if a user tries to execute the create method of the Actor entity, and the user is not assigned the ACTORENTITY_CREATE permission, an error will be returned back to the client.

Input JSON Sanitization

In order to further strengthen security against input XSS attacks, the generated application also includes an input JSON sanitization module that is based on OWASP Json Sanitizer.

We intercept the incoming request to the ReST controller by using once per request filter and get the request Json. We then pass this Json to the sanitization method of OWASP Json Sanitizer and sanitize it. We then convert the sanitized json to a java object and pass this Java object to the filter chain.

Input

The sanitizer takes JSON like content, and interprets it as JS eval would. Specifically, it deals with these non-standard constructs.

  • '...' Single quoted strings are converted to JSON strings.

  • \xAB Hex escapes are converted to JSON unicode escapes.

  • \012 Octal escapes are converted to JSON unicode escapes.

  • 0xAB Hex integer literals are converted to JSON decimal numbers.

  • 012 Octal integer literals are converted to JSON decimal numbers.

  • +.5 Decimal numbers are coerced to JSON's stricter format.

  • [0,,2] Elisions in arrays are filled with null.

  • [1,2,3,] Trailing commas are removed.

  • {foo:"bar"} Unquoted property names are quoted.

  • //comments JS style line and block comments are removed.

  • (...) Grouping parentheses are removed.

The sanitizer fixes missing punctuation, end quotes, and mismatched or missing close brackets. If an input contains only white-space then the valid JSON string null is substituted.

Output

The output is well-formed JSON as defined by RFC 4627. The output satisfies three additional properties:

  1. The output will not contain the substring (case-insensitively) "</script" so can be embedded inside an HTML script element without further encoding.

  2. The output will not contain the substring "]]>" so can be embedded inside an XML CDATA section without further encoding.

  3. The output is a valid Javascript expression, so can be parsed by Javascript's eval builtin (after being wrapped in parentheses) or by JSON.parse. Specifically, the output will not contain any string literals with embedded JS newlines (U+2028 Paragraph separator or U+2029 Line separator).

  4. The output contains only valid Unicode scalar values (no isolated UTF-16 surrogates) that are allowed in XML unescaped.

Security

Since the output is well-formed JSON, passing it to eval will have no side-effects and no free variables, so is neither a code-injection vector, nor a vector for exfiltration of secrets.

This library only ensures that the JSON string → Javascript object phase has no side effects and resolves no free variables, and cannot control how other client-side code later interprets the resulting Javascript object. So, if client-side code takes a part of the parsed data that is controlled by an
attacker and passes it back through a powerful interpreter like eval or innerHTML then that client-side code might suffer unintended side-effects.

Efficiency

The sanitize method will return the input string without allocating a new buffer when the input is already valid JSON that satisfies the properties above. Thus, if used on input that is usually well formed, it has minimal memory overhead.

The sanitize method takes O(n) time where n is the length in UTF-16 code-units.

Implementation Details

We use five java classes to implement this functionality:

  1. CachedBodyHttpServletRequest.java

  2. CachedBodyServletInputStream.java

  3. JsonSanitizition.java

  4. ProductServiceInterceptor.java

  5. ProductServiceInterceptorAppConfig.java

1.CachedBodyHttpServletRequest.java:-

This class extends HttpServletRequestWrapper which is used to get the http request. We cannot directly get the request from the http servlet request because it serves the data only once and therefore we have to get the request using this wrapper.

HttpServletRequest is an interface for a HTTP specific servlet request. Typically you get instances of this interface in servlet filters or servlets.

Sometimes you may want to adjust the original request. With a HttpServletRequestWrapper you can wrap the original request and override methods to change it's behavior.

2.CachedBodyServletInputStream.java

This class gets the request from input stream and converts it to a string and passes the string to the Owasp json sanitizer. It then converts the output of the json sanitizer to a byte string and returns the input stream after sanitization

3. JsonSanitizition.java

This class performs the Owasp json sanitization. It accepts a string and returns a string

4. ProductServiceInterceptor.java

This class extends OncePerRequestFilter and implements HandlerInterceptor

Any post request will come here first. We then filter the request with the json sanitizer and forward it to the ReST controller.

OncePerRequestFilter
This is a Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container. It provides a doFilterInternalmethod with HttpServletRequest and HttpServletResponse arguments.

As of Servlet 3.0, a filter may be invoked as part of a REQUEST or ASYNC dispatches that occur in separate threads. A filter can be configured in web.xml whether it should be involved in async dispatches. However, in some cases servlet containers assume different default configuration

HandlerInterceptor
HandlerInterceptor is similar to a Servlet Filter, but it only allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful. For example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.

5. ProductServiceInterceptorAppConfig.java

This class extends the WebMvcConfigurerAdapter which has a method add interceptor which tells the mvc to add a interceptor which is created and passed by us

Flow of the code:-

ProductServiceInterceptorAppConfig>>ProductServiceInterceptor>>CachedBodyHttpServletRequest>>CachedBodyServletInputStream>>jsonSanitize