Security Best Practices

While JourneyApps goes to great lengths to secure all your apps automatically, the freedom and flexibility we aim to provide developers does in certain cases expose some pitfalls and limitations that developers should be aware of.

Out of the box, JourneyApps provides the following security mechanisms:

  1. All data is encrypted at rest and in transit using the strongest available encryption and transport protocols
  2. Client-side source code is obfuscated by default, and optionally encrypted
  3. All exposed API endpoints are encrypted using HTTPS
  4. Each customer organization is hosted in a highly secure, logically isolated sandbox
  5. Regular penetration testing of all cloud infrastructure and client runtimes are conducted
  6. Our annual SOC2 Type 2 Audit report is available to customers on request

Your app is therefore secure by default, however it’s worth taking the following into consideration for each app that you build.

1. Runtime / Container

App Code

All view JavaScript or TypeScript gets served to the client. On iOS it’s very hard for an app end-user to inspect this code. However using the web runtime, it’s easy to open the chrome developer tools and inspect the app’s source code. It is therefore recommended to avoid ever including and sensitive code such as API or encryption secrets in your app code - instead store these in CloudCode and use CloudCode.callTask, as end-users can never access the source code for a Cloudcode task.

App Data

As a reminder: there are three data “namespaces” available to developers:

  • DB - data stored on the device and synced with the backend database
  • LocalDB - data stored on the device but never synced anywhere
  • OnlineDB - data stored in the backend database

The data in DB and LocalDB are encrypted by default (over and above the default OS-level file system encryption provided by modern operating systems). On iOS the DB encryption key is stored in the keychain. The data is similarly encrypted on Android and Windows. The only exception is the Web platform, where encryption of data is not feasible. Depending on your security model, the OS-level full disk encryption might be sufficient for data stored on disk by the web browser. If not, using OnlineDB instead of DB will avoid any data being synchronized to the client.

Furthermore, for web, it’s always possible for end users to open the developer console, and access the OnlineDB object from the console. For this reason, it’s recommended to implement Data rules for apps being accessed using the Web container, especially when third parties such as customers or contractors will be accessing the app via web. Note that using OnlineDB from the browser developer console will not bypass the JourneyApps audit trail system, so all changes made by a user in this way will be audited.

2. Backend (Cloud) Database

All data stored in the JourneyApps backend are encrypted at rest, so there is nothing to be taken into account on that front. Having said that, the following are recommended best practice when using the JourneyApps backend:

  1. When creating Backend Users, always remember to apply the principle of least privilege, and assign the user the most restrictive role available (i.e. Data Manager vs Admin)
  2. Remember to use the text:password field data type for any sensitive fields stored in the DB
  3. If using the Backend API, it’s recommended to use single-purpose named tokens, and to disable the username and password authentication. Administrators can create two named tokens for each use case for key rotation.

Securing Webhooks (Advanced)

When using external webhooks, all outbound (POST) requests will be encrypted using TLS. If you want to authenticate webhook requests, you can use the X-Journey-Signature field in the request header. This field is calculated using HMAC-SHA256, where the data is the raw request body, and the key is the “Secret” field on the “Logs” page for your webhook in the JourneyApps Backend Databrowser. Using OpenSSL, this would be calculated as OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(‘sha256’), key, data)

Alternatively, you can also use certificate-based authentication. The X-Journey-Signature-RSA-SHA256-Key-ID header is the CN of the certificate, and the certificate itself can be obtained from your customer success representative upon request.

3. CloudCode

Since CloudCode tasks are ephemeral (i.e. they spin up, execute, and shut down), there is no attack surface. For most tasks, standard security practices apply (avoid making plaintext http API calls, don’t overwrite the default fetch SSL configuration, etc.). The exception is for Web Tasks, where special precautions are required:

  1. Make sure you implement authentication using a strong key - don’t simply return access.authorized
  2. If you include any npm packages into your webtask, make sure you monitor security announcements for these packages, and upgrade them when required
  3. If you accept and use any dynamic parameters to your task, make sure to sanitize these parameters, especially if combining with the nodeJS fs module, since unsanitized parameters could enable directory traversal attacks.

4. User Authentication

  • Enrolled devices will remain enrolled indefinitely. If periodic re-enrollment is required (e.g. every 30 days), your customer success representative can provide you with advice and code snippets for how to achieve this.
  • Signed SAML requests and responses are supported for customers on our Enterprise plan.
  • Best-practices for authentication on shared devices are available from your customer success representative.