要分析Xml Web Service的安全性,首先要解決的問題是我們能了解和清楚Soap消息的格式和內容,如果獲得不了SoapMessage,分析如何能構建安全XML web service也就無從下手,即使分析出來,自己也可能模模糊糊,不能定論。下面就分析下如何獲得SoapMessage。
首先介紹一個類-SoapExtension,msdn對這個類的備注為:ASP.NET 允許通過擴展性機制生成與 SOAP 相關的基礎結構。ASP.NET SOAP 擴展結構以一種擴展為中心,該擴展可以在客戶端或服務器上處理消息時在特定階段中檢查或修改消息。ASP.Net SOAP 擴展從 SoapExtension 類派生。GetInitializer 和 Initialize 方法提供其他可用機制,用於初始化 SOAP 擴展以增強性能。ProcessMessage 是大多數 SOAP 擴展的核心,原因是該方法在 SoapMessageStage 中定義的每一個階段都被調用,從而使 SOAP 擴展得以執行所需的該特定 SOAP 擴展的行為。對於需要修改 SOAP 請求或 SOAP 響應的 SOAP 擴展,ChainStream 提供一個機會以接收要通過網絡發送的建議數據。 仔細閱讀這段文字,如果您以前開發過windows程序,那第一個應該想到的是:原來web service的處理機制和Windows窗口程序的消息機制竟然有著一曲同工之妙。下面談談如何利用這個類,來截獲Xml web Service請求和相應的Soap消息,從而看看XML web service的廬山真面目。
首先大家先看看這個類,這個類完成的功能是將XML Web Service通過擴展的方式,將每次的請求和響應的Soap消息通過日志的方式保存到文本文件中。日志記錄的方式也有兩種:
1。針對每個WebMethod產生一個日志文件。
2。針對每個WebService產生一個日志文件
因為一個WebService可能包含一個或者多個WebMethod,所以如果指定兩種方法都支持的話,那第二個日志兩面應該包括第一個日志裡面的內容,而有些情況下,是不需要對每個WebMethod都進行日志記錄的,這時候采用第一種記錄方式,增強了系統的靈活性。
下面是擴展了的SoapExtension
<webServices>
<soapExtensionTypes>
<add type="Jillzhang.TraceExtension,Jillzhang" priority="1" group="High" />
</soapExtensionTypes>
</webServices>
可以記錄SoapMessage的SoapExtension
namespace Jillzhang
{
public class TraceExtension: SoapExtension
{
static readonly string LogRoot = System.Configuration.ConfigurationManager.APPSettings["logRoot"];
Stream oldStream;
Stream newStream;
string filename;
/// <summary>
/// 將請求流和響應流存到內存流中,已被調用
/// </summary>
/// <param name="stream">包含 SOAP 請求或響應的內存緩沖區</param>
/// <returns>它表示此 SOAP 擴展可以修改的新內存緩沖區。</returns>
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
/// <summary>
/// 在XML Web Service第一次運行的時候,一次性的將通過TraceExtensionAttribute傳遞進來的
/// 保存日志信息的文件名初始化
/// </summary>
/// <param name="methodInfo">應用 SOAP 擴展的 XML Web services 方法的特定函數原型</param>
/// <param name="attribute">應用於 XML Web services 方法的 SoapExtensionAttribute</param>
/// <returns>SOAP 擴展將對其進行初始化以用於緩存</returns>
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return ((TraceExtensionAttribute)attribute).Filename;
}
/// <summary>
/// 替代為每個方法配置的保存SoapMessage文件名,而是將整個網絡服務
/// 的SoapMessage都保存到一個日志文件中,這個文件路徑需要在Web Service
/// 的配置文件中web.config指出,如
/// <aPPSettings>
/// <add key="logRoot" value="c:\serviceLog"/>
/// </aPPSettings>
/// </summary>
/// <param name="WebServiceType">網絡服務的類型</param>
/// <returns>用於保存日志記錄的文件路徑</returns>
public override object GetInitializer(Type WebServiceType)
{
//return LogRoot.TrimEnd('\') + "\" + WebServiceType.FullName + ".log";
return LogRoot.TrimEnd('\')+"\"+ WebServiceType.FullName + ".log";
}
//獲得文件名,並將其保存下來
public override void Initialize(object initializer)
{
filename = (string)initializer;
}
/// <summary>
/// 當數據還為Soap格式的時候,將數據寫入日志
/// </summary>
/// <param name="message"></param>
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
WriteOutput(message);
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput(message);
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}
/// <summary>
/// 將SoapMessage寫入到日志文件
/// </summary>
/// <param name="message"></param>
public void WriteOutput(SoapMessage message)
{
newStream.Position = 0;
//創建或追加記錄文件
FileStream fs = new FileStream(filename, FileMode.Append,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
string soapString = (message is SoapServerMessage) ? "Soap響應" : "Soap請求";
w.WriteLine("-----" + soapString + " 在 " + DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒"));
w.Flush();
Copy(newStream, fs);
w.Close();
newStream.Position = 0;
Copy(newStream, oldStream);
}
public void WriteInput(SoapMessage message)
{
Copy(oldStream, newStream);
FileStream fs = new FileStream(filename, FileMode.Append,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
string soapString = (message is SoapServerMessage) ?
"Soap請求" : "Soap響應";
w.WriteLine("-----" + soapString +
" 在 " + DateTime.Now.ToString("yyyy年MM月dd日 HH時mm分ss秒"));
w.Flush();
newStream.Position = 0;
Copy(newStream, fs);
w.Close();
newStream.Position = 0;
}
/// <summary>
/// 拷貝流到流
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
//創建一個用於在WebMethod上使用的SoapExtension屬性
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
private string filename = "c:\log.txt";
private int priority;
/// <summary>
/// 擴展類型
/// </summary>
public override Type ExtensionType
{
get { return typeof(TraceExtension); }
}
/// <summary>
/// 優先級
/// </summary>
public override int Priority
{
get { return priority; }
set { priority = value; }
}
/// <summary>
/// 用於記錄該WebMethod的SoapMessage的文件的絕對路徑
/// 默認為c:\log.txt;
/// </summary>
public string Filename
{
get
{
return filename;
}
set
{
filename = value;
}
}
}
}
結下來,介紹一個如何使用該類:
如果要使讓TraceExtension支持第一種記錄方式,需要作的額外工作為:
只需要在要記錄SoapMessage的WebMethod添加如下的Attribute
[TraceExtension(Filename="d:\data.XML",Priority=0)]
當然路徑,您可以自己設定
前一節的WebMethod就變成了
添加了針對WebMethod日志記錄的WebMethod
public MySoapHeader header = new MySoapHeader();
[WebMethod]
[SoapHeader("header")]
[TraceExtension(Filename="d:\data.XML",Priority=0)]
public string HelloWorld()
{
if (header == null)
{
return "您沒有設置SoapHeader,不能正常訪問此服務!";
}
if (header.UserName != "jillzhang" || header.Pwd != "123456")
{
return "您提供的身份驗證信息有誤,不能正常訪問此服務!";
}
return "Hello World";
}
調用下該WebService,便在d盤產生一個data.XML文件,裡面的內容為:
-----Soap請求 在 2007年05月25日 09時06分29秒
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Header><MySoapHeader xmlns="http://tempuri.org/"><UserName>jillzhang</UserName><Pwd>123456</Pwd></MySoapHeader></soap:Header><soap:Body><HelloWorld XMLns="http://tempuri.org/" /></soap:Body></soap:Envelope>
-----Soap響應 在 2007年05月25日 09時06分29秒
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorldResponse XMLns="http://tempuri.org/"><HelloWorldResult>Hello World</HelloWorldResult></HelloWorldResponse></soap:Body></soap:Envelope>
如何采用第二種方法,讓WebService的每個WebMethod都能記錄日志
需要在Web.config中作如下的設定
首先添加如下節點
<webServices>
<soapExtensionTypes>
<add type="Jillzhang.TraceExtension,Jillzhang" priority="1" group="High" />
</soapExtensionTypes>
</webServices>
然後通過配置設定日志文件保留的路徑:
<aPPSettings>
<add key="logRoot" value="d:"/>
</aPPSettings>
找到日志文件,裡面也赫然有著SoapMessage的真面目。通過以上方法,大家可以清晰地分析到SoapMessage的具體格式和內容,知道了這個以後,對付Web Service,您就可以隨心應手,隨心所欲,如果你願意,你甚至也可以“強奸"一次webservice,哈哈!