XML 安全: 使用 SAML 確保可移植的信任

級別: 初級

Manish Verma
首席架構師, Second Foundation
2004 年 4 月

長期以來,人們認識到需要提供一種機制在不同的協作域之間傳遞關於實體的信息,同時域又不失去對這些信息的所有權,安全性斷言標記語言或者 SAML 滿足了這種要求。交換的信息可以是關於主題或者驗證信息的斷言。這種方式也稱爲單點登錄。

在 XML 安全系列文章的 上一篇中,我談及了與安全有關的兩個很重要的主題:

  • 易於管理。
  • 可移植的信任。

在那篇文章中,我討論了使用 XKMS 實現安全基礎設施的易管理性。本文中將探討第二個主題,可移植的信任。安全斷言標記語言或者 SAML 是一種保證可移植信任的機制。我將從 SAML 背後的目標談起,然後介紹 SAML 的體系結構,最後解釋 SAML 的概念。

信任不可移植的問題
您經常會遇到需要提供個人信息的 Web 站點,原因基本上是驗證或者通過個人首選項定製站點。最終,您會發現在許多不同的地方有多個帳戶。如果每個站點都爲此設立自己的用戶資料庫,結果就會造成:

  • 對所有這些單獨的站點都要考慮您的信息的安全。
  • 多個站點不能協同爲您提供所感興趣的更精細的服務。

爲了進一步說明上述第二點,我提供一個很典型的例子:旅遊預約。其中包括機票、旅館和出租車的預定。您可能希望向某家航空公司、某個旅館以及某個汽車出租商預訂,但是不存在一種機制使您能夠在三個獨立的站點之間無縫移植——可能需要提交證明進行三次驗證(假設這三家您都有帳戶)。

通行證適用於什麼地方?
Microsoft Passport 試圖通過一種非常簡單的、集中的方式解決這一問題。Microsoft 通行證服務包括一個大型的中心資料庫,存儲所有主體及其屬性。使用這個中心資料庫中的信息,各個參與的站點可以允許主體訪問信息或者授權執行特定的動作。唯一的問題是——可能很多組織都有同感——他們失去了對主體數據的所有權。SAML 通過聯合識別、驗證和授權的形式克服了這一問題。驗證和授權的聯合制允許企業建立自己所有和控制的數據,並能夠以結構化的、受控的方式與協作企業共享這些數據。

SAML 被設計成解決這一問題,它允許只有少數經過選擇的團體保留您的信息,並且如果需要,在得到您的明確批准之後這些團體可以與其他有關的團體共享這些信息。這意味着,您的信息安全的掌握在您所信任的團體手中,並且可以訪問一些供應商通過組織多種低層次服務所提供的高級服務。

本文將說明 SAML 如何實現這一目標。

SAML 的目標
SAML 的主要目標包括:

  • 建立一種獨立於協議和平臺的驗證和授權交換機制(也稱爲單點登錄,或者SSO)。
  • 應該獨立部署環境,能夠用於集中式的、分散式的以及聯合式的部署場景。
  • SAML 框架應該是基於 XML 的。

SAML 的體系結構
SAML 是一種控制對已驗證主體的資源進行訪問的機制。對資源的訪問基於特定的策略管理。這種機制需要兩種關鍵性的活動:

  • 基於策略的訪問控制決策。
  • 強制實施這些決策。

SAML 提供了兩種角色處理這些活動:策略決策點(Policy Decision Points,PDPs)和策略實施點(Policy Enforcement Points,PEPs)。

場景:主體希望從目標 Web 站點訪問一些受保護的內容。主體前往能夠識別或者已經驗證該主體的源 Web 站點。用戶從源站點出發嘗試訪問目標 Web 站點上的受保護內容,步驟如下:

  1. 主體向源站點驗證並請求到目標站點受保護資源的鏈接。
  2. 源站點使用驗證標誌重定向主體。
  3. 主體使用該標誌向目標站點請求受保護的資源。
  4. 目標站點 PEP 檢查該 PDP 的權限。
  5. PDP 可能內部請求源站點使用該標誌進行 SAML 驗證斷言。
  6. 源站點根據標誌向目標站點提供 SAML 驗證斷言。
  7. 目標站點向主體提供受保護的資源。

總之,在源站點驗證的主體從 SAML 權威獲得一個標誌並將其提供給目標站點。目標站點使用該標誌從源站點請求所需要的信息而不需要明確地從主體獲取。

SAML 綜述
SAML 規範由以下部分組成:

  • 斷言與協議:該規範處理定義 XML 編碼的斷言的語法和語義,以及請求和響應協議。
  • 綁定與配置文件:該規範處理 SAML 請求/響應消息到底層通信協議如 SOAP 和 SMTP 的映射。控制在底層通信協議中嵌入和提取 SAML 信息的一組規則稱爲一個配置文件。
  • 一致性規範:不同的 SAML 實現可能只實現這些規範的一部分。一致性規範設置了一種基本標準,SAML 規範的實現在能夠稱爲一致性實現之前必須滿足這一標準。這樣有助於提高互操作性和兼容性。
  • 安全和保密的問題:該規範涉及 SAML 體系結構中的安全風險,具體而言就是 SAML 如何應對這些風險以及無法解決的風險。

我將採用問答的形式介紹和 SAML 有關的概念。我將逐個回答以下問題以澄清這些基本概念:

什麼是斷言?
斷言提供主體所執行的驗證、主體屬性、是否允許主體訪問特定資源的授權決策等信息。

一組斷言組成一個主體的配置文件。配置文件中的斷言可能來自不同的組織。

斷言有三種類型:

  • 驗證:驗證斷言處理主體在特定時刻、特定機制下的身份驗證。
  • 屬性:屬性斷言提供聯繫特定屬性與給定主體的一種機制。
  • 授權決策:授權決策斷言管理給定主體訪問資源的權限。

誰生產和消費斷言?
SAML 權威生產斷言。SAML 權威可進一步劃分爲驗證權威、屬性權威或者 PDP。斷言的消費者是客戶或者 SAML 權威本身。

如何請求斷言,如何發出響應?基本上而言,請求和響應協議是什麼?
SAML 請求響應協議爲發送斷言請求和獲取響應定義了一種標準消息格式。SAML 請求協議定義的消息格式採用下面的請求類型:

  • SubjectQuery允許使用模式定義新的查詢類型,指定一個 SAML 主體。
  • AuthenticationQuery請求一個主體的驗證信息。返回驗證斷言作爲響應。
  • AttributeQuery請求主體的屬性信息。響應中包括請求者擁有權限的那些屬性的屬性斷言。
  • AuthorizationDecisionQuery進行授權決策。根據請求者提交的證據,該查詢決定是否授權該請求者訪問受保護的資源。
  • AssertionIDReference根據唯一標識符檢索特定的斷言。
  • AssertionArtifact根據代表斷言的助診文件檢索一個斷言。

響應消息格式分別對應請求的類型。

SAML 請求響應協議可以綁定到哪些底層的通信傳輸協議?
SAML 定義了 SAML 請求和響應消息在標準通信傳輸協議上的映射。目前只定義了一種綁定,SOAP 上的 SAML。SAML SOAP 綁定描述了 SAML 請求和響應消息交換如何映射到 SOAP 消息交換上。SAML 規範規定任何遵循 SAML 規範的實現必須實現基於 HTTP 的 SOAP 上的 SAML。實現也可以選擇實現基於其他傳輸協議系如 SMTP 或 FTP 的 SOAP 上的 SAML。

如何向 SOAP 這樣的底層通信傳輸協議中插入 SAML 斷言?
SAML 規範定義了一組稱爲配置文件的規則,描述了實現應該如何在底層協議消息中插入、提取和集成斷言。比如,SAML 的 SOAP 配置文件描述瞭如何將 SAML 斷言添加到 SOAP 消息、SOAP 頭會受到 SAML 斷言的什麼影響,以及如何在 SOAP 消息中處理 SAML 錯誤。

SAML 規範中定義了兩個配置文件:

  • 瀏覽器發送配置文件或推式配置文件: SAML 斷言在 HTML 表單中上傳到瀏覽器,當用戶提交表單時作爲 HTTP POST 有效負載傳遞給目標站點。這種情況下,源站點從目標 Web 站點請求一個標誌,後者返回一個授權決策標誌。使用這個標誌將主體重定向到目標 Web 站點。
  • 瀏覽器助診文件(artifact)配置文件或者拉式配置文件:一個 SAML 助診文件作爲 URL 查詢字符串的一部分帶給目標站點,在返回給源站點時明確地引用一個斷言。對目標站點的一些保護內容感興趣的主體從源站點發送一個標誌(SAML 助診文件),目標站點使用這個標誌取得驗證/授權信息並決定是否允許訪問。

什麼是一致性規範,爲何需要它?
一致性規範有助於客觀地評價 SAML 實現或應用程序對 SAML 規範的一致程度。以下原因使一致性規範很有必要:

  • 確保對一致性和一致性需求有共同的理解。
  • 促進驗證和授權信息交換的互操作性。
  • 促進一致性測試開發中的統一性。

如果只實現了標準的子集,也能說該實現是兼容的麼?
是的。對於遵循整個規範或者規範子集的 SAML 實現來說,可以說是兼容的。

定義一致性的粒度級別是什麼?
SAML 一致性根據應用程序或實現所支持的 SAML 綁定和配置文件來定義。對於支持的每種綁定和配置文件,必須從以下幾個方面規定一致性:

  • 應用程序或實現是作爲 SAML 消息的生產者、消費者還是兼具兩種作用。
  • 應用程序或實現支持哪些斷言和陳述。

基於 SAML 的系統的安全風險是什麼?
SAML 允許就驗證和授權進行陳述,但是沒有規定如何進行驗證和建立授權。SAML 陳述的消費者必須在信任 SAML 陳述之前確信底層的基礎設施。底層的基礎設施也必須包括作爲客戶請求和服務器響應來回傳遞的有效負載的安全性和機密性。

既然 SAML 是一種多方驗證和授權系統,一個 SAML 事務中的合法參與者就有可能是用對其他事務有威脅的信息。

和 SAML 斷言有關的風險:斷言一旦發出就不在發出者的控制之內了。比如,消費者可能在未定的時期內持續使用斷言,或者選擇與最初的發出者不知道的第三方共享這些信息。

與 SAML 協議有關的風險:通過要求較低層次上的客戶驗證、客戶發出的請求需要簽名,或者限制 SAML 請求只能發給有限的已知方,可以防止拒絕服務攻擊(DOS)。

與 SAML 協議綁定(目前只有 SOAP 綁定)有關的風險:

  • 竊聽
  • 重放
  • 消息插入
  • 消息刪除
  • 消息篡改
  • 中間人

與基於 TLS/SSL 的 HTTP 上的 SOAP 有關的風險: 這種方法只針對單跳提供安全性、機密性和授權。對於多次中轉,HTTP 和 TLS/SSL 沒有提供足夠的安全性。

和 SAML 配置文件(瀏覽器/助診文件配置文件和瀏覽器/POST 配置文件)有關的風險:

  • 竊聽
  • 偷竊用戶驗證信息
  • 偷竊持票人的標誌
  • 重放
  • 消息插入
  • 消息刪除
  • 消息篡改
  • 中間人

SAML 過程
清單 1 示範瞭如何使用 SAML 檢索關於一個主體的斷言。這段代碼來自 Verisign 所提供的 SAML 示例實現。(請參閱 參考資料下載源代碼和相關文件。)

清單 1. 檢索屬性斷言

Public class SAMLAssertions {
// SAML service provider
// Change it to the SAML service provider url that you are
// trying to connect to
static final String SERVICE_URL =
      "https://pilotpts.verisign.com/vspts/SamlResponder";
// Email of the subject that uniquely identifies it
static final String SUBJECT_EMAIL = "[email protected]";
// Subject password to authenticate the user	
static final String SUBJECT_PASSWORD = "password";
// The keystore to be used for getting the private key and 
// the public key
static final String KEYSTORE_FILE = "saml_sample_keystore";
// Keystore password
static final String STORE_PASS = "changeit";
static final String SIGNER_ALIAS = "pilot_saml_signer";
//	VeriSign attribute naming constants
static final String V_NAME_QUALIFIER = "verisign.com/ams";
static final String V_ATTR_NAMESPACE = 
      "verisign.com/ams/namespace/common";
static final String V_ATTR_EMAIL = 
      "//verisign.com/core/attr/email";
static final String V_ATTR_FIRSTNAME = 
      "//verisign.com/core/attr/first_name";
public static void main(String arg[]) {
NameIdentifier nameId =	
      new NameIdentifier(SUBJECT_EMAIL, V_NAME_QUALIFIER, "");
String[] confMethods = { Identifiers.AUTHN_METHOD_PASSWORD };	
SubjectConfirmation sconf = 
      new SubjectConfirmation(confMethods, SUBJECT_PASSWORD);
Subject subject = new Subject(nameId, sconf);
AttributeDesignator[] reqAttrs = null;
AttributeQuery query = new AttributeQuery(subject, reqAttrs);
AttributeStatementProvider provider = null;
try {	
provider = getAttributeStatementProvider();
} catch (Exception e) {	
// XXX Auto-generated catch block
e.printStackTrace();
}
AttributeStatement[] statements = null;
try {	
statements = provider.processAttributeQuery(query);
} catch (Exception e1) {
// XXX Auto-generated catch block
e1.printStackTrace();
}
if (statements.length == 0) {
System.out.println("No statements returned");
}
for (int i = 0; i < statements.length; i += 1) {	
AttributeStatement statement = statements[i];
Authenticity authenticity = statement.getAuthenticity();
System.out.println(	"Statement "+ (i + 1)+ " authenticity: "
      + authenticity.isAuthentic());
// Print attribute statement	
System.out.println("--- Attribute Statement ---");
System.out.println(  "  Subject: "
      + statement.getSubject().getNameIdentifier().getValue())
// Print attribute values
Attribute[] attrs = statement.getAttributes();
for (int k = 0; k < attrs.length; k += 1) {
Attribute attr = attrs[k];
System.out.println(  "Namespace: " + attr.getAttributeNamespace());
System.out.println(" Name: " + attr.getAttributeName());	
Object[] values = attr.getAttributeValues();
for (int m = 0; m < values.length; m += 1) {	
System.out.println( "	Value: " + values[m].toString());
}
}
}
}
private static AttributeStatementProvider
getAttributeStatementProvider()	throws Exception {
// Read the keystore and get the signing cert/key.
InputStream fileInput = new FileInputStream(KEYSTORE_FILE);
KeyStore keystore = 
      KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(fileInput,STORE_PASS.toCharArray());
PrivateKey key = 
      (PrivateKey) keystore.getKey(
            SIGNER_ALIAS,STORE_PASS.toCharArray());
if (key == null) {
throw new Exception(SIGNER_ALIAS + 
      " key not found in keystore " + KEYSTORE_FILE);
}
Certificate[] certArray = 
      keystore.getCertificateChain(SIGNER_ALIAS);
if (certArray == null) {
throw new Exception(SIGNER_ALIAS + 
      " cert not found in keystore " + KEYSTORE_FILE);
}
X509Certificate[] certs = new X509Certificate[certArray.length];
System.arraycopy(certArray, 0, certs, 0, certs.length);
// Create SOAP assertion provider factory with signing information
SOAPAssertionProviderFactory factory =
      new SOAPAssertionProviderFactory(new URL(SERVICE_URL));
factory.setSigningKey(new RSASigningKey(key));
factory.setVerifyingKey(new RSAVerifyingKey(certs));
return factory.newAttributeStatementProvider();	}
}

清單 1中在 SOAP 協議上查詢主體屬性的步驟描述如下:

  • 第 1 步: 建立關心其屬性的主體。這是通過創建 SubjectIdentifier 並定義 SubjectConfirmation 方法實現的。
  • 第 2 步:使用主體的 e-mail ID 作爲主體的唯一標識符。
  • 第 3 步:指定驗證用戶所使用的方法。清單 1 中使用的是口令驗證。
  • 第 4 步:創建 AttributeDesignator 對象,該對象指定了希望檢索的屬性。在清單 1 中,Verisign 的 SAML 實現把這個數組保留爲空值,檢索與查詢對象相關的所有屬性值,這些屬性在 AttributeDesignator 數組中指定。
  • 第 5 步:使用主體和 AttributeDesignator 數組創建 AttributeQuery 對象。
  • 第 6 步:使用密鑰存儲和示例 SAML 服務所運行的服務 URL 創建 AttributeStatementProvider
  • 第 7 步:使用 SOAPAssertionProviderFactory 創建 AttributeStatementProvider 。在創建 AttributeStatementProvider 之前必須向 SOAPAssertionProviderFactory 提供簽名密鑰和檢驗密鑰。簽名密鑰是主體的私鑰,檢驗密鑰帶有附加的證書。
  • 第 8 步:對第 6 步創建的 AttributeStatementProvider 執行第 5 步建立的查詢。獲得一個 AttributeStatement 對象數組。該數組包含提供程序作爲查詢結果發送的所有屬性的值。迭代陳述中的所有屬性以便獲得屬性值。

結束語
本文考察了 SAML 的目標、體系結構和基本概念。SAML 完成了幾件事情,澄清了安全可移植性領域的一些混亂:它定義了表示需要交換的信息的標準機制,定義了交換這類信息的標準。如果您迫切需要提供安全可移植性,您就不能不考慮基於這一計劃的安全可移植性產品並採用 SAML 作爲標準。

參考資料

 

關於作者
Manish Verma 是 Second Foundation 的首席架構師,Second Foundation 一家全球 IT 服務公司。Manish 在軟件開發生命週期的各個方面有 11 年的經驗,曾經設計過運行在完全不同系統上的客戶組織的集成策略。Manish 在集成方面的專長建立在理解各種技術的基礎上,包括各種不同的遺留系統、.NET、Java 技術和最新的中間件技術。在進入 Second Foundation 之前,Manish 先後在 Quark Inc.、Hewlett Packard、Endura Software 和 The Williams Company 做過軟件架構師和技術主管。可以通過 [email protected] 與 Manish 聯繫。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章