SQL 注入攻擊

oschina發表於2013-09-16

  一個客戶對我們請求說,請我們來檢查一下他的內部網路,這個網路被公司的職員以及客戶們來使用。這是一個較大的安全評估的一部分,而且,雖然我們以前從沒有真正的使用過SQL隱碼攻擊來破解一個網路,但是我們對於其一般的概念相當的熟悉。在此次"戰鬥“中,我們是完全成功的,而且想要通過把這個過程的每一個步驟重新記錄下來,並作為一個”生動的例子“。

  ”SQL隱碼攻擊“是特定的一種未被確認或未明確身份的使用者輸入漏洞的一個子集(”緩衝溢位“是一個不同的子集),而這個想法的目標是,讓應用程式確信從而去執行SQL程式碼,而這些程式碼並不在其目的之內。如果一個應用程式是在本地通過即時的方式來建立一個SQL字串,結果很直接,會造成一些真正的出人意料的結果。

  我們要明確說明的是,這是一個有些曲折的過程,並且其中會有多次的錯誤的轉折,而其餘的更有經驗的人當然會有著不同的-甚至更好的-方法。而事實上,我們成功的實現了建議,並沒有被完全的誤導。

  還有一些不同的論文討論SQL隱碼攻擊問題,包括一些更加詳細的文章,不過此文所展示的,與破解的過程同樣份量的是發現了SQL隱碼攻擊的原因。

  目標內網

  這顯然是一個完全自主開發的應用程式,而我們對於它沒有預先的瞭解,或者訪問原始碼的許可權:這已是一個“blind”攻擊。若干次偵測之後,我們瞭解到伺服器執行的是微軟的IIS6,並使用ASP.NET框架,從這其中得到的,似乎可以假定資料庫是微軟的SQL伺服器:我們相信這些技術可以應用於幾乎任何一種web應用,而此應用可能為任何一種SQL伺服器所支援。

  登陸頁面是一個傳統的使用者名稱-密碼錶單,帶有一個用電子郵件給我傳送密碼的連結;而後者被證實是整個系統的敗筆。

  當輸入一個電子郵件地址的時候,系統會假定次郵件存在的方式,再使用者資料庫裡面尋找這個電子郵件地址,並且會郵寄一些內容到這個地址。由於我的電子郵件地址沒有被找到,所以它不會給我傳送任何內容。

  所以,第一個測試,對於任何SQL化的表單而言,是輸入一個帶有單引號構成的資料:這樣做的目標是檢視是否他們在構建SQL字串的時候根本沒有使用資料的清理機制。當為此表單提交了一個有單引號的電子郵件之後,我們得到了一個500錯誤(伺服器失敗),這就是說,這個“被破壞了的”的輸入實際上被真實的分析過。中!

  我們猜測底層的SQL程式碼可能類似於如此:

SELECT fieldlist FROM table WHERE field = '$EMAIL';

  這裡,$EMAIL是由使用者通過表單提交的電子郵件地址,而這段較長的查詢提供的應用符號,是為了使得這個$EMAIL成為一個真正的字串。我們不知道這個資料域的確切的名字或者是於此相關的資料表的名字,但是我們卻瞭解他們的特性,而此後我們將會得到一些很好的猜測結果。

  當我們輸入steve@unixwiz.net' - 留意那個結尾的引號 - 這將產生出一個如下構建的SQL:

SELECT fieldlist FROM table WHERE field = 'steve@unixwiz.net'';

  當這個SQL被執行的時候,SQL分析器發現了多餘的引號,從而中止了工作,並給出一個語法錯誤。而這個錯誤如何表述給使用者,取決於應用程式內部錯誤修復的過程,但是這通常不同於“電子郵件地址不存在”的錯誤提示。這個錯誤的響應是致命的第一通道,既使用者的輸入沒有被正確的清理,而因此應用程式成為了破解的美食。

  由於我們輸入的資料顯然位於WHERE子語句中,讓我們用合法的SQL方式改變一下這個子句的本貌並看看會發生什麼。通過輸入任何一種‘OR ’x‘=x語句,結果SQL成為:

SELECT fieldlist FROM table WHERE field = 'anything' OR 'x'='x';

  因為應用程式沒有真正的對這樣的查詢有所考慮,而僅僅是構建一個字串,以致於我們使用的引號使得一個單元素的WHERE子句,變成了一個雙元素的子句,而且’x'=x字句是確定為真的,無論第一個字句是什麼。(有一種更好的方式來確保“始終為真”,這部分我們後面會談及)
不過與“真實”的查詢不同,本應當一次返回一個單獨的項,這個版本必然會返回成員資料庫裡面的每一個項。唯一的可以發現應用程式在這種情況下會做什麼的方式,就是嘗試。不斷嘗試,我們留意到以下結果:

  您的登入資訊已經傳送郵件到 random.person@example.com

  我們一般都會把查詢返回的第一行作為作為猜測的主要入口。這哥們確實從E-mail裡拿回了他的密碼,同樣這個郵件可能會讓他感到吃驚並且會引起懷疑。

  現在我們知道怎麼在本地玩這條查詢了,雖然目前我們還不知道我們看不到的那部分SQL結構是怎麼拼起來的。但是我們通過逆向工程看到了三個不同的查詢結果:

  • 您的登陸資訊已經以Email形式傳送給您
  • 我們無法識別您的Email地址
  • 服務端錯誤

  頭兩個響應是有效的查詢的結果,最後一個是無效SQL造成的。 類似這樣的響應結果會幫助我們更好的逆推服務端用來查詢的SQL語句結構。

  預設欄位對映

  我們要乾的第一步就是猜欄位名,首先我們合理的推測查詢帶了“email address” 和 “password”,所以可能的欄位名選擇會有“US Mail Address”  或者 “userid” 亦或者“phone number”  。當然最好能執行 show table,但是我們又不知道表名,貌似目前沒什麼明顯的方法能讓我們拿到表名。

  那就分步走吧。在每個例子裡,我們會用我們已知的SQL加上我們自己的特殊“段”。我們已知的這條SQL的結尾是個Email地址的比對,那就猜下email是這個欄位名吧

SELECT fieldlist FROM table WHERE field = 'x' AND email IS NULL; --';

  這段SQL的用意是我們假設預設的SQL查詢中的欄位名是 email ,跑一下看看是不是有效。我不會管你到底有沒有匹配的Email,所以用了個偽名“x” , “--” 這個標示是表示SQL的起始。這樣SQL解析到這就會把它直接當成一條命令,而“--”後面的會是一個新的命令,這樣就遮蔽掉了後面的那些不知道的玩意兒。

  如果伺服器響應是報錯,那基本上可以說明我們的SQL拼錯了。但是如果我們得到任何正常的返回,例如“未知的郵件地址” 或 “密碼已傳送”  ,說明我們的欄位名蒙對了。

  要注意的是,我們的“And” 關鍵字而沒用“OR” 關鍵字,這麼做是有目地滴。在上一步中,我們不關心到底是哪一個Email,而且我們不想因為蒙中某人的Email然後給他發了重置密碼的郵件。這麼搞那哥們兒一定會懷疑有人對他的帳號搞三搞四。所以用“And”關鍵字拼上一個不合法的Email地址,這樣服務端總是返回空結果集,也就不會給任何人發郵件。

  提交上面的SQL程式碼段確實返回了“未知的郵件地址” 這麼一個響應。現在我們確認了email地址的欄位名是email。如果不是這麼個響應,那我就再蒙“email_adress”或者“mail”亦或者其他類似的。這個環節總是靠蒙的,但是蒙也得講技巧和方法方式。

  下一步,我們來猜下其他比較明顯的欄位名: "password","userid","name" 和類似的。每次我們只蒙一個名字,當返回結果不是“服務端錯誤” , 那就說明我們蒙對了。

SELECT fieldlist FROM table WHERE email = 'x' AND userid IS NULL; --';

  通過這一步,我們蒙出來了下面幾個欄位名:

  • email
  • passwd
  • login_id
  • full_name

  肯定還有更多其他的(把HTML頁面表單的 <Input   name="XXXXX"> 拿來做參考是個非常不錯的選擇)後來我又挖了一下但是沒挖出來更多的欄位名。 到目前為止,我們還是不知道這些欄位所屬的表的表名--咋個弄呢?

  搜尋表名

  應用內建的query已經把表名放在語句中,但我們不知道表的名字。有幾種方法可以找到這些插入在語句中表名(以及其他的表名)。我們用的是一種依賴於subselect的方法。

  下面這個單獨的query

SELECT COUNT(*) FROM tabname 

  返回表中記錄的數量,當然,若表名是無效的,則查詢失敗。我們可以把這個查詢放入我們的查詢語句中來探查表名。

SELECT email, passwd, login_id, full_name
  FROM table WHERE email = 'x' AND 1=(SELECT COUNT(*) FROM tabname); --';

  我們實際上並不關心表中有多少記錄,我們關心的是表名是否有效。通過試探不同的猜測,我們最終確定members是資料庫中的有效表名。但是,這是這個查詢中所用的表名嗎?為此,我們需要另一個使用table.field的查詢:這隻在表名的確是查詢中的表名時才工作,而不僅僅當表存在時工作。

SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x' AND members.email IS NULL; --';

  當這個語句返回 "Email unknown"時,可以確認我們的SQL正確執行了,並且我們成功的猜出了表名。這對後面的工作很重要,但我們先臨時使用一下另一種方法。

  弄幾個帳號先

  目前我們搞到了members表結構的部分資訊,但是我們只知道一個使用者名稱,就是之前我們蒙中的那個發了郵件通知的那個使用者名稱。當時我們只得到了郵件地址,但是拿不到郵件內容。所以我們得再弄幾個有效得使用者名稱,高階洋氣上檔次的最好。

  我們的從公司的網站開始人肉,找到那些人物介紹的頁面,一般都是介紹公司內部人員的。這些介紹裡大多都有這些人的Email地址和名字。就算沒有這些資訊也沒啥,我們兜裡還有貨。

  思路是這樣的,提交一個帶有“Like” 關鍵字的SQL,這樣我們可以對Email地址或者使用者名稱做些模糊匹配,每次提交如果返回“我們已傳送您的密碼至郵箱”那也就是說我們的模糊查詢有效了,而且郵件也真的發了!!!。這麼幹雖然我們能拿到郵件地址,也意味著對方會收到郵件並引起警覺,所以  慎用!!

  我們能做email、full_name(或者其他欄位)的查詢,每次放入%這個萬用字元執行如下的查詢:

SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x' OR full_name LIKE '%Bob%';

 記住這有可能會返回不止一個“Bob”的資料,我們只能看到其中一條,建議繼續減少查詢範圍。

  最終,我們只會需要利用這個方式獲取一個有效的email地址。

  暴力破解密碼

  我們肯定可以在登陸頁面嘗試暴力破解密碼,但是大多的應用都做了相應的防護手段。可能的防護會有操作日誌,帳號鎖定或者其他能大大降低我們效率的手段或裝置,但是因為輸入沒有被過濾所以給我們繞過這些防護多了一些可能。

  我們將把密碼和郵件名稱的程式碼段加到我們已知的SQL裡。在這個例子裡我們會用一個倒黴催的哥們的郵箱,bob@example.com 然後試試我們準備的一些密碼。

SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'bob@example.com' AND passwd = 'hello123';

  這條SQL是完整有效的,所以伺服器鐵定不能夠報錯,所以我們知道當伺服器響應是“您的密碼已經傳送至您的郵箱” 這麼個結果是我們就知道剛提交的那個密碼就是我們要的密碼。雖然倒黴催的Bob也收到了郵件並且一定會警覺,但是我們在他警覺之前就幹完我們想幹的了。

  這個過程可以在Perl下用指令碼自動化完成,所以我們就去搞了搞Perl的指令碼,結果寫指令碼的時候發現了另外一種方法來幹這事。

  資料庫不是隻讀的

  迄今為止,我們對資料庫除了進行查詢外,沒做其他事。儘管SELECT是隻讀的,但不意味SQL就只能這樣。SQL使用分號來中斷一個語句, 如果對輸入沒有做正確的處理, 它就不能阻止我們在查詢語句後面新增不相關的字串。

  最恰當的一個例子就是這樣:

SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x'; DROP TABLE members; --';  -- Boom!

  第一部分提供了一個假的郵件地址 -- 'x' -- 我們並不關係這個查詢的返回,我們僅僅是給出了一個我們能夠使用無關SQL指令的方式,一個嘗試刪除整個members表而真的是無任何關係的操作.

  這表明我們不僅僅可以切分SQL指令,我們還可以修改資料庫,這完全是被允許的。

  加入一個新成員

  由上所得,我們獲知了members表的部分的結構,嘗試新增一個新的記錄到這個表裡面似乎是一個可拊掌稱慶的方法:如果這能成功,我們就能夠通過我們新插入的帳戶來直接登陸了。

  這裡,毫不驚奇的是,這隻需要稍微加些SQL,我們把它放在不同的行從而讓我們的展示更易理解,不過從頭到尾這一部分其實仍然是一個字串:

SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x'; INSERT INTO members ('email','passwd','login_id','full_name')  VALUES ('steve@unixwiz.net','hello','steve','Steve Friedl');--';

  即便是我們真的確定了表的名字以及使用的欄位都是正確的,在成功的實施攻擊之前,仍然有一些障礙:

  1.可能,在表單上,我們並沒有足夠的空間來直接輸入這些文字(儘管,可以使用指令碼來解決這個問題,而這並不是很簡便的事情)

  2. web應用程式的使用者可能並沒有在members表上的插入許可權。

  3. 毫無疑問,members表裡面有一些別的欄位,有一些可能需要初始化的資料,而這可能導致插入失敗。

  4. 即便是可以達到插入一個新的記錄,應用程式可能會出現不當的行為,因為自動插入的時候有一些欄位使用的是NULL。

  5. 一個有效的“成員”可能不僅僅需要在members表裡面的一個記錄,而是和別的表有著資料上的關聯,(如,“訪問許可權”),所以新增一個資料到一個表中,可能並不充分有效。

  對於手頭的案例而言,我們遇到了#4或者是#5上面的障礙 - 我們無法真正確定是哪一個 - 因為在主登陸介面上,輸入上面的使用者名稱+密碼的時候,返回了一個伺服器上的錯誤。這就是說,那些我們沒有用到的欄位可能是必要的欄位,而儘管如此,它們仍然沒有被正確的處理。

  這裡,一個可行的方法,就是猜測別的欄位,但是這可以保證是一個長時間而且耗費勞力的過程:雖然你可能可以猜測出來那些“顯而易見”的欄位,但是卻很難構建出整個應用程式的組織影像。

  所以,最後我們選擇走另一條不同的路。

  郵給我一個密碼

  我們意識到雖然不能新增一條新的記錄在members表中,但我們可以通過修改一個存在的記錄, 這也獲得了我們的證明是可行的。

  從先前的步驟中,我們知道bob@example.com在系統中有一個帳號,我們使用SQL隱碼攻擊更新了他的資料庫記錄為我們的郵件地址:

SELECT email, passwd, login_id, full_name
  FROM members
 WHERE email = 'x'; UPDATE members SET email = 'steve@unixwiz.net' WHERE email = 'bob@example.com';

  執行這個之後,我們當然會收到"we didn't know your email address"訊息,但這是預期的提供了不正確的郵件地址。UPDATE操作並不會嚮應用程式通知, 因此他被悄然執行了。

  我們可以用更新後的郵件地址,使用常規的"I lost my password"連結 - 一分鐘後就會受到這樣的郵件:

From: system@example.com
To: steve@unixwiz.net
Subject: Intranet login

This email is in response to your request for your Intranet log in information.
Your User ID is: bob
Your password is: hello

  現在,就可以使用標準的登入流程進入系統,作為一個高等級的職員。這是一個高許可權使用者,遠遠高於我們INSERT建立的受限使用者.

  我們發現這個內網站點的資訊比較全,甚至還有一個全使用者列表。所以我們可以合理的推測很多內網站點會有公司Windows網路帳號,並且用的是同樣的密碼。目前我們很明顯可以拿到內網的使用者密碼,而且我們在公司防火牆找到了一個開放的PPTP模式的的VPN埠讓我們很方便的做些登陸嘗試。

  我們手動抽查了一些帳號,沒成功。而且不知道到底服務端是因為“錯誤的密碼”還是“內網帳號和Windows帳號不同”而拒絕登陸的。反正就是個沒幹成。不過我嚼得自動化的工具應該能讓這步簡單點。

  其他一些方法

  在這次滲透中,我們覺得其實已經挖的足夠深了,不過還可以用其他的方法。我們就先看看我們現在想到一些普適性不是很高的方法。

  我們同時也注意到了不是所有的方法都是資料庫無關的,有些方法得依賴特定的資料庫。

  呼叫XP_CMDSHELL

  Microsoft的SQL Server 有一個儲存過程 “XP_CMDSHELL” 允許執行任意的作業系統命令,如果這項功授權給Web使用者呼叫的話,那基本上網站肯定會遭黑。我們現在乾的都被限制在了Web應用和資料庫這個環境下,一旦能執行作業系統命令,防禦再好的應用伺服器也沒轍了。呼叫這個儲存過程的許可權一般會賦給管理員帳號,但是還是存在授權給低階別使用者的可能。

  資料庫結構深度挖掘

  這個應用登陸後能幹的事太多了,在我看來實在沒啥必要再去挖了。不過在其他一些特殊的環境下我們的這些方法也許不夠用。如果能深度挖掘資料庫的結構,我們會發現更多的方法來黑掉站點。你可以試著看看其他的提交切入點(例如“留言板”,“幫助論壇”等等)。不過這都是對應用環境的強依賴而且還得靠有一定技術含量的瞎蒙。

  減輕危害

  我們相信web應用的開發者,通常不會去想那些“令人意外的輸入”,但是安全人員會(包括那些“壞人”),所以這裡有三個寬泛的方法,可以用來除害。

  過濾輸入,這是絕對重要的事情,過濾使用者的輸入,從而確保他們的輸入沒有包含具有威脅的程式碼,無論是對於SQL伺服器,或則是HTML本身都要考慮。某人最初的想法來剝掉“惡意程式碼”,例如引號,分號或者是轉義符號,其實這種嘗試是被誤導了的。儘管找出來些具有威脅的字元很容易,但是很難把他們全部找到。web的語言種到處都是特定的字元以及奇特的組合(包括那些用來表達同一些字元的另類模式),而努力去鑑定那些沒有被授權的“惡意程式碼”很可能不會成功。換而言之,與其“除去那些已知的惡資料”,倒不如“去掉所有良好資料之外所有的內容”:這其中的分別是至關重要。如前 - 我們的例子中 - 一個電子郵件地址僅能包括以下字元:

abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
@.-_+

  用沒有意義的文字是不益的,應該早點拒絕這麼做,這可能會產生一些錯誤資訊,這樣不僅可以幫助我們搶先SQL隱碼攻擊,而且可以讓我們及時發現拼寫錯誤以至於不會讓錯誤存入資料庫。

電子郵件的一些選項

  我們應該要特別的注意郵件地址,它會給驗證程式設計帶來麻煩的,因為,每個人看起來對郵件地址的“有效性”都有自己的想法,不用一個好的郵箱地址是不光彩的,這樣你將遇到你想不到的文字。

  這方面的真正權威是RFC 2822(比大家耳熟能詳RFC822內容還多),裡面包含了什麼是允許的比較範的定義。如果郵箱地址接受&和*(和其它普通字元比較)是不好的,但是其它的,包括這篇文章的作者,都會對“大多數”郵件地址滿意。

 那些採用嚴格方法的人應該充分的認識到不包括這些郵件地址的後果,特別是認為現在有很好的技術能夠解決那些“奇怪”的字元所帶來的安全問題。
請注意“過濾輸入”並不意味著“移除引號”,因為即使一個“正規”的字元也會很麻煩,在這個例子中,一個整型數字ID值被拿來和使用者的輸入做比較(叫數字型PIN):

SELECT fieldlist FROM table WHERE id = 23 OR 1=1;  -- Boom! Always matches!

  不過實際情況是我們很難把輸入項完全過濾掉潛在危險字元。“日期項”,“郵件地址”,或者“整形” 用上面的辦法過濾是可以的。但是在真實環境中我們,我們還得用到其他的方法。

  輸入項編碼/轉義

  雖然現在可以過濾郵件地址或者電話號碼,但是貌似“Bill O‘Reilly” 這樣的合法的名字你是很難處理的,因為“ ’ ” 這個單引號是合法的輸入。於是有人就想到在過濾到單引號的時候我再加一個單引號這樣就沒問題了,其實這麼幹得出大事。

SELECT fieldlist FROM customers
 WHERE name = 'Bill O''Reilly';  -- 目前這樣是OK的

  但是,這個方法很容易就被繞過去了。像MySQL允許 \' 這個輸入,然後如果有人造一段SQL “ \'; DROP TABLE users;” ,你又給單引號加了個單引號,那就變成

SELECT fieldlist FROM customers
 WHERE name = '\''; DROP TABLE users; --';  -- 刪表了

  ' \' '就被新加的單引號分成一個字串(按照前面的過濾方法這裡判斷有一個單引號,所以過濾後再單引號後面加一個單引號),後面接的是常見的SQL惡意程式碼。其實不僅僅有反斜線的情況,還有Unicode編碼和其他編碼規則或者其他的過濾漏洞都會給程式設計師挖坑。俺們都知道,實現輸入過濾的絕對安全是超級難滴,這就是為啥很多資料庫介面都提供了相關功能。當同樣的內容被系統自帶的字串過濾或字串編碼處理後會好很多。例如呼叫MySQL的函式 mysql_real_escape_string() 或者 perl  DBD 的$dbh->quote($value)方法. 這些方法都必用的。

  引數繫結(預編譯語句)

  雖然資料庫自帶的過濾是個不錯的實現,但是我們還是處在“使用者輸入被當成 SQL語句的一部分 ”這麼個圈子裡,其實要跳出這個圈子還有一個實現,就是引數繫結。基本上所有的主流資料庫都提供這種介面。這種方法提前預編譯了SQL語句的邏輯,然後對引數預留了位置(就是“ ?1  ?2” 這種的)。這樣語句在執行的時候只是按照輸入的參數列來執行。

  Perl環境的例子:

$sth = $dbh->prepare("SELECT email, userid FROM members WHERE email = ?;");

$sth->execute($email);

  感謝Stefan Wagner幫我寫了個java的實現

  不安全版

Statement s = connection.createStatement();
ResultSet rs = s.executeQuery("SELECT email FROM member WHERE name = "
                             + formField); // *boom*

  安全版

PreparedStatement ps = connection.prepareStatement(
    "SELECT email FROM member WHERE name = ?");
ps.setString(1, formField);
ResultSet rs = ps.executeQuery();

  這兒$email是從使用者表單中獲取來的資料,並且是做為位置引數#1(即第一個問號)傳遞進來的,因此在任何情況下,這個變數的內容都可以解析為SQL語句。引號、分號、反斜槓、SQL註釋表示法-其中的任何一個都不會產生任何特殊的效果,這個因為它們“只是資料”。這不會對其他東西造成破壞,因此這個應用很大程度上防止了SQL隱碼攻擊。

  如果對這個查詢進行多次重用(這個查詢只解析一次)的話,還可以提高效能。然而與獲得大量的安全方面的好處相比,這個是微不足道的。這還可能是我們保證網際網路應用安全所採取的一個重要的措施。

  限制資料庫許可權和隔離使用者

  在目前這種情況下,我們觀察到只有兩個互動式動作不在登入使用者的上下文環境中:“登入”和“給我傳送密碼”。web應用應該使用盡可能少許可權的資料庫連線:僅對members表具有查詢許可權,對其它表沒有任何訪問許可權的資料庫連線。

  這樣做的結果是:即便是“成功地”進行了SQL隱碼攻擊,也只能取得非常有限的成功。這種情況下,我們不能做最終授權給我們的任何更新(UPDATE)請求,因此為了能夠實現更新(UPDATE)請求,我們不得不尋求其他解決方法。

  一旦web應用確定了通過登入表單傳遞認證憑證是有效的話,那麼它將把這個會話切換到一個具有更多許可權的資料庫連線上。

  對任何web應用來說,從不使用sa許可權幾乎是理所當然的事情。

  對資料庫的訪問採用儲存過程

  當一個資料庫伺服器支援儲存過程時,請使用儲存過程執行這個應用的訪問行為,這樣(在儲存過程編寫正確的情況下)就完全不需要SQL了。

  通過把諸如查詢、更新、刪除等某個動作的規則封裝成一個單獨的儲存過程,你就可以根據這個單獨的儲存過程和所執行的商務規則額對其進行測試和歸檔。(例如,“增加新的訂單”儲存過程在客戶超過了信用限制的情況下可能拒絕新增這個訂單。)

  對簡單的查詢來說,這麼做可能僅僅獲得很少的好處,不過當這個操作變的越來越複雜(或者是在多個地方使用這個操作)的情況下,給這樣操作一個單獨的定義就意味著維護這個操作將更簡單而且這個操作的功能會更強壯。

  注意:總可以編寫動態構建查詢語句的儲存過程:這麼做並不會防止SQL隱碼攻擊-它只不過把準備/執行過程正確地結合在一起,或者只不過把SQL語句與提供保護的變數直接捆綁在一起。

  隔離web伺服器

  即便已經實施了所有這些預防措施,仍然有可能遺漏了某些地方,從而使伺服器對於傷害而門戶洞開。你應當在設計網路基礎架構的時候假設壞人獲得了完全訪問機器的管理員許可權,然後試圖降低這種情形對其它事情傷害的影響。

  例如,把這臺機器放置在網路“內部”具有非常少漏洞的DMZ區,這麼做意味著即便取得了web伺服器的完全控制也不能自動的獲得對其他一切的完全訪問許可權。當然,這麼做不能阻止所有的入侵,不過它可以使入侵變的非常困難。

  配置錯誤彙報機制

  一些框架的預設錯誤彙報機制包括開發人員的除錯資訊,而且這不可能向外部使用者公開。想象一下:如果完整的查詢都顯示出來,並且知名語法錯誤的地方,那麼對攻擊者來說,這時的攻擊將是多麼容易啊。

  這些資訊對開發者來說很有用,不過如果可能的話,應當限制只有內部使用者才能訪問。

  注意:不是所有的資料庫都可以採用同樣的方式配置,而且並不是所有的資料庫都支援相同的SQL(這裡“S”代表的是“結構化的”,而不是“標準的”)方言。例如,MySQL的大多數版本都不支援子查詢,同時也不允許出現多個語句:當你試圖穿透一個網路的時候,這些實質上就是使問題複雜化的因素。

  強調一下,雖然俺們在這次演示中選擇“忘記密碼”這麼個功能點,不是因為這個功能點本身不安全,而是普遍存在各站點的幾個容易遭受攻擊的功能點之一。如果你把關注點放在如何通過“忘記密碼”來進行滲透的話,那你就跑偏了。

  這篇文章的本意不是全面覆蓋SQL隱碼攻擊的精髓,甚至連教學都算不上。其實只是我們花了幾個小時做的一單滲透測試的工作紀錄。我們也見過其他的文章講SQL隱碼攻擊的技術背景,但是都只是展示了滲透結束後的成果而沒有細節。

  但是那些結果需要大量的背景知識才能看的懂的,而且我覺得滲透的細節也是很有價值的。我們正常情況下是拿不到原始碼的,所以滲透人員的逆向黑盒滲透的能力也是有價值的。

  我要感謝David Litchfield Randal Schwartz 對本篇文章的技術貢獻,和Chris Mospaw 牛逼的排版設計。(© 2005 by Chris Mospaw, used with permission)

  其他參考源

  原文地址:http://www.unixwiz.net/techtips/sql-injection.html

相關文章