Paul's pages

OutSystems and related stories

How to secure your OutSystems applications

Security headers

The OWASP Secure Headers Project (also called OSHP) describes HTTP response headers that your application can use to increase the security of your application. Once set, these HTTP response headers can restrict modern browsers from running into easily preventable vulnerabilities. The OWASP Secure Headers Project intends to raise awareness and use of these headers.

You can add HTTP Security Headers to your OutSystems application using the Factory Configuration forge component. See [Factory Configuration] How to - Setup the web.config file to send HTTP Security Headers to your App

OWASP has an overview of available analysis tools to validate an HTTP security header configuration.

I used securityheaders.com to check the headers of my sample application.

In the following sections we describe each header and how to set it in OutSystems at the server and application level.

Strict-Transport-Security

HTTP Strict Transport Security (also named HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797. A server implements an HSTS policy by supplying a header (Strict-Transport-Security) over an HTTPS connection (HSTS headers over HTTP are ignored).

Values:

Value Description
max-age=SECONDS The time, in seconds, that the browser should remember that this site is only to be accessed using HTTPS.
includeSubDomains If this optional parameter is specified, this rule applies to all of the site’s subdomains as well.

Example:

Strict-Transport-Security: max-age=31536000 ; includeSubDomains

Follow the steps in this [Documentation] Enforce HTTPS Security page to set the strict transport security settings.

X-Frame-Options

The X-Frame-Options response header (also named XFO) improves the protection of web applications against clickjacking. It instructs the browser whether the content can be displayed within frames. The Content-Security-Policy (CSP) frame-ancestors directive obsoletes the X-Frame-Options header. If a resource has both policies, the CSP frame-ancestors policy will be enforced and the X-Frame-Options policy will be ignored.

Values

Value Description
deny No rendering within a frame.
sameorigin No rendering if origin mismatch.
allow-from: DOMAIN Allows rendering if framed by frame loaded from DOMAIN (not supported by modern browsers).

Example

X-Frame-Options: deny

We use CPS therefore there is no need to set the X-Frame-Options header

X-Content-Type-Options

TODO

Content-Security-Policy

Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution. <mdn csp>

It is advisable that you configure the CSP in every environment. Start with the allowed sources in an environment, for all its applications. Then, specify the sources per application, as needed, to override the general configuration.

Follow this [Documentation] Apply Content Security Policy guide to apply csp to your environments and applications.

Allow google fonts imports

When you import a google font e.g.: @import url('https://fonts.googleapis.com/css?family=Roboto:400,700'); then we must add two directives. One fore the import and one for the actual fonts as these are loaded from a different location.

directive value
Style-src https://fonts.googleapis.com
Font-src https://fonts.gstatic.com

Other directives

Consider requiring Trusted Types for scripts to lock down DOM XSS injection sinks. You can do this by adding “require-trusted-types-for ‘script’” to your policy. In the Other directives input add the following:

require-trusted-types-for 'script'

High Security Findings

At runtime the following values are added to the ‘script’ directive:

These are reported as a ‘High Severity Finding’ TODO explain why they are inserted and how the risk is mitigated.

CSP Evaluator

You can validate your csp with this csp evaluator

X-Permitted-Cross-Domain-Policies

TODO

Referrer-Policy

The Referrer-Policy HTTP header governs which referrer information, sent in the Referer header, should be included with requests made.

<add name="Referrer-Policy" value="no-referrer" />

Clear-Site-Data

Cross-Origin-Embedder-Policy

Cross-Origin-Opener-Policy

Cross-Origin-Resource-Policy

Cache-Control

Restrict Access to an Internal Network

Restrict Access to an Internal Network

Configuration proposal

Please note the best practices below suggest methods to change web server configuration to add headers. Security headers can also be successfully added to your application at the software level as well in almost every web language. Many web frameworks add some of these headers automatically.

The following section proposes a configuration for the actively supported and working draft security headers. Proposed values

⚠️ The Pragma header is only specified for backwards compatibility with the HTTP/1.0 caches.

💡 Content of the table below is also provided, as JSON, via this file (automatically updated).

Header name Proposed value
Strict-Transport-Security max-age=31536000 ; includeSubDomains
X-Frame-Options deny
X-Content-Type-Options nosniff
Content-Security-Policy default-src ‘self’; object-src ‘none’;
frame-ancestors ‘none’; upgrade-insecure-requests; block-all-mixed-content
X-Permitted-Cross-Domain-Policies none
Referrer-Policy no-referrer
Clear-Site-Data “cache”,”cookies”,”storage”
Cross-Origin-Embedder-Policy require-corp
Cross-Origin-Opener-Policy same-origin
Cross-Origin-Resource-Policy same-origin
Permissions-Policy accelerometer=(),ambient-light-sensor=(),autoplay=(), battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()
Cache-Control no-store, max-age=0
Pragma no-cache

Secure Cookies

Example security headers

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" encoding="UTF-8"/> 

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
 
    <xsl:template match="/configuration/system.webServer/httpProtocol/customHeaders">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
            <add name="X-Frame-Options" value="SAMEORIGIN" />
            <add name="X-XSS-Protection" value="1; mode=block" />
            <add name="Referrer-Policy" value="no-referrer" />
           <add name="Permissions-Policy" value="accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
{
  "last_update_utc": "2023-01-12 21:19:59",
  "headers": [
    {
      "name": "Cache-Control",
      "value": "no-store, max-age=0"
    },
    {
      "name": "Clear-Site-Data",
      "value": "\"cache\",\"cookies\",\"storage\""
    },
    {
      "name": "Content-Security-Policy",
      "value": "default-src 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content"
    },
    {
      "name": "Cross-Origin-Embedder-Policy",
      "value": "require-corp"
    },
    {
      "name": "Cross-Origin-Opener-Policy",
      "value": "same-origin"
    },
    {
      "name": "Cross-Origin-Resource-Policy",
      "value": "same-origin"
    },
    {
      "name": "Permissions-Policy",
      "value": "accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()"
    },
    {
      "name": "Pragma",
      "value": "no-cache"
    },
    {
      "name": "Referrer-Policy",
      "value": "no-referrer"
    },
    {
      "name": "Strict-Transport-Security",
      "value": "max-age=31536000 ; includeSubDomains"
    },
    {
      "name": "X-Content-Type-Options",
      "value": "nosniff"
    },
    {
      "name": "X-Frame-Options",
      "value": "deny"
    },
    {
      "name": "X-Permitted-Cross-Domain-Policies",
      "value": "none"
    }
  ]
}