Font Size:
Ask Joget AI

App - Security & Best Practices

Set up secure authentication

Description:

The "default" directory manager used by Joget only has a basic username/password login mechanism using basic encryption algorithm.

At minimum, you must enable "Security Enhanced Directory Manager", as this will utilize stronger encryption, and enable user session and password policy configuration.

For best measure, set up "OpenID Connect" or "SAML" or "SP-SAML" directory manager plugins to allow end users to SSO into Joget.

Since most enterprises should already have active directory or idM in place, many identity management services should also already support these modern authentication protocols.

This also has the benefits of eliminating another set of credentials for end users to remember, the ability to easily enforce 2FA via the idM, and also centralizing user access control on the idM layer (e.g.: revoke access for offboarded employees).

Solution:

  • Explore the authentication protocols supported by your active directory or idP, and set up SSO mechanism in Joget.
    As an additional security measure, via the idM layer, enable 2FA.
  • If there are no authentication protocols supported by your infrastructure, at minimum, please use "Security Enhanced Directory Manager".

Create distinct accounts for all users

Description:

Shared or "dummy" accounts pose significant security risks. To name a few:

  • Zero Accountability: You cannot determine who performed specific actions (e.g.: unauthorized mass data deletions).
  • Increased Attack Vector: If one person's credentials are stolen, the attacker gains access to all permissions granted to this compromised account.
  • Broken Auditing/Compliance: Regulations often require tracking user behavior; shared accounts fail these audits.
  • Offboarding Headaches: When a user leaves, you cannot revoke their specific access without changing the password for everyone, often leading to shared passwords rarely being updated.
  • Increased Vulnerability: More users knowing a password increases the chance of phishing or credential leakage.

Solution

  • Every Joget user, including administrators, app creators, and system managers must have a unique, named account.
    Generic accounts like "developer" or "joget" are not permitted in any environment.
  • All developer accounts must be tied to a real individual's identity (name + email). Account creation and deactivation can be easily controlled by your idM if you have set up SSO via directory manager in Joget.
  • Shared logins are prohibited.
    If a shared account is discovered in any environment, it must be disabled immediately and replaced with individual named accounts.

Application Decomposition

Description:

In larger implementations, it's possible for UI to contain several tens or hundred plus menus, where there would be "sets" of major functions.

For example, a finance department could have several sets of functions consisting of audit management, financial reporting, risk and compliance, tax management, etc. 

And within each major functional area, there could be even more processes and forms and lists involved.

If one app is bloated with all major functions combined, app maintenance will be increasingly challenging, as it will be difficult to inspect and track changes.

Not only that, complex changes to a single app may inevitably have configuration errors, which may unintendedly break other functions.

In severe cases of extremely large apps, every operation related to app management such as app import/export, using App Composer features, app builders load/save, etc., performance will be significantly degraded.

Solution:

  • For large implementations, modularize major functions to separate apps.
    Using the example above, Audit Management and Tax Management must be separated into its own individual apps.
    • Improved performance, as Joget does not need to parse huge payloads of the app definition.
    • Improved app maintainability and change management.
    • Streamlined iterative development and UAT testing.
      By making changes only on one app, testing can be confined to that one specific app, or within the subset of functions if integrations are involved.
    • Reduced potential cross-function breakages from configuration errors.
      In cases where there is a configuration error found, debugging is easier since the bug is confined only to a specific app without possibly affecting other major functions.

Set up Permission Control

Description:

It is mandatory to set up permission control for all Joget apps.

We should configure permission rules on the UI layer first, and only if granularity is required, then only consider applying permission rules on Forms, Lists, Processes.

If there are many menus that must be publicly exposed without any permission, it is highly recommended to create a separate UI that will only contain publicly-viewable pages.

Doing this will reduce risk of accidental permission configuration errors that may expose sensitive functions publicly.

Solution

  • Apply permission rules on the UI first.
    Only if the UI is unable to granularly control permission to your requirement, only then apply permission rules on individual app components.
  • Remove redundant permission rules.
    If the "outer layer(s)" already have adequate permissions, it is not required to add permission on the "lower layers" of app components.
  • Place all public menus in a separate UI(s). This makes it clear for app developers that any menus created in this UI will be publicly available.

 

Put administrative menus in a separate, secured UI

Description:

Enterprise apps commonly have CRUDs for master data, or specially built functions that are capable of manipulating sensitive data and process flows, perform task delegation, create scheduled jobs, or etc.

Any menus that do not belong to your app(s) general audience should be split into its own separate UI(s).

With the example above, these functions must never be displayed to the general audience and may only be suited for "Managers" or "Admins" or equivalent roles, and hence should have its separate UI that has permission only for the intended high-privilege roles.

By doing this, there is reduced risk of accidental permission configuration errors that may expose sensitive functions to unintended end users.

Solution:

  • Place all administrative menus in a separate UI(s), and configure permission to secure this entire UI only for the intended high-privilege roles.

Permission Control - "Hidden" is NOT equal to "Deny Access"

Description:

A common misconception of a "Hidden" UI or menu is that unauthorized users will not be able to access it.

While technically true that unauthorized end users will not see any button to be able to click on and navigate to hidden UIs or menus, however, "hidden" UI/menus are still accessible via the browser URL as long as UI ID or menu ID is known.

Insufficiently protected hidden objects pose a significant security risk if a technically-savvy user is able to reverse-engineer or infer such information from the overall app. Similarly, web crawlers or web scrappers are highly capable of utilizing advanced techniques to uncover hidden pages.

A good example of failure to sufficiently secure privileged UI/menus, if Google's web crawler discovers those hidden pages, it will leak those pages publicly in global search results.

Solution:

  • Unless absolutely necessary to keep it as "Hidden", any unauthorized UIs and menus should instead set its permission to "Deny Access".
    You can utilize Permission Control on the UI, and if granularity is needed, also configure permissions on the Form/List/Process layer.

Form - Write Permission - Correctly use "Readonly" and "Disabled" settings

Description:

A new feature in Joget DX 9.1 onwards, Form Permission feature has been upgraded to better control how form field values can be written to the database.

Form permissions now has 4 options:

  • Visible
  • Readonly
  • Disabled
  • Hidden

"Readonly" blocks user input, but is still editable via JavaScript and will save its value to the database.

"Disabled" will block any modifications and will not save its value to the database at all.

"Hidden" will simply omit the field from being rendered in the form, similar to "Deny Access" in UI permission.

It is important to understand these differences in order to properly secure your form fields from unexpected or unauthorized modifications.

Solution

  • For form fields that do not require user input, but must still be editable via JavaScript and value saved to the database, set its write permission to "Readonly".
  • For form fields that must not be editable under any circumstances and whose value must not be saved to the database, set its write permission to "Disabled".

Beware of Hash Variable usages in custom scripts

Description:

Since hash variables can be used almost anywhere in any plugin configurations, this includes the ability to also use them to dynamically append values in your custom scripts.

However, such usages must ensure that hash variables that involve user-input values are escaped properly.

Examples of hash variables that involve user input values are:

  • #form.table.fieldId#
  • #requestParam.id#
  • #variable.id#
  • etc...

Although Joget's built-in plugins are already protected, this protection is not included for any custom code created by app developers.

An example of insecure usage of hash variables, let's say you are crafting a custom SQL query like below:

SELECT * FROM my_secret_table WHERE number = #requestParam.number#;

The request parameter hash variable retrieves information from the browser's request URL, which is easily manipulatable by end users. With this fact in mind, a malicious actor can craft the URL parameters like:

.../myPage?number=5; DROP TABLE my_secret_table

Which will result in a malicious SQL query like:

SELECT * FROM my_secret_table WHERE number = 5; DROP TABLE my_secret_table;

With the above example, the safe way to use hash variables would look like:

SELECT * FROM my_secret_table WHERE status = "#requestParam.status?sql#";

Failure to use hash variables in a secure manner may reveal vulnerabilities for attackers to exploit cross-site scripting (XSS) or Remote Code Execution (RCE) attacks, where malicious actors can "inject" Java or JavaScript code to execute attacks on end users or the Joget server.

Solution

Always test apps in UAT before deploying to PROD

Description:

Under any circumstance, Joget apps developed must be tested extensively for full functionality prior to deployment.

The UAT environment should ideally mirror the PROD environment as closely as possible, and contain the required sets of replicated sanitized data from production, in order to test as close to real-world conditions.

It is also highly recommended to perform regular performance testing to approximate/extrapolate the expected throughputs of your apps against the PROD infrastructure. This way, potential performance bottlenecks can be caught before these issues reach production.

Solution:

  • All apps targeted for PROD must undergo QA & tested extensively in UAT for full functionality, followed by formal UAT sign-off before deployment to PROD can be initiated.
  • Prior to PROD deployment, ensure that all necessary backups have been performed.
    In the case of regression or unexpected behaviour, these backups are vital to allow prompt rollback.
  • Regularly benchmark the performance of your apps in UAT.
    If the situation does not allow time for performance benchmarking, then closely monitor your observability tools (e.g.: APM, or built-in Glowroot in Joget) for performance or stability issues.
  • Under any circumstance, do not import any unfinished apps into PROD.
    Even if this unfinished app is not published, it should never be imported into PROD unless the app has hit a determined milestone or MVP, has undergone QA / UAT-sign-off, and is ready for real-world use.

Compress image files prior to usage in UI

Description:

Joget app developers use App Resource to store various files, which can then be referenced in the app design using its respective resource URL or App Resource Hash Variable.

The primary use case for App Resource is storing image files for elements such as the app favicon, thumbnails, HTML backgrounds, and banners, etc.

A critical best practice is image compression. Images sourced directly from devices or editing software are often saved as high-fidelity uncompressed files, and can be several megabytes (MB) in size.

Serving these large uncompressed images in the UI is strongly discouraged as it significantly degrades page load performance.

  • Slow Page Load: Uncompressed images can be up to 50x larger than optimized versions, forcing users to download unnecessary megabytes before a page is usable.
  • Wasted Bandwidth: High data consumption is especially costly for users on limited internet plans.
  • CPU Overhead: Large image sizes require more browser processing power to decode and scale, which can lead to laggy scrolling and high battery drain on portable devices.

Solution:

  • Proper Resizing: Don't just scale a large 3000px image down to 300px using CSS.
    Resize the actual file to match the display size before using it in your app.
  • Lazy Loading: Where relevant only, use loading="lazy" attribute on the <img> tags to ensure images only download as the user scrolls them into view.
    However, do not apply this rule carelessly as this method has its own drawbacks.
    This rule is typically only applicable for use in off-screen content, and must always specify image width and height to prevent layout shifts upon image load.
  • Modern Formats: Consider using WebP or AVIF instead of standard JPEG or PNG. Some modern formats can be smaller in file size while still maintaining similar quality.
  • Compression Tools: Use compression tools to strip image EXIF metadata and appropriately compress pixels without visible quality loss.
Created by Debanraj Ravindran Last modified by Debanraj Ravindran on Apr 24, 2026