此代碼仍然生成相同的連接字符串,但此代碼的編寫(xiě)更容易一些。另外,如果在提供連接字符串關(guān)鍵字時(shí)產(chǎn)生了輸入錯(cuò)誤,將會(huì)產(chǎn)生一個(gè)編譯時(shí)錯(cuò)誤。如果正在Microsoft Visual Studio中編寫(xiě)代碼,并且難以記住連接字符串選項(xiàng),那么可以很容易地通過(guò)IntelliSense下拉菜單使用SqlConnectionStringBuilder中的可用選項(xiàng)。使用這些下拉菜單還可以減少輸入字符,防止出現(xiàn)一些輸入錯(cuò)誤,在其他情況下,需要到測(cè)試代碼時(shí)才能發(fā)現(xiàn)這些錯(cuò)誤。
3. 處理復(fù)雜的連接字符串選項(xiàng)值
使用連接字符串生成器的一個(gè)間接好處是:不必再記憶如何分析、轉(zhuǎn)義或分隔連接字符串中的值。假定需要提供一個(gè)包括空格的連接字符串值,是否需要在關(guān)鍵字兩側(cè)加引號(hào)呢?用大括號(hào)?一個(gè)也不用?還是用兩個(gè)?
如果以手動(dòng)方式生成連接字符串,那么這些問(wèn)題不存在簡(jiǎn)單的答案。事實(shí)上,其答案取決于所使用的.NET數(shù)據(jù)提供程序。過(guò)去使用過(guò)ODBC的開(kāi)發(fā)人員可能還會(huì)記得:包含空間的ODBC驅(qū)動(dòng)程序名稱必須用大括號(hào)分隔:"Driver={SQL Server}"。利用連接字符串生成器來(lái)代替手動(dòng)方式生成連接字符串,就不再為這些問(wèn)題而費(fèi)心。
如果正在使用連接字符串生成器,可以將邏輯交給生成器處理。例如,可能希望使用連接字符串選項(xiàng)以連接到服務(wù)器,并在過(guò)程中附加一個(gè)數(shù)據(jù)庫(kù)文件。希望使用的文件可能在路徑中包含空格或其他字符。開(kāi)發(fā)人員不必?fù)?dān)心是否或如何分隔連接字符串中的文件名稱,只需要使用SqlConnectionStringBuilder類來(lái)生成連接字符串,如下所示:
SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
bldr.DataSource = @".\SQLExpress";
bldr.IntegratedSecurity = true;
bldr.AttachDBFilename = @"C:\My Complex Path\AttachMe.mdf";
//將生成的連接字符串輸出到控制臺(tái)窗口
Console.WriteLine("Resulting connection string: {0}", bldr.ConnectionString);
//以SqlConnection使用所生成的連接字符串
SqlConnection cn = new SqlConnection(bldr.ConnectionString);
cn.Open();
讀者可能出于某種原因而希望了解如何正確分隔連接字符串中的文件名稱,那么該代碼段生成以下連接字符串:
Data Source=.\SQLExpress;AttachDbFilename="C:\My Complex Path\AttachMe.mdf";
Integrated Security=True
4. 惡意連接字符串輸入,也稱為“連接字符串注入”
在編寫(xiě)安全代碼時(shí),最重要的規(guī)則之一就是“絕對(duì)不要盲目相信用戶輸入”。參數(shù)化查詢(這一主題將在第4章中詳細(xì)討論)非常有用,其原因有很多,但重要原因之一是它可以防止SQL注入。可以構(gòu)建一個(gè)參數(shù)化查詢,并為參數(shù)指定由用戶提供的取值,而不必?fù)?dān)心用戶輸入是否會(huì)改變查詢的結(jié)構(gòu)。在生成連接字符串時(shí)也存在類似問(wèn)題。
您可能決定對(duì)用戶進(jìn)行提示,使其輸入憑據(jù)——提供一個(gè)文本框以允許用戶輸入用戶名和密碼,然后利用以下代碼,基于連接字符串中的輸入來(lái)構(gòu)造連接字符串。
string strConn;
strConn = @"Data Source=.\SQLExpress;Initial Catalog=Northwind;" +
"User ID=" + txtUserID.Text + ";" +
"Password=" + txtPassword.Text + ";"
Console.WriteLine("Resulting connection string: {0}", strConn);
乍看起來(lái),這似乎是安全而符合邏輯的。現(xiàn)在假定有一位惡意用戶,他希望修改此連接字符串。圖3.1說(shuō)明了一種方式,這種惡意用戶可以采用這種方式來(lái)修改要訪問(wèn)哪個(gè)服務(wù)器。由于沒(méi)有更好的術(shù)語(yǔ),我們將這種方法稱為“連接字符串注入”。

圖3.1 惡意用戶嘗試通過(guò)用戶輸入改變應(yīng)用程序訪問(wèn)的服務(wù)器
利用以上代碼段和如圖3.1所示的輸入,所得到的連接字符串如下所示:
Data Source=.\SQLExpress;Initial Catalog=Northwind;User ID=MyUserID;
Data Source=EvilServerName;Password=MyPassword;
可以看到,Data Source被指明兩次。在使用所得到的連接字符串時(shí),會(huì)選用哪個(gè)值呢?是第一個(gè)(本地計(jì)算機(jī)),還是第二個(gè)(“evil”服務(wù)器)?是否存在手動(dòng)檢查用戶輸入的方式,以檢查諸如此類的可能的連接字符串注入?這些答案對(duì)于其他連接字符串關(guān)鍵字或其他.NET數(shù)據(jù)提供程序是否正確呢?開(kāi)發(fā)人員能夠做些什么呢?
幸運(yùn)的是,連接字符串生成器可以幫助開(kāi)發(fā)人員處理來(lái)自惡意或惡作劇用戶的輸入。
5. 用連接字符串生成器防止連接字符串注入
利用SqlConnectionStringBuilder重新編寫(xiě)前面的代碼段,得到以下代碼:
SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
bldr.DataSource = @".\SQLExpress";
bldr.InitialCatalog = "Northwind";
bldr.UserID = txtUserID.Text;
bldr.Password = txtPassword.Text;
Console.WriteLine("Resulting connection string: {0}", bldr.ConnectionString);
通過(guò)利用具有如圖3.1所示惡意輸入的SqlConnectionStringBuilder(對(duì)于用戶名使用"MyUserID;Data Source=EvilServerName",對(duì)于密碼使用MyPassword),將會(huì)生成以下連接字符串。
Data Source=.\SQLExpress;Initial Catalog=Northwind;
User ID="MyUserID;Data Source=EvilServerName";Password=MyPassword
注意,User ID關(guān)鍵字的值被分隔。這會(huì)導(dǎo)致ADO.NET嘗試以“MyUserID;Data Source=EvilServerName”為User ID來(lái)登錄到SQL Server數(shù)據(jù)庫(kù)。