You can add HTTP request headers to application requests to secure web applications. This topic describes how to define EnvoyFilter resources to add HTTP request headers.

Prerequisites

Background information

Open Web Application Security Project (OWASP) provides best practices and a coding framework to describe how to use HTTP request headers to secure applications. The following table describes basic HTTP request headers.
Table 1. Basic HTTP request headers
HTTP request 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 any 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 webpages.
Server envoy The server that generates the response. This HTTP 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 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 operations that are available to browsers.
This topic takes the Bookinfo application as an example. For more information, see Deploy applications in an ASM instance. You can run the curl command to query the HTTP request headers of the application.
curl -I http://{IP address of the ingress gateway service}/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 requests from the application homepage does not contain any HTTP headers described in Table 1. You can define an EnvoyFilter resource to quickly add secure HTTP headers to application requests.

Procedure

  1. Define an EnvoyFilter resource.
    Run the following command to define an EnvoyFilter resource:
    kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: addheader-into-ingressgateway
      namespace: istio-system
    spec:
      workloadSelector:
        # select by label in the same namespace
        labels:
          istio: ingressgateway
      configPatches:
        # The Envoy config you want to modify
      - applyTo: HTTP_FILTER
        match:
          context: GATEWAY
          listener:
            filterChain:
              filter:
                name: "envoy.http_connection_manager"
                subFilter:
                  name: "envoy.router"
        patch:
          operation: INSERT_BEFORE
          value: # lua filter specification
            name: envoy.lua
            typed_config:
              "@type": "type.googleapis.com/envoy.config.filter.http.lua.v2.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
  2. Verify HTTP request headers.
    Run the following curl command to check whether the application requests contain secure HTTP headers:
    curl -I http://{IP address of the ingress gateway service}/productpage
    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 requests from the application homepage contains the secure HTTP headers described in Table 1.