IIS CORS 模組配置參考

由IIS團隊製作

本文概述了 IIS CORS 模組,並說明模組的配置。

功能概觀

Microsoft IIS CORS 模組是一個擴充套件,使網站能支援 CORS(跨來源資源共享)協定。

IIS CORS 模組提供一種方式,讓網頁伺服器管理員與網站作者能讓他們的應用程式支援 CORS 協定。 透過此模組,開發者可以將 CORS 邏輯從應用程式中移除,並依賴網頁伺服器。 模組對 CORS 請求的處理由設定中定義的規則決定。 這些 CORS 規則可輕易定義或配置,方便將所有 CORS 協定處理委派給模組。

IIS CORS 模組是伺服器端的 CORS 元件

CORS 協定管理用戶端與伺服器的通訊。 通常,網頁瀏覽器作為用戶端的 CORS 元件,而 IIS 伺服器則在 IIS CORS 模組的協助下,作為伺服器端的 CORS 元件。

CORS 請求發生在一個具備協定需求的用戶端,例如網頁瀏覽器,向與目前網域不同的網域(來源)提出請求時。 這種情況稱為跨來源請求。 當未使用 CORS 時,客戶端會阻擋跨來源請求。 當使用 CORS 模組時,IIS 會告知用戶端是否能根據 IIS 配置執行跨來源請求。

CORS 預檢申請

CORS 預檢請求用來判定伺服器是否設定所請求的資源為跨來源共享。 CORS 預檢使用 HTTP OPTIONS 方法,搭配 ACCESS-CONTROL-REQUEST-METHOD 及 ORIGIN 請求標頭。 IIS CORS 模組的設計目的是在其他 IIS 模組處理相同請求之前,先處理 CORS 的預檢請求。 OPTIONS 請求始終是匿名的,因此 CORS 模組讓 IIS 伺服器能夠正確回應預檢請求,即使在伺服器層面需要停用匿名身份驗證。

CORS 配置

IIS CORS 透過站點或應用程式 web.config 檔案設定,並在 中擁有自己的 cors 設定區 system.webServer段。

以下是啟用名為 contentSite 網站 CORS 的設定範例。 為 * 來源允許所有主機來源;但以 http://* 開頭的則被排除。 對於 https://*.microsoft.com 主機起點,CORS 回應會依多種 CORS 配置進行客製化,舉例來說。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="*" />
            <add origin="https://*.microsoft.com"
                 allowCredentials="true"
                 maxAge="120"> 
                <allowHeaders allowAllRequestedHeaders="true">
                    <add header="header1" />
                    <add header="header2" />
                </allowHeaders>
                <allowMethods>
                     <add method="DELETE" />
                </allowMethods>
                <exposeHeaders>
                    <add header="header1" />
                    <add header="header2" />
                </exposeHeaders>
            </add>
            <add origin="http://*" allowed="false" />
        </cors>
    </system.webServer>
</configuration>

透過 IIS CORS 模組,您可以:

  1. 啟用並停用整個 IIS 伺服器、特定 IIS 站點、應用程式、虛擬目錄、實體目錄或檔案(system.webServer/cors)的 CORS。
  2. 將所有原始主機網域設定為 * 原始主機規則,使其被接受。
  3. 設定特定來源主機網域的清單,並只允許與其中一個來源主機網域擁有相同原始請求標頭值的 CORS 請求。
  4. 在設定來源網域清單時,請設定例如http://*或https://*.mydomain.com的萬用字元來源主機網域。
  5. 準備一份不允許作為 CORS 請求來源的域名清單。
  6. 用設定的值自訂 CORS 回應標頭值。

cors 元素的屬性

Attribute 說明
enabled 選擇性的 Boolean 屬性。
指定是否啟用 CORS。
預設值為 false。
failUnlistedOrigins 選擇性的 Boolean 屬性。
規定若請求的起點未與設定的起始點清單匹配,或是起始主機設定為不允許,則 CORS 回應狀態碼是否應設為 403。
預設值為 false。

新增起源規則 <add>

起源規則

<add>集合元素<cors>指定一個要加入原點規則清單的個別原點。

原點規則的屬性

Attribute 說明
origin 必要的字串屬性。
指定可施加起源規則的起源宿主。 你可以用星號(*)來套用這條規則給所有原始請求標頭值。 你也可以用星號(*)作為子域名的萬用字元。 如果有多個起源規則,則會套用到最特定的起源主機名稱規則,不論允許的屬性值為何。
allowed 選擇性的 Boolean 屬性。
指定是否接受原始主機的 CORS 請求。
預設值為 true。
allowCredentials 選擇性的 Boolean 屬性。
指定是否設定存取控制允許憑證: true CORS 回應標頭。 此屬性應僅用於特定來源主機名稱,而非 * 來源主機,以符合 CORS 協定。
預設值為 false。
maxAge 選擇性的整數屬性。 持續時間以秒為單位。
指定預檢 CORS 請求的回應標頭值 Access-Control-Max-Age 。 存取控制-Max-Age 回應標頭應僅用於 CORS 預檢請求。 如果你不想在 CORS 回應中設定 Access-Control-Max-Age 標頭,請將此屬性設為 -1。
預設值為 -1。

僅使用 * 原始主機規則

若僅有 * 起源主機規則,IIS CORS 模組的行為與有特定起源主機名稱規則時有所不同。 若僅有 * 起點主機規則,IIS CORS 模組會執行以下操作:

  1. Access-Control-Allow-Origin 回應標頭的值設為 *,無論客戶端 CORS 元件傳送的請求標頭值為何 origin 。
  2. Vary: origin 回應標頭未被加入,因為 IIS CORS 不會產生 * 以外的存取控制允許來源回應標頭值,而且無需使用 Vary: origin 回應標頭值。

來源主機規則的子元素

Element 說明
allowHeaders 配置 AllowHeaders 集合,用於Access-Control-Allow-Headers CORS 回應標頭值,該值依據原始主機規則指定。
Access-Control-Allow-Headers回應標頭將僅針對實際的 CORS 請求設定,而非預檢請求。
allowMethods 配置 allowMethods 集合,用於原始Access-Control-Allow-Methods主機規則中指定的 CORS 回應標頭值。
Access-Control-Allow-Methods回應標頭只會設定給 CORS 預檢查請求。
exposeHeaders 配置 暴露Header 集合,此集合用於原始Access-Control-Expose-Headers主機規則中指定的 CORS 回應標頭值。
Access-Control-Expose-Headers回應標頭將僅適用於實際發生的 CORS 請求,而非預檢請求。

allowHeaders 元素的屬性

Attribute 說明
allowAllRequestedHeaders 選擇性的 Boolean 屬性。 若屬實,IIS 模組會取指定 Access-Control-Request-Headers CORS 請求標頭的值,並將 Access-Control-Allow-Headers 回應標頭設為相同值,表示所有標頭皆被允許。 若為假,則會將 Access-Control-Allow-Headers 回應標頭設定為 allowHeaders 集合的標頭值,表示只有列出的標頭被允許。 預設值為 false。

範例程式碼

C#

using System;
using System.Text;
using Microsoft.Web.Administration;

internal static class Sample {

    private static void Main() {

        using(ServerManager serverManager = new ServerManager()) {
            Configuration config = serverManager.GetWebConfiguration("contentSite");

            ConfigurationSection corsSection = config.GetSection("system.webServer/cors");
            corsSection["enabled"] = true;
            corsSection["failUnlistedOrigins"] = true;

            ConfigurationElementCollection corsCollection = corsSection.GetCollection();

            ConfigurationElement addElement = corsCollection.CreateElement("add");
            addElement["origin"] = @"*";
            corsCollection.Add(addElement);

            ConfigurationElement addElement1 = corsCollection.CreateElement("add");
            addElement1["origin"] = @"https://*.microsoft.com";
            addElement1["allowCredentials"] = true;
            addElement1["maxAge"] = 120;

            ConfigurationElement allowHeadersElement = addElement1.GetChildElement("allowHeaders");
            allowHeadersElement["allowAllRequestedHeaders"] = true;

            ConfigurationElementCollection allowHeadersCollection = allowHeadersElement.GetCollection();

            ConfigurationElement addElement2 = allowHeadersCollection.CreateElement("add");
            addElement2["header"] = @"header1";
            allowHeadersCollection.Add(addElement2);

            ConfigurationElement addElement3 = allowHeadersCollection.CreateElement("add");
            addElement3["header"] = @"header2";
            allowHeadersCollection.Add(addElement3);

            ConfigurationElementCollection allowMethodsCollection = addElement1.GetCollection("allowMethods");

            ConfigurationElement addElement4 = allowMethodsCollection.CreateElement("add");
            addElement4["method"] = @"DELETE";
            allowMethodsCollection.Add(addElement4);

            ConfigurationElementCollection exposeHeadersCollection = addElement1.GetCollection("exposeHeaders");

            ConfigurationElement addElement5 = exposeHeadersCollection.CreateElement("add");
            addElement5["header"] = @"header1";
            exposeHeadersCollection.Add(addElement5);

            ConfigurationElement addElement6 = exposeHeadersCollection.CreateElement("add");
            addElement6["header"] = @"header2";
            exposeHeadersCollection.Add(addElement6);
            corsCollection.Add(addElement1);

            ConfigurationElement addElement7 = corsCollection.CreateElement("add");
            addElement7["origin"] = @"http://*";
            addElement7["allowed"] = false;
            corsCollection.Add(addElement7);

            serverManager.CommitChanges();
        }
    }
}

JavaScript


var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST/contentSite";

var corsSection = adminManager.GetAdminSection("system.webServer/cors", "MACHINE/WEBROOT/APPHOST/contentSite");
corsSection.Properties.Item("enabled").Value = true;
corsSection.Properties.Item("failUnlistedOrigins").Value = true;

var corsCollection = corsSection.Collection;

var addElement = corsCollection.CreateNewElement("add");
addElement.Properties.Item("origin").Value = "*";
corsCollection.AddElement(addElement);


var addElement1 = corsCollection.CreateNewElement("add");
addElement1.Properties.Item("origin").Value = "https://*.microsoft.com";
addElement1.Properties.Item("allowCredentials").Value = true;
addElement1.Properties.Item("maxAge").Value = 120;
var allowHeadersElement = addElement1.ChildElements.Item("allowHeaders");
allowHeadersElement.Properties.Item("allowAllRequestedHeaders").Value = true;

var allowHeadersCollection = allowHeadersElement.Collection;

var addElement2 = allowHeadersCollection.CreateNewElement("add");
addElement2.Properties.Item("header").Value = "header1";
allowHeadersCollection.AddElement(addElement2);


var addElement3 = allowHeadersCollection.CreateNewElement("add");
addElement3.Properties.Item("header").Value = "header2";
allowHeadersCollection.AddElement(addElement3);


var allowMethodsCollection = addElement1.ChildElements.Item("allowMethods").Collection;

var addElement4 = allowMethodsCollection.CreateNewElement("add");
addElement4.Properties.Item("method").Value = "DELETE";
allowMethodsCollection.AddElement(addElement4);


var exposeHeadersCollection = addElement1.ChildElements.Item("exposeHeaders").Collection;

var addElement5 = exposeHeadersCollection.CreateNewElement("add");
addElement5.Properties.Item("header").Value = "header1";
exposeHeadersCollection.AddElement(addElement5);


var addElement6 = exposeHeadersCollection.CreateNewElement("add");
addElement6.Properties.Item("header").Value = "header2";
exposeHeadersCollection.AddElement(addElement6);

corsCollection.AddElement(addElement1);


var addElement7 = corsCollection.CreateNewElement("add");
addElement7.Properties.Item("origin").Value = "http://*";
addElement7.Properties.Item("allowed").Value = false;
corsCollection.AddElement(addElement7);


adminManager.CommitChanges();

命令列(AppCmd)

appcmd.exe set config "contentSite" -section:system.webServer/cors /enabled:"True" /failUnlistedOrigins:"True"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='*']"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowHeaders.allowAllRequestedHeaders:"True"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowHeaders.[header='header1']"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowHeaders.[header='header2']"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowMethods.[method='DELETE']"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].exposeHeaders.[header='header1']"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].exposeHeaders.[header='header2']"

appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='http://*',allowed='False']"

PowerShell

Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors" -name "enabled" -value "True"
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors" -name "failUnlistedOrigins" -value "True"

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors" -name "." -value @{origin='*'}

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors" -name "." -value @{origin='https://*.microsoft.com';allowCredentials='True';maxAge=120}
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowHeaders" -name "allowAllRequestedHeaders" -value "True"

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowHeaders" -name "." -value @{header='header1'}

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowHeaders" -name "." -value @{header='header2'}

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowMethods" -name "." -value @{method='DELETE'}

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/exposeHeaders" -name "." -value @{header='header1'}

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/exposeHeaders" -name "." -value @{header='header2'}

Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite'  -filter "system.webServer/cors" -name "." -value @{origin='http://*';allowed='False'}