You can add HTTP response headers for web applications to improve the application security. This topic describes how to use an Envoy filter to add HTTP response headers in Alibaba Cloud Service Mesh (ASM).

Prerequisites

Background information

Open Web Application Security Project (OWASP) provides best practices and a coding framework to describe how to use HTTP response headers to improve the security of applications. The following table describes basic HTTP response headers.
HTTP response header Default value Description
Content-Security-Policy frame-ancestors none; Prevents clickjacking attacks from other websites.
X-XSS-Protection 1;mode=block Activates the cross-site scripting (XSS) filter (if it is available) of a browser so that the browser can stop rendering when XSS attacks are detected.
X-Content-Type-Options Nosniff Disables content sniffing of a browser.
Referrer-Policy no-referrer Specifies to send no referrer information along with requests.
X-Download-Options noopen Prevents old versions of Internet Explorer from allowing downloads to be automatically executed.
X-DNS-Prefetch-Control off Disables DNS prefetching for external hyperlinks on web pages.
Server envoy The server that generates the response. This HTTP response header is automatically set by the Istio ingress gateway.
X-Powered-by N/A Contains information about the hosting environments or other frameworks. Do not set this HTTP response header if you want to hide the name and version information of vulnerable application servers.
Feature-Policy

camera 'none';

microphone 'none';

geolocation 'none';

encrypted-media 'none';

payment 'none';

speaker 'none';

usb 'none';

Specifies the features and API operations that are available to browsers.
In this example, a Bookinfo application is used. For more information, see Deploy an application in an ASM instance. You can run the following curl command to query the HTTP response headers of the application:
curl -I http://{IP address of the ingress gateway}/productpage
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 5183
server: istio-envoy
date: Tue, 28 Jan 2020 08:15:21 GMT
x-envoy-upstream-service-time: 28

The command output indicates that the application does not contain any of the HTTP response headers described in the preceding table. You can create an Envoy filter to add HTTP response headers for the application to improve the application security.

Procedure

  1. Create an Envoy filter.
    • If the version of your ASM instance is earlier than v1.12.4.0-g7d140f10-aliyun, run the following command to create an Envoy filter:
      kubectl apply -f - <<EOF
      apiVersion: networking.istio.io/v1alpha3
      kind: EnvoyFilter
      metadata:
        name: addheader-into-ingressgateway
        namespace: istio-system
        labels:
          asm-system: 'true'
          provider: asm
      spec:
        workloadSelector:
          # You can use the workloadSelector field to select workloads in a specific namespace for the Envoy filter. 
          labels:
            istio: ingressgateway
        configPatches:
          # The Envoy configuration that you need to modify. 
        - applyTo: HTTP_FILTER
          match:
            context: GATEWAY
            proxy:
              proxyVersion: '^1\.9.*'
            listener:
              filterChain:
                filter:
                  name: "envoy.filters.network.http_connection_manager"
                  subFilter:
                    name: "envoy.filters.http.router"
          patch:
            operation: INSERT_BEFORE
            value: # Lua script configuration 
              name: envoy.lua
              typed_config:
                "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
                inlineCode: |-
                  function envoy_on_response(response_handle)
                      function hasFrameAncestors(rh)
                      s = rh:headers():get("Content-Security-Policy");
                      delimiter = ";";
                      defined = false;
                      for match in (s..delimiter):gmatch("(.-)"..delimiter) do
                          match = match:gsub("%s+", "");
                          if match:sub(1, 15)=="frame-ancestors" then
                          return true;
                          end
                      end
                      return false;
                      end
                      if not response_handle:headers():get("Content-Security-Policy") then
                      csp = "frame-ancestors none;";
                      response_handle:headers():add("Content-Security-Policy", csp);
                      elseif response_handle:headers():get("Content-Security-Policy") then
                      if not hasFrameAncestors(response_handle) then
                          csp = response_handle:headers():get("Content-Security-Policy");
                          csp = csp .. ";frame-ancestors none;";
                          response_handle:headers():replace("Content-Security-Policy", csp);
                      end
                      end
                      if not response_handle:headers():get("X-Frame-Options") then
                      response_handle:headers():add("X-Frame-Options", "deny");
                      end
                      if not response_handle:headers():get("X-XSS-Protection") then
                      response_handle:headers():add("X-XSS-Protection", "1; mode=block");
                      end
                      if not response_handle:headers():get("X-Content-Type-Options") then
                      response_handle:headers():add("X-Content-Type-Options", "nosniff");
                      end
                      if not response_handle:headers():get("Referrer-Policy") then
                      response_handle:headers():add("Referrer-Policy", "no-referrer");
                      end
                      if not response_handle:headers():get("X-Download-Options") then
                      response_handle:headers():add("X-Download-Options", "noopen");
                      end
                      if not response_handle:headers():get("X-DNS-Prefetch-Control") then
                      response_handle:headers():add("X-DNS-Prefetch-Control", "off");
                      end
                      if not response_handle:headers():get("Feature-Policy") then
                      response_handle:headers():add("Feature-Policy",
                                                      "camera 'none';"..
                                                      "microphone 'none';"..
                                                      "geolocation 'none';"..
                                                      "encrypted-media 'none';"..
                                                      "payment 'none';"..
                                                      "speaker 'none';"..
                                                      "usb 'none';");
                      end
                      if response_handle:headers():get("X-Powered-By") then
                      response_handle:headers():remove("X-Powered-By");
                      end
                  end
      EOF
      proxyVersion: Specifies the Istio version to which the Envoy filter applies. Set the value to your Istio version. Some fields in the Envoy filter may be incompatible with the Istio version. You may need to modify the fields in the Envoy filter based on your Istio version:
      • If your Istio version is 1.8 or earlier, set the proxyVersion parameter to your Istio version and replace envoy.filters.network.http_connection_manager with envoy.http_connection_manager, envoy.filters.http.router with envoy.router, and type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua with type.googleapis.com/envoy.config.filter.http.lua.v2.Lua.
      • If your Istio version is 1.9 or later, set the proxyVersion parameter to your Istio version.
    • If the version of your ASM instance is v1.12.4.0-g7d140f10-aliyun or later, create an Envoy filter on the Market Place page in the ASM console.
      1. Log on to the ASM console.
      2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
      3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.
      4. On the details page of the ASM instance, choose Plugin Extension Center > Market Place in the left-side navigation pane.
      5. On the Market Place page, click Add HTTP response headers. On the Plugin Detail page, click the Plugin Config tab.
      6. In the Plugin Effective scope section, select Gateway Scope and click Add ASM Gateway to effective scope.
      7. In the Add ASM Gateway to effective scope dialog box, select ingressgateway in the Select ASM Gateway section, click the Add icon icon to add ingressgateway to the selected section, and then click OK.
        Note ingressgateway is the name of the default ingress gateway. You can also select other gateways where you want the added HTTP response headers to take effect.
      8. In the Plugin Config section, delete all content from the YAML code editor, turn on Plugin Switch, and wait until the plugin is enabled.

        After the plugin is enabled, ASM automatically creates an Envoy filter.

  2. Run the following curl command to query the HTTP response headers of the application:
    curl -I http://{IP address of the ingress gateway}/productpage
    Expected output:
    HTTP/1.1 200 OK
    content-type: text/html; charset=utf-8
    content-length: 4183
    server: istio-envoy
    date: Tue, 28 Jan 2020 09:07:01 GMT
    x-envoy-upstream-service-time: 17
    content-security-policy: frame-ancestors none;
    x-frame-options: deny
    x-xss-protection: 1; mode=block
    x-content-type-options: nosniff
    referrer-policy: no-referrer
    x-download-options: noopen
    x-dns-prefetch-control: off
    feature-policy: camera 'none';microphone 'none';geolocation 'none';encrypted-media 'none';payment 'none';speaker 'none';usb 'none';
    The command output indicates that the application contains the HTTP response headers described in Basic HTTP response headers.

FAQ

Why am I unable to access an application by using a URL with special characters?

The encoding formats of special characters may not comply with the URL encoding rule. For example, special character Á uses Unicode encoding instead of ASCII encoding. For more information, see Envoy cannot parse non-alphanumeric characters if not urlencoded.