全部產品
Search
文件中心

Alibaba Cloud Service Mesh:在ASM中通過EnvoyFilter添加HTTP回應標頭

更新時間:Jun 30, 2024

在應用程式中添加HTTP回應標頭可以提高Web應用程式的安全性。本文介紹如何通過定義EnvoyFilter添加HTTP回應標頭。

前提條件

背景資訊

OWASP提供了最佳實務指南和編程架構,描述了如何使用安全回應標頭保護應用程式的安全。HTTP回應標頭的基準配置如下:

HTTP回應標頭

預設值

描述

Content-Security-Policy

frame-ancestors none;

防止其他網站進行Clickjacking攻擊。

X-XSS-Protection

1;mode=block

啟用瀏覽器的XSS過濾器(如果可用),檢測到XSS時阻止渲染。

X-Content-Type-Options

Nosniff

禁用瀏覽器的內容嗅探。

Referrer-Policy

no-referrer

禁用自動發送引薦來源。

X-Download-Options

noopen

禁用舊版本IE中的自動開啟下載功能。

X-DNS-Prefetch-Control

off

禁用對頁面上的外部連結的推測性DNS解析。

Server

envoy

由Istio的入口網關自動化佈建。

X-Powered-by

無預設值

去掉該值來隱藏潛在易受攻擊的應用程式伺服器的名稱和版本。

Feature-Policy

camera ‘none’;

microphone ‘none’;

geolocation ‘none’;

encrypted-media ‘none’;

payment ‘none’;

speaker ‘none’;

usb ‘none’;

控制可以在瀏覽器中使用的功能和API。

以Bookinfo應用程式為例(詳情請參見在ASM執行個體關聯的叢集中部署應用),通過Curl命令可以看到應用程式的HTTP回應標頭資訊如下。

curl -I http://{入口網關服務的IP地址}/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

可以看到,在預設情況下,應用程式範例的入口首頁響應並沒有包含上述表格中安全相關的HTTP回應標頭。通過Istio EnvoyFilter可以快速添加安全相關的HTTP回應標頭。

操作步驟

  1. 按照ASM執行個體版本部署EnvoyFilter。

    ASM執行個體版本為1.12.4.0及以上

    您可以通過外掛程式市場直接部署EnvoyFilter。

    1. 登入ASM控制台,在左側導覽列,選擇服務網格 > 網格管理

    2. 網格管理頁面,單擊目標執行個體名稱,然後在左側導覽列,選擇外掛程式擴充中心 > 外掛程式市場

    3. 外掛程式市場頁面,單擊添加HTTP回應標頭,然後在外掛程式詳情頁面,單擊建立外掛程式執行個體

    4. 外掛程式生效範圍地區,選中網關生效,然後單擊添加網關生效範圍

    5. 添加網關生效範圍對話方塊中,在選擇網關地區選中ingressgateway,單擊添加表徵圖,將ingressgateway添加到已選擇地區中,然後單擊確定

      說明

      ingressgateway為ASM預設部署的網關名稱,您也可以選擇希望生效添加HTTP回應標頭能力的其它ASM網關。

    6. 外掛程式配置地區,刪除YAML框中的所有內容,然後開啟生效開關,等待外掛程式啟用。

      在外掛程式啟用後,ASM會自動建立EnvoyFilter。

    ASM執行個體版本為1.12.4.0以下

    您可以執行以下命令,直接部署EnvoyFilter。

    展開查看命令

    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:
        # EnvoyFilter通過標籤選擇同一命名空間下的工作負載。
        labels:
          istio: ingressgateway
      configPatches:
        # 需要修改的Envoy配置。
      - 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指令碼配置。
            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:配置為您當前的Istio版本。EnvoyFilter建立時需要設定proxyVersion來指定期望作用的Istio版本範圍,EnvoyFilter配置中的一些欄位存在Istio版本不相容的可能性。不同Istio版本的EnvoyFilter內容不同:

    • 如果您使用的Istio1.8及以下版本,請根據版本替換proxyVersion欄位,並且替換上述EnvoyFilter中的envoy.filters.network.http_connection_managerenvoy.http_connection_managerenvoy.filters.http.routerenvoy.routertype.googleapis.com/envoy.extensions.filters.http.lua.v3.Luatype.googleapis.com/envoy.config.filter.http.lua.v2.Lua

    • 如果您使用的Istio1.9及以上版本,請根據版本替換proxyVersion欄位。

  2. 執行以下命令,驗證安全HTTP回應標頭是否添加成功。

    請將{入口網關的IP地址}替換為實際的網關地址。關於如何擷取入口網關地址,請參見擷取入口網關地址

    curl -I http://{入口網關的IP地址}/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';

    由預期輸出得到,應用程式範例的入口首頁響應已經包含了HTTP回應標頭的基準配置中所描述的安全相關的HTTP回應標頭。

FAQ

為什麼無法訪問帶有特殊字元的URL?

以特殊字元Á為例,特殊字元Á的編碼格式採用Unicode,而不是ASCII。因此請求的URL中帶有特殊字元,不符合URL規範。更多資訊,請參見Envoy cannot parse non-alphanumeric characters if not urlencoded