長期以來,人們認(rèn)識到需要提供一種機制在不同的協(xié)作域之間傳遞關(guān)于實體的信息,同時域又不失去對這些信息的所有權(quán),安全性斷言標(biāo)記語言或者SAML滿足了這種要求。交換的信息可以是關(guān)于主題或者驗證信息的斷言。這種方式也稱為單點登錄。
  在XML安全系列文章的上一篇中,我談及了與安全有關(guān)的兩個很重要的主題: 
  ·易于管理。 
  ·可移植的信任。 
  在那篇文章中,我討論了使用XKMS實現(xiàn)安全基礎(chǔ)設(shè)施的易管理性。本文中將探討第二個主題,可移植的信任。安全斷言標(biāo)記語言或者SAML是一種保證可移植信任的機制。我將從SAML背后的目標(biāo)談起,然后介紹SAML的體系結(jié)構(gòu),最后解釋SAML的概念。
  信任不可移植的問題
  您經(jīng)常會遇到需要提供個人信息的Web站點,原因基本上是驗證或者通過個人首選項定制站點。最終,您會發(fā)現(xiàn)在許多不同的地方有多個帳戶。如果每個站點都為此設(shè)立自己的用戶資料庫,結(jié)果就會造成:
  ·對所有這些單獨的站點都要考慮您的信息的安全。 
  ·多個站點不能協(xié)同為您提供所感興趣的更精細(xì)的服務(wù)。 
  為了進(jìn)一步說明上述第二點,我提供一個很典型的例子:旅游預(yù)約。其中包括機票、旅館和出租車的預(yù)定。您可能希望向某家航空公司、某個旅館以及某個汽車出租商預(yù)訂,但是不存在一種機制使您能夠在三個獨立的站點之間無縫移植——可能需要提交證明進(jìn)行三次驗證(假設(shè)這三家您都有帳戶)。
  SAML被設(shè)計成解決這一問題,它允許只有少數(shù)經(jīng)過選擇的團體保留您的信息,并且如果需要,在得到您的明確批準(zhǔn)之后這些團體可以與其他有關(guān)的團體共享這些信息。這意味著,您的信息安全的掌握在您所信任的團體手中,并且可以訪問一些供應(yīng)商通過組織多種低層次服務(wù)所提供的高級服務(wù)。
  本文將說明SAML如何實現(xiàn)這一目標(biāo)。
  SAML的目標(biāo)
  SAML的主要目標(biāo)包括:
  ·建立一種獨立于協(xié)議和平臺的驗證和授權(quán)交換機制(也稱為單點登錄,或者SSO)。 
  ·應(yīng)該獨立部署環(huán)境,能夠用于集中式的、分散式的以及聯(lián)合式的部署場景。 
  ·SAML框架應(yīng)該是基于XML的。 
  SAML的體系結(jié)構(gòu)
  SAML是一種控制對已驗證主體的資源進(jìn)行訪問的機制。對資源的訪問基于特定的策略管理。這種機制需要兩種關(guān)鍵性的活動:
  ·基于策略的訪問控制決策。 
  ·強制實施這些決策。 
  SAML提供了兩種角色處理這些活動:策略決策點(Policy Decision Points,PDPs)和策略實施點(Policy Enforcement Points,PEPs)。
 
  場景:主體希望從目標(biāo)Web站點訪問一些受保護(hù)的內(nèi)容。主體前往能夠識別或者已經(jīng)驗證該主體的源Web站點。用戶從源站點出發(fā)嘗試訪問目標(biāo)Web站點上的受保護(hù)內(nèi)容,步驟如下: 
  ·主體向源站點驗證并請求到目標(biāo)站點受保護(hù)資源的鏈接。 
  ·源站點使用驗證標(biāo)志重定向主體。 
  ·主體使用該標(biāo)志向目標(biāo)站點請求受保護(hù)的資源。 
  ·目標(biāo)站點PEP檢查該PDP的權(quán)限。 
  ·PDP可能內(nèi)部請求源站點使用該標(biāo)志進(jìn)行SAML驗證斷言。 
  ·源站點根據(jù)標(biāo)志向目標(biāo)站點提供SAML驗證斷言。 
  ·目標(biāo)站點向主體提供受保護(hù)的資源。 
  總之,在源站點驗證的主體從SAML權(quán)威獲得一個標(biāo)志并將其提供給目標(biāo)站點。目標(biāo)站點使用該標(biāo)志從源站點請求所需要的信息而不需要明確地從主體獲取。
  SAML綜述
  SAML規(guī)范由以下部分組成:
  ·斷言與協(xié)議:該規(guī)范處理定義XML編碼的斷言的語法和語義,以及請求和響應(yīng)協(xié)議。 
  ·綁定與配置文件:該規(guī)范處理SAML請求/響應(yīng)消息到底層通信協(xié)議如SOAP和SMTP的映射。控制在底層通信協(xié)議中嵌入和提取SAML信息的一組規(guī)則稱為一個配置文件。 
  ·一致性規(guī)范:不同的SAML實現(xiàn)可能只實現(xiàn)這些規(guī)范的一部分。一致性規(guī)范設(shè)置了一種基本標(biāo)準(zhǔn),SAML規(guī)范的實現(xiàn)在能夠稱為一致性實現(xiàn)之前必須滿足這一標(biāo)準(zhǔn)。這樣有助于提高互操作性和兼容性。 
  ·安全和保密的問題:該規(guī)范涉及SAML體系結(jié)構(gòu)中的安全風(fēng)險,具體而言就是SAML如何應(yīng)對這些風(fēng)險以及無法解決的風(fēng)險。 
  我將采用問答的形式介紹和SAML有關(guān)的概念。我將逐個回答以下問題以澄清這些基本概念:
  ·什么是斷言? 
  ·誰生產(chǎn)和消費斷言? 
  ·如何請求斷言,如何發(fā)送響應(yīng)?基本上而言,請求和響應(yīng)協(xié)議是什么? 
  ·SAML請求響應(yīng)協(xié)議可以綁定到哪些底層的通信傳輸 協(xié)議? 
  ·如何向SOAP這樣的底層通信傳輸協(xié)議中插入SAML斷言? 
  ·什么是一致性規(guī)范,為何需要它? 
  ·如果只實現(xiàn)了標(biāo)準(zhǔn)的子集,也能說該實現(xiàn)是兼容的么? 
  ·定義一致性的粒度級別是什么? 
  ·基于SAML的系統(tǒng)的安全風(fēng)險是什么? 
  什么是斷言?
  斷言提供主體所執(zhí)行的驗證、主體屬性、是否允許主體訪問特定資源的授權(quán)決策等信息。
  一組斷言組成一個主體的配置文件。配置文件中的斷言可能來自不同的組織。
  斷言有三種類型:
  ·驗證:驗證斷言處理主體在特定時刻、特定機制下的身份驗證。 
  ·屬性:屬性斷言提供聯(lián)系特定屬性與給定主體的一種機制。 
  ·授權(quán)決策:授權(quán)決策斷言管理給定主體訪問資源的權(quán)限。 
  誰生產(chǎn)和消費斷言?
  SAML權(quán)威生產(chǎn)斷言。SAML權(quán)威可進(jìn)一步劃分為驗證權(quán)威、屬性權(quán)威或者PDP。斷言的消費者是客戶或者SAML權(quán)威本身。
  如何請求斷言,如何發(fā)出響應(yīng)?基本上而言,請求和響應(yīng)協(xié)議是什么?
  SAML請求響應(yīng)協(xié)議為發(fā)送斷言請求和獲取響應(yīng)定義了一種標(biāo)準(zhǔn)消息格式。SAML請求協(xié)議定義的消息格式采用下面的請求類型:
  ·SubjectQuery:允許使用模式定義新的查詢類型,指定一個SAML主體。 
  ·AuthenticationQuery:請求一個主體的驗證信息。返回驗證斷言作為響應(yīng)。 
  ·AttributeQuery:請求主體的屬性信息。響應(yīng)中包括請求者擁有權(quán)限的那些屬性的屬性斷言。 
  ·AuthorizationDecisionQuery:進(jìn)行授權(quán)決策。根據(jù)請求者提交的證據(jù),該查詢決定是否授權(quán)該請求者訪問受保護(hù)的資源。 
  ·AssertionIDReference:根據(jù)唯一標(biāo)識符檢索特定的斷言。 
  ·AssertionArtifact:根據(jù)代表斷言的助診文件檢索一個斷言。 
  響應(yīng)消息格式分別對應(yīng)請求的類型。
  SAML請求響應(yīng)協(xié)議可以綁定到哪些底層的通信傳輸協(xié)議?
  SAML定義了SAML請求和響應(yīng)消息在標(biāo)準(zhǔn)通信傳輸協(xié)議上的映射。目前只定義了一種綁定,SOAP上的SAML。SAML SOAP綁定描述了SAML請求和響應(yīng)消息交換如何映射到SOAP消息交換上。SAML規(guī)范規(guī)定任何遵循SAML規(guī)范的實現(xiàn)必須實現(xiàn)基于HTTP的SOAP上的SAML。實現(xiàn)也可以選擇實現(xiàn)基于其他傳輸協(xié)議系如SMTP或FTP的SOAP上的SAML。
  如何向SOAP這樣的底層通信傳輸協(xié)議中插入SAML斷言?
  SAML規(guī)范定義了一組稱為配置文件的規(guī)則,描述了實現(xiàn)應(yīng)該如何在底層協(xié)議消息中插入、提取和集成斷言。比如,SAML的SOAP配置文件描述了如何將SAML斷言添加到SOAP消息、SOAP頭會受到SAML斷言的什么影響,以及如何在SOAP消息中處理SAML錯誤。
  SAML規(guī)范中定義了兩個配置文件:
  ·瀏覽器發(fā)送配置文件或推式配置文件:SAML斷言在HTML表單中上傳到瀏覽器,當(dāng)用戶提交表單時作為HTTP POST有效負(fù)載傳遞給目標(biāo)站點。這種情況下,源站點從目標(biāo)Web站點請求一個標(biāo)志,后者返回一個授權(quán)決策標(biāo)志。使用這個標(biāo)志將主體重定向到目標(biāo)Web站點。 
  ·瀏覽器助診文件(artifact)配置文件或者拉式配置文件:一個SAML助診文件作為URL查詢字符串的一部分帶給目標(biāo)站點,在返回給源站點時明確地引用一個斷言。對目標(biāo)站點的一些保護(hù)內(nèi)容感興趣的主體從源站點發(fā)送一個標(biāo)志(SAML助診文件),目標(biāo)站點使用這個標(biāo)志取得驗證/授權(quán)信息并決定是否允許訪問。 
  什么是一致性規(guī)范,為何需要它?
  一致性規(guī)范有助于客觀地評價SAML實現(xiàn)或應(yīng)用程序?qū)AML規(guī)范的一致程度。以下原因使一致性規(guī)范很有必要:
  ·確保對一致性和一致性需求有共同的理解。 
  ·促進(jìn)驗證和授權(quán)信息交換的互操作性。 
  ·促進(jìn)一致性測試開發(fā)中的統(tǒng)一性。 
  如果只實現(xiàn)了標(biāo)準(zhǔn)的子集,也能說該實現(xiàn)是兼容的么?
  是的。對于遵循整個規(guī)范或者規(guī)范子集的SAML實現(xiàn)來說,可以說是兼容的。
  定義一致性的粒度級別是什么?
  SAML一致性根據(jù)應(yīng)用程序或?qū)崿F(xiàn)所支持的SAML綁定和配置文件來定義。對于支持的每種綁定和配置文件,必須從以下幾個方面規(guī)定一致性:
  ·應(yīng)用程序或?qū)崿F(xiàn)是作為SAML消息的生產(chǎn)者、消費者還是兼具兩種作用。 
  ·應(yīng)用程序或?qū)崿F(xiàn)支持哪些斷言和陳述。 
  基于SAML的系統(tǒng)的安全風(fēng)險是什么?
  SAML允許就驗證和授權(quán)進(jìn)行陳述,但是沒有規(guī)定如何進(jìn)行驗證和建立授權(quán)。SAML陳述的消費者必須在信任SAML陳述之前確信底層的基礎(chǔ)設(shè)施。底層的基礎(chǔ)設(shè)施也必須包括作為客戶請求和服務(wù)器響應(yīng)來回傳遞的有效負(fù)載的安全性和機密性。
  既然SAML是一種多方驗證和授權(quán)系統(tǒng),一個SAML事務(wù)中的合法參與者就有可能是用對其他事務(wù)有威脅的信息。
  和SAML斷言有關(guān)的風(fēng)險:斷言一旦發(fā)出就不在發(fā)出者的控制之內(nèi)了。比如,消費者可能在未定的時期內(nèi)持續(xù)使用斷言,或者選擇與最初的發(fā)出者不知道的第三方共享這些信息。 
  與SAML協(xié)議有關(guān)的風(fēng)險:通過要求較低層次上的客戶驗證、客戶發(fā)出的請求需要簽名,或者限制SAML請求只能發(fā)給有限的已知方,可以防止拒絕服務(wù)攻擊(DOS)。 
  與SAML協(xié)議綁定(目前只有SOAP綁定)有關(guān)的風(fēng)險: 
  ·竊聽 
  ·重放 
  ·消息插入 
  ·消息刪除 
  ·消息篡改 
  ·中間人 
  與基于TLS/SSL的HTTP上的SOAP有關(guān)的風(fēng)險:這種方法只針對單跳提供安全性、機密性和授權(quán)。對于多次中轉(zhuǎn),HTTP和TLS/SSL沒有提供足夠的安全性。 
  和SAML配置文件(瀏覽器/助診文件配置文件和瀏覽器/POST配置文件)有關(guān)的風(fēng)險: 
  ·竊聽 
  ·偷竊用戶驗證信息 
  ·偷竊持票人的標(biāo)志 
  ·重放 
  ·消息插入 
  ·消息刪除 
  ·消息篡改 
  ·中間人 
  SAML過程
  清單1示范了如何使用SAML檢索關(guān)于一個主體的斷言。這段代碼來自Verisign所提供的SAML示例實現(xiàn)。
  清單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./vspts/SamlResponder";
// Email of the subject that uniquely identifies it
static final String SUBJECT_EMAIL = "jdoe@tpms.";
// 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 = "/ams";
static final String V_ATTR_NAMESPACE = 
      "/ams/namespace/common";
static final String V_ATTR_EMAIL = 
      "http:///core/attr/email";
static final String V_ATTR_FIRSTNAME = 
      "http:///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協(xié)議上查詢主體屬性的步驟描述如下: 
  ·第1步:建立關(guān)心其屬性的主體。這是通過創(chuàng)建SubjectIdentifier并定義SubjectConfirmation方法實現(xiàn)的。 
  ·第2步:使用主體的e-mail ID作為主體的唯一標(biāo)識符。 
  ·第3步:指定驗證用戶所使用的方法。清單1中使用的是口令驗證。 
  ·第4步:創(chuàng)建AttributeDesignator對象,該對象指定了希望檢索的屬性。在清單1中,Verisign的SAML實現(xiàn)把這個數(shù)組保留為空值,檢索與查詢對象相關(guān)的所有屬性值,這些屬性在AttributeDesignator數(shù)組中指定。 
  ·第5步:使用主體和AttributeDesignator數(shù)組創(chuàng)建AttributeQuery對象。 
  ·第6步:使用密鑰存儲和示例SAML服務(wù)所運行的服務(wù)URL創(chuàng)建AttributeStatementProvider 。 
  ·第7步:使用SOAPAssertionProviderFactory創(chuàng)建AttributeStatementProvider。在創(chuàng)建AttributeStatementProvider之前必須向SOAPAssertionProviderFactory提供簽名密鑰和檢驗密鑰。簽名密鑰是主體的私鑰,檢驗密鑰帶有附加的證書。 
  ·第8步:對第6步創(chuàng)建的AttributeStatementProvider執(zhí)行第5步建立的查詢。獲得一個AttributeStatement對象數(shù)組。該數(shù)組包含提供程序作為查詢結(jié)果發(fā)送的所有屬性的值。迭代陳述中的所有屬性以便獲得屬性值。 
  結(jié)束語
  本文考察了SAML的目標(biāo)、體系結(jié)構(gòu)和基本概念。SAML完成了幾件事情,澄清了安全可移植性領(lǐng)域的一些混亂:它定義了表示需要交換的信息的標(biāo)準(zhǔn)機制,定義了交換這類信息的標(biāo)準(zhǔn)。如果您迫切需要提供安全可移植性,您就不能不考慮基于這一計劃的安全可移植性產(chǎn)品并采用SAML作為標(biāo)準(zhǔn)。
  關(guān)于作者
  Manish Verma是Second Foundation的首席架構(gòu)師,Second Foundation一家全球IT服務(wù)公司。Manish在軟件開發(fā)生命周期的各個方面有11年的經(jīng)驗,曾經(jīng)設(shè)計過運行在完全不同系統(tǒng)上的客戶組織的集成策略。Manish在集成方面的專長建立在理解各種技術(shù)的基礎(chǔ)上,包括各種不同的遺留系統(tǒng)、.NET、Java技術(shù)和最新的中間件技術(shù)。在進(jìn)入Second Foundation之前,Manish先后在Quark Inc.、Hewlett Packard、Endura Software和The Williams Company做過軟件架構(gòu)師和技術(shù)主管??梢酝ㄟ^mverma@secf.com與Manish聯(lián)系。