Security

This document is referring to a past Scout release. Please click here for the recent version.

Default HTTP Response Headers

All Scout HTTP servlets delegate to a central authority to append HTTP response headers. This is the bean HttpServletControl. It enables developers to control which headers that should be added to the HTTP response for each servlet and request.

The next sections describe the headers that are added to any response by default. Beside these also the following headers may be of interest for an end user application (consider adding them to your application if possible):

Please note that not all headers are supported in all user agents!

X-Frame-Options

The X-Frame-Options HTTP response header [1] can be used to indicate whether a user agent should be allowed to render a page in a <frame>, <iframe> or <object>. Sites can use this to avoid clickjacking [2] attacks, by ensuring that their content is not embedded into other sites. The X-Frame-Options header is described in RFC 7034 [3].

In Scout this header is set to SAMEORIGIN which allows the page to be displayed in a frame on the same origin (scheme, host and port) as the page itself only.

X-XSS-Protection

This header is no longer interpreted by modern browser engines and is considered insecure. Scout does no longer set this header in HttpServletControl.

Content Security Policy

Content Security Policy is an HTTP response header that helps you reduce XSS risks on modern user agents by declaring what dynamic resources are allowed to load [4]. The CSP header is described in Level 1 and Level 2. There is also a working draft for a Level 3.

Scout makes use of Level 1 (and one directive from Level 2) and sets by default the following settings:

  • JavaScript [5]: Only accepts JavaScript resources from the same origin (same scheme, host and port). Inline JavaScript is allowed and unsafe dynamic code evaluation (like eval(string), setTimeout(string), setInterval(string), new Function(string)) is allowed as well.

  • Stylesheets (CSS) [6]: Only accepts Stylesheet resources from the same origin (same scheme, host and port). Inline style attributes are allowed.

  • Frames [7]: All sources are allowed because the iframes created by the Scout BrowserField run in the sandbox mode and therefore handle the security policy on their own.

  • All other types (Image, WebSocket [8], EventSource [9], AJAX calls [10], fonts, <object> [11], <embed> [12], <applet> [13], <audio> [14] and <video> [15]) only allow resources from the same origin (same scheme, host and port).

If a resource is blocked because it violates the CSP a report is created and logged on server side using level warning. This is done in the class ContentSecurityPolicyReportHandler. This enables admins to monitor the application and to react if a CSP violation is detected.

The UiServlet checks if the session cookie is configured safely. The validation is only performed on first access to the UiServlet. There is no automatic validation on the backend server side or on any custom servlets!

If the validation fails, a corresponding error message is logged to the server and an exception is thrown making the UiServlet inaccessible. Because of security reasons the exception shown to the user includes no details about the error. These can only be seen on the server side log.

HttpOnly

First the existence of the HttpOnly flag is checked. The servlet container will then add this flag to the Set-Cookie HTTP response header. If the user agent supports this flag, the cookie cannot be accessed through a client side script. As a result even if a cross-site scripting (XSS) flaw exists and a user accidentally accesses a link that exploits this flaw, the user agent will not reveal the cookie to a third party. For a list of user agents supporting this feature please refer to OWASP.

This flag is enabled by default via scout.app.sessionCookieConfigHttpOnly.

Secure

Second the existence of the Secure flag is checked. The servlet container will then add this flag to the Set-Cookie HTTP response header. The purpose of the secure flag is to prevent cookies from being observed by unauthorized parties due to the transmission of a cookie in clear text. Therefore, setting this flag will prevent the user agent from transmitting the session id over an unencrypted channel.

This flag is enabled by default for non-development builds via scout.app.sessionCookieConfigSecure.

This of course only makes sense if the application is exposed to the end user using an encrypted channel like HTTPS (which is strongly recommended).

Secure Output

This chapter describes how HTML Output can be handled in a secure way.

Scout applications often display potentially dangerous data, e.g. user input or data from other systems. Encoding this input in such a way, that it can not be executed, prevents security vulnerabilities like cross-site scripting.

Encoding by Default

By default, all input in the Scout model is encoded. Examples are values/labels in value fields, cells in tables, message in message box. The reason behind this default choice is that developers do not have to think about output encoding in the standard case and are therefore less likely to forget output encoding and introduce a security vulnerability.

Example: In the following label field, the HTML <b> tag is encoded as &lt;b&gt;bold text&lt;/b&gt;:

encodedField
public class LabelField extends AbstractLabelField {
  @Override
  protected void execInitField() {
    setValue("...<b>Bold text</b>...");
  }

Html Enabled

Sometimes developers may want to use HTML in the Scout model.

Examples are

  • Simple styling of dynamic content, such as addresses or texts in message boxes

  • Text containing application-internal or external links

  • Html or XML content received from other systems, such as e-mails or html pages

Html input should only partially be encoded or not at all.

To disable the encoding of the whole value, the property HtmlEnabled can be used:

public class NoEncodingLabelField extends AbstractLabelField {
  @Override
  protected boolean getConfiguredHtmlEnabled() {
    return true;
  }
@Override
protected void execInitField() {
  setValue("...<b>Bold text</b>...");
}

There are several ways to implement the use cases above. Some typical implementations are described in the following sections.

CSS Class and Other Model Properties

Often using HTML in value fields or table cells is not necessary for styling. Very basic styling can be done for example by setting the CSS class.

HTML Builder

For creating simple HTML files or fragments with encoded user input, the class org.eclipse.scout.rt.platform.html.HTML can be used. It is also easily possible to create application internal and external link with this approach.

Styling in the UI-Layer

For more complex HTML, using IBeanField in the scout model and implementing the styling in the UI-Layer is often the preferred way. Links are possible as well.

Manual Encoding

It is also possible to encode any String manually using StringUtility.htmlEncode(String). org.eclipse.scout.rt.platform.html.HTML uses this method internally for encoding. However, using HTML is recommended, where possible, because it is more concise and leads to less errors.

Using a White-List Filter

If HTML or XML from external sources or more complex HTML are used in the Scout model, using a white-list filter might be the best way to avoid security bugs. Libraries, such as JSoup provide such a white-list filter. Scout currently does not include any services or utilities for using white-list filters, because the configuration and usage is very use-case-specific and would therefore not add much benefit.

Authorization (Granting)

Scout uses the java.security API principles to grant access to a specific resource.

Each user has a set of granted java.security.Permission instances. This set is a java.security.PermissionCollection. A call to PermissionCollection.implies(Permission p) does the access check. The argument p in this call is a new permission instance for which we want to do the access check and which is compared against the granted permissions. Usually, the permission collection implementation iterates through all granted permissions and calls on each Permission.implies(Permission p) until one call returns true.

Scout adds some concepts and helpers to this API:

IPermission

Unlike other permissions, a permission implementing this interface can only be implied by another IPermission with the same name. A permission used together with scouts security API does not have to implement the IPermission interface, but it is recommended.

PermissionLevel

An IPermission, which is part of an IPermissionCollection has always a granted access level assigned (IPermission.getLevel()). If the granted level is PermissionLevel.NONE, any access checks will fail.

IAccessControlService

This service is responsible to provide and manage a users set of granted permissions. A scout application usually extends AbstractAccessControlService and implements #execLoadPermissions.

ACCESS

Provides a set of convenience methods to check access.

Let us assume you require a permission to allow a user to access companies.

public class ReadCompanyPermission extends AbstractPermission {
  private static final long serialVersionUID = 1L;

  public ReadCompanyPermission() {
    super("scoutdoc.ReadCompany");
  }
}

To check access one can use ACCESS.

if (ACCESS.check(new ReadCompanyPermission())) { (1)
  throw new AccessForbiddenException(TEXTS.get("YouAreNotAllowedToReadThisData"));
}

ACCESS.checkAndThrow(new ReadCompanyPermission()); (2)
1 Checks permission against granted permissions of current user.
2 Checks permission and if this check fails, throw an AccessForbiddenException with a default message.

We can define a default access check failed message for a permission.

public class CreateCompanyPermission extends AbstractPermission {
  private static final long serialVersionUID = 1L;

  public CreateCompanyPermission() {
    super("scoutdoc.CreateCompany");
  }

  @Override
  public String getAccessCheckFailedMessage() {
    return TEXTS.get("YouAreNotAllowedToRegisterThisData");
  }
}

ACCESS allows to check multiple permissions at once.

ACCESS.checkAllAndThrow(new ReadCompanyPermission(), new CreateCompanyPermission());

ACCESS.checkAnyAndThrow(new ReadCompanyPermission(), new CreateCompanyPermission());

We have seen some simple permission checks. Now let us assume, that some users may modify a company only if they have registered the company by themselves. For this use case we introduce a new permission level ScoutdocPermissionLevels.OWN. This is the permission level which is granted for those users.

public final class ScoutdocPermissionLevels {

  private ScoutdocPermissionLevels() {
  }

  public static final int LEVEL_NONE = PermissionLevel.LEVEL_NONE;
  public static final int LEVEL_OWN = 10;
  public static final int LEVEL_ALL = PermissionLevel.LEVEL_ALL;

  public static final PermissionLevel NONE = PermissionLevel.NONE;
  public static final PermissionLevel OWN =
      PermissionLevel.register(LEVEL_OWN, "OWN", true, () -> TEXTS.get("Own"));
  public static final PermissionLevel ALL = PermissionLevel.ALL;

  public static void init() {
    // ensures all static initializers have been called
  }
}

In order to check access for this new level we have to override AbstractPermission#evalPermission.

public class UpdateCompanyPermission extends AbstractPermission {
  private static final long serialVersionUID = 1L;

  private final UUID m_companyId;

  public UpdateCompanyPermission() {
    this(null);
  }

  public UpdateCompanyPermission(UUID companyId) {
    super("scoutdoc.UpdateCompany");
    m_companyId = companyId;
  }

  public UUID getCompanyId() {
    return m_companyId;
  }

  @Override
  protected boolean evalPermission(IPermission p) {
    // Precondition: p.getClass() == getClass() && getName().equals(p.getName()) &&
    //               getLevel() != PermissionLevel.NONE
    if (ScoutdocPermissionLevels.OWN == getLevel()) {
      UUID companyId = ((UpdateCompanyPermission) p).getCompanyId();
      return BEANS.get(ICompanyService.class).isOwnCompany(companyId);
    }
    return true; // ScoutdocPermissionLevels.ALL == getLevel()
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = super.hashCode();
    result = prime * result + ((m_companyId == null) ? 0 : m_companyId.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!super.equals(obj)) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    UpdateCompanyPermission other = (UpdateCompanyPermission) obj;
    if (m_companyId == null) {
      if (other.m_companyId != null) {
        return false;
      }
    }
    else if (!m_companyId.equals(other.m_companyId)) {
      return false;
    }
    return true;
  }
}
ACCESS.checkAndThrow(new UpdateCompanyPermission(companyId));

If such a service call would be expensive, one may cache the result of such a permission check. You have to implement this by yourself. A recommended solution is to create a wrapper around IPermissionCollection and this wrapper caches calls to IPermissionCollection#implies if required.

Scout JS

The access utility provides the possibility and convenience of checking permissions in Scout JS code.

All necessary information the Scout JS code needs is made available by the PermissionResource in the org.eclipse.scout.rt.api-module. To enable the feature this REST resource needs to be added to the server and the access utility needs to be bootstrapped in the entry file.

Listing 1. Add maven dependency to server providing REST resources (see REST)
<dependency>
  <groupId>org.eclipse.scout.rt</groupId>
  <artifactId>org.eclipse.scout.rt.api</artifactId>
</dependency>
Listing 2. bootstrap the access utility in the entry file myapp.js
new App().init({
  bootstrap: {
    permissionsUrl: 'api/permissions'
  }
});

If the visibility of a field depends on a permission the access utility can be used to evaluate this permission. The method quickCheck returns a boolean while check returns a JQuery.Promise<boolean>

field.setVisible(access.quickCheck('SomePermission'));

access.check('SomePermission').then(granted => field.setVisible(granted);

If one has created IOwnPermissions with a special implies-logic and wants to make them available in Scout JS a couple of things need to be added:

  • OwnPermissionDo, data object used to transfer additional properties from the server to the Scout JS code

  • ToOwnPermissionDoFunction, IToDoFunction transforming IOwnPermission into the data object

  • OwnPermission, Scout JS pendant for IOwnPermission used by the access utility

Listing 3. OwnPermissionDo containing the ToOwnPermissionDoFunction
@TypeName("myapp.OwnPermission")
public class OwnPermissionDo extends PermissionDo {

  protected static final String OWN_PERMISSION_OBJECT_TYPE = "myapp.OwnPermission";

  public DoValue<String> foo() {
    return doValue("foo");
  }

  public DoValue<String> bar() {
    return doValue("bar");
  }

  /* **************************************************************************
   * CUSTOM CONVENIENCE TO DO FUNCTION
   * *************************************************************************/

  @Order(4000)
  public static class ToOwnPermissionDoFunction extends AbstractToPermissionDoFunction<IOwnPermission, OwnPermissionDo> {

    @Override
    public void apply(IOwnPermission ownPermission, OwnPermissionDo ownPermissionDo) {
      BEANS.get(ToPermissionDoFunction.class).apply(ownPermission, ownPermissionDo);
      ownPermissionDo
          .withObjectType(OWN_PERMISSION_OBJECT_TYPE) // important to create the correct Scout JS pendant
          .withFoo(ownPermission.getFoo())
          .withBar(ownPermission.getBar());
    }
  }

  /* **************************************************************************
   * GENERATED CONVENIENCE METHODS
   * *************************************************************************/

  // ...
}
Listing 4. important overridden methods in the Scout JS OwnPermission
override matches(permission: Permission): boolean {
  if (!super.matches(permission)) {
    return false;
  }
  // check if permission is OwnPermission and foo matches
  if (!(permission instanceof OwnPermission)) {
    return false;
  }
  return this.foo === permission.foo;
}

protected override _evalPermissionQuick(permission: Permission): boolean {
  // check if bar matches
  return super._evalPermissionQuick(permission) && this.bar === permission.bar;
}

protected override _evalPermission(permission: Permission): JQuery.Promise<boolean> {
  // call server to evaluate foo and bar
  return ajax.getJson(`api/permissions/checkFooBar/${permission.foo}/${permission.bar}`)
    .catch((error: AjaxError) => {
      // handle error and return false
      return false;
    });
}

The PermissionResource will automatically collect the granted IOwnPermissions of a user (see Authorization (Granting)). These IOwnPermissions will be transformed by the ToOwnPermissionDoFunction and send to the browser where OwnPermission instances are created. Each access check will now automatically consider the matches-, implies- and evaluate-logic of OwnPermission.

Login Form

Scout provides support for form based login. To activate the form based login, a login.html needs to be provided and the FormBasedAccessController along with a ICredentialVerifier needs to be added to the AuthFilter. An example setup can be found in the archetype.

Two-factor Authentication (2FA)

The form based login also supports two-factor-authentication.

If the registered ICredentialVerifier requests a second factor by returning AUTH_2FA_REQUIRED, the FormBasedAccessController instructs the UI to show an input field so the user can enter the token. To verify this token a second-factor verifier ICredentialVerifier needs to be registered.