2013年7月27日 星期六

CSRF攻擊與防禦策略

目錄 
1 CSRF攻擊簡介 1 
1.1 什麼是CSRF 1 
1.2 CSRF可以做什麼 1 
1.3 CSRF漏洞現狀 1 
2 CSRF的攻擊原理 1 
2.1 CSRF攻擊原理 1 
2.2 CSRF攻擊實例 2 
2.3 CSRF攻擊對象 3 
3 CSRF的防禦策略 3 
3.1 驗證HTTP REFERER字段 3 
3.2 請求中添加TOKEN 4 
3.3 在HTTP頭中自定義屬性並驗證 5 
3.4 代碼示例 6 


1 CSRF攻擊簡介 
1.1 什麼是CSRF 
CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱为:one click attack/session riding,縮寫为:CSRF/XSRF。盡管聽起來像跨站腳本(XSS), 但它與XSS非常不同,並且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則通過偽裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊 相比,CSRF攻擊往往不大流行(因此對其進行防範的資源也相當稀少)和難以防範,所以被認为比XSS更具危險性。 

1.2 CSRF可以做什麼 
你這可以這麼理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發送惡意請求。CSRF能夠做的事情包括:以你名義發送郵件,發消息,盜取你的账號,甚至於購買商品,虛擬貨币轉账......造成的問題包括:個人隱私泄露以及财產安全。 

1.3 CSRF漏洞現狀 
CSRF這種攻擊方式在2000年已經被國外的安全人員提出,但在國內,直到06年才開始被關注,08年,國內外的多個大型社區和交互網站分別爆出 CSRF漏洞,如:NYTimes.com(紐約時報)、Metafilter(一個大型的BLOG網站),YouTube和百度HI......而現 在,互聯網上的許多站點仍對此毫無防備,以至於安全業界稱CSRF为“沉睡的巨人”。 

2 CSRF的攻擊原理 
2.1 CSRF攻擊原理 
CSRF攻擊原理如下圖所示: 

 

圖中Browse是瀏覽器,WebServerA是受信任網站/被攻擊網站A,WebServerB是惡意網站/攻擊網站B。 
(1),用戶打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼登陆請求登陆網站A。 
(2),網站A驗證用戶信息,用戶信息通過驗證後,網站A產生Cookie信息並返回给瀏覽器。 
(3),用戶登陆網站A成功後,可以正常請求網站A。 
(4),用戶未退出網站A之前,在同一瀏覽器中,打開一個TAB訪問網站B。 
(5),網站B接受到用戶請求後,返回一些攻擊性代碼。 
(6),瀏覽器在接受到這些攻擊性代碼後,促使用戶不知情的情況下瀏覽器攜帶Cookie(包括sessionId)信息,請求網站A。這種請求有可能刪除,更新等操作。 

從上面CSRF攻擊原理可以看出,要完成一次CSRF攻擊,需要被攻擊者完成兩個步驟: 
1, 登陆受信任網站A,並在本地生成COOKIE。 
2, 在不登出A的情況下,訪問危險網站 B。 
看到這裏,你也許會說:“如果我不滿足以上兩個條件中的一個,我就不會受到CSRF的攻擊”。是的,確實如此,但你不能保證以下情況不會發生: 
1.你不能保證你登錄了一個網站後,不再打開一個tab頁面並訪問另外的網站。 
2.你不能保證你關閉瀏覽器了後,你本地的Cookie立刻過期,你上次的會話已經結束。(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認为關閉瀏覽器就等於退出登錄/結束會話了......) 
3.上圖中所謂的攻擊網站,可能是一個存在其他漏洞的可信任的經常被人訪問的網站。 

2.2 CSRF攻擊實例 
CSRF 攻擊可以在受害者毫不知情的情況下以受害者名義偽造請求發送给受攻擊站點,從而在並未授權的情況下執行在權限保護之下的操作。 
比如說,受害者 Bob 在銀行有一筆存款,通過對銀行的網站發送請求 
http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 
可以使 Bob 把 1000000 的存款轉到 bob2 的账號下。通常情況下,該請求發送到網站後,服務器會先驗證該請求是否來自一個合法的 session,並且該 session 的用戶 Bob 已經成功登陆。 
黑客 Mallory 自己在該銀行也有账戶,他知道上文中的 URL 可以把錢進行轉帳操作。Mallory 可以自己發送一個請求给銀行: 
http://bank.example/withdraw?account=bob& amount=1000000&for=Mallory。 
但是這個請求來自 Mallory 而非 Bob,他不能通過安全認證,因此該請求不會起作用。 
這時,Mallory 想到使用 CSRF 的攻擊方式,他先自己做一個網站,在網站中放入如下代碼: 
 
並且通過廣告等誘使 Bob 來訪問他的網站。當 Bob 訪問該網站時,上述 url 就會從 Bob 的瀏覽器發向銀行,而這個請求會附帶 Bob 瀏覽器中的 cookie 一起發向銀行服務器。大多數情況下,該請求會失敗,因为他要求 Bob 的認證信息。但是,如果 Bob 當時恰巧剛訪問他的銀行後不久,他的瀏覽器與銀行網站之間的 session 尚未過期,瀏覽器的 cookie 之中含有 Bob 的認證信息。這時,悲剧發生了,這個 url 請求就會得到響應,錢將從 Bob 的账號轉移到 Mallory 的账號,而 Bob 當時毫不知情。等以後 Bob 發現账戶錢少了,即使他去銀行查詢日志,他也只能發現確實有一個來自於他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而 Mallory 則可以拿到錢後逍遙法外。 

2.3 CSRF攻擊對象 
在討論如何抵禦 CSRF 之前,先要明確 CSRF 攻擊的對象,也就是要保護的對象。從以上的例子可知,CSRF 攻擊是黑客借助受害者的 cookie 騙取服務器的信任,但是黑客並不能拿到 cookie,也看不到 cookie 的內容。另外,對於服務器返回的結果,由於瀏覽器同源策略的限制,黑客也無法進行解析。因此,黑客無法從返回的結果中得到任何東西,他所能做的就是给服務 器發送請求,以執行請求中所描述的命令,在服務器端直接改變數據的值,而非竊取服務器中的數據。所以,我們要保護的對象是那些可以直接產生數據改變的服 務,而對於讀取數據的服務,則不需要進行 CSRF 的保護。比如銀行系統中轉账的請求會直接改變账戶的金額,會遭到 CSRF 攻擊,需要保護。而查詢餘額是對金額的讀取操作,不會改變數據,CSRF 攻擊無法解析服務器返回的結果,無需保護。 

3 CSRF的防禦策略 
在業界目前防禦 CSRF 攻擊主要有三種策略:驗證 HTTP Referer 字段;在請求地址中添加 token 並驗證;在 HTTP 頭中自定義屬性並驗證。下面就分別對這三種策略進行詳細介紹。 
3.1 驗證HTTP Referer字段 
根據 HTTP 協議,在 HTTP 頭中有一個字段叫 Referer,它記錄了該 HTTP 請求的來源地址。在通常情況下,訪問一個安全受限頁面的請求來自於同一個網站,比如需要訪問 http://bank.example/withdraw?account=bob&amount=1000000& for=Mallory,用戶必須先登陆 bank.example,然後通過點擊頁面上的按鈕來觸發轉账事件。這時,該轉帳請求的 Referer 值就會是轉账按鈕所在的頁面的 URL,通常是以 bank.example 域名開頭的地址。而如果黑客要對銀行網站實施 CSRF 攻擊,他只能在他自己的網站構造請求,當用戶通過黑客的網站發送請求到銀行時,該請求的 Referer 是指向黑客自己的網站。因此,要防禦 CSRF 攻擊,銀行網站只需要對於每一個轉账請求驗證其 Referer 值,如果是以 bank.example 開頭的域名,則說明該請求是來自銀行網站自己的請求,是合法的。如果 Referer 是其他網站的話,則有可能是黑客的 CSRF 攻擊,拒絕該請求。 
這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不需要操心 CSRF 的漏洞,只需要在最後给所有安全敏感的請求統一增加一個攔截器來檢查 Referer 的值就可以。特別是對於當前現有的系統,不需要改變當前系統的任何已有代碼和邏輯,沒有風險,非常便捷。 
然而,這種方法並非萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,但是每個瀏覽器對於 Referer 的具體實現可能有差別,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來講,這样並不安全。事實上,對於某些瀏覽器,比如 IE6 或 FF2,目前已經有一些方法可以篡改 Referer 值。如果 bank.example 網站支持 IE6 瀏覽器,黑客完全可以把用戶瀏覽器的 Referer 值設为以 bank.example 域名開頭的地址,這样就可以通過驗證,從而進行 CSRF 攻擊。 
即便是使用最新的瀏覽器,黑客無法篡改 Referer 值,這種方法仍然有問題。因为 Referer 值會記錄下用戶的訪問來源,有些用戶認为這样會侵犯到他們自己的隱私權,特別是有些組織擔心 Referer 值會把組織內網中的某些信息泄露到外網中。因此,用戶自己可以設置瀏覽器使其在發送請求時不再提供 Referer。當他們正常訪問銀行網站時,網站會因为請求沒有 Referer 值而認为是 CSRF 攻擊,拒絕合法用戶的訪問。 

3.2 請求中添加Token 
CSRF 攻擊之所以能夠成功,是因为黑客可以完全偽造用戶的請求,該請求中所有的用戶驗證信息都是存在於 cookie 中,因此黑客可以在不知道這些驗證信息的情況下直接利用用戶自己的 cookie 來通過安全驗證。要抵禦 CSRF,關鍵在於在請求中放入黑客所不能偽造的信息,並且該信息不存在於 cookie 之中。可以在 HTTP 請求中以参數的形式加入一個隨機產生的 token,並在服務器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 內容不正確,則認为可能是 CSRF 攻擊而拒絕該請求。 
這種方法要比檢查 Referer 要安全一些,token 可以在用戶登陆後產生並放於 session 之中,然後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以参數的形式加入請求。對於 GET 請求,token 將附在請求地址之後,這样 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來說,要在 form 的最後加上 ,這样就把 token 以参數的形式加入請求了。但是,在一個網站中,可以接受請求的地方非常多,要對於每一個請求都加上 token 是很麻煩的,並且很容易漏掉,通常使用的方法就是在每次頁面加載時,使用 javascript 遍曆整個 dom 樹,對於 dom 中所有的 a 和 form 標簽後加入 token。這样可以解决大部分的請求,但是對於在頁面加載之後動態生成的 html 代碼,這種方法就沒有作用,還需要程序員在編碼時手動添加 token。 
該方法還有一個缺點是難以保證 token 本身的安全。特別是在一些論壇之類支持用戶自己發表內容的網站,黑客可以在上面發布自己個人網站的地址。由於系統也會在這個地址後面加上 token,黑客可以在自己的網站上得到這個 token,並馬上就可以發動 CSRF 攻擊。为了避免這一點,系統可以在添加 token 的時候增加一個判斷,如果這個鏈接是鏈到自己本站的,就在後面添加 token,如果是通向外網則不加。不過,即使這個 csrftoken 不以参數的形式附加在請求之中,黑客的網站也同样可以通過 Referer 來得到這個 token 值以發動 CSRF 攻擊。這也是一些用戶喜歡手動關閉瀏覽器 Referer 功能的原因。 


3.3 在HTTP頭中自定義屬性並驗證 
這種方法也是使用 token 並進行驗證,和上一種方法不同的是,這裏並不是把 token 以参數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裏。通過 XMLHttpRequest 這個類,可以一次性给所有該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這样解决了上種方法在請求中加入 token 的不便,同時,通過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔心 token 會透過 Referer 泄露到其他網站中去。 
然而這種方法的局限性非常大。XMLHttpRequest 請求通常用於 Ajax 方法中對於頁面局部的異步刷新,並非所有的請求都适合用這個類來發起,而且通過該類請求得到的頁面不能被瀏覽器所記錄下,從而進行前進,後退,刷新,收藏 等操作,给用戶帶來不便。另外,對於沒有進行 CSRF 防護的遺留系統來說,要采用這種方法來進行防護,要把所有請求都改为 XMLHttpRequest 請求,這样幾乎是要重寫整個網站,這代價無疑是不能接受的。 


3.4 代碼示例 
下文將以 Java 为例,對上述三種方法分別用代碼進行示例。無論使用何種方法,在服務器端的攔截器必不可少,它將負責檢查到來的請求是否符合要求,然後視結果而决定是否繼 續請求或者丟棄。在 Java 中,攔截器是由 Filter 來實現的。我們可以編寫一個 Filter,並在 web.xml 中對其進行配置,使其對於訪問所有需要 CSRF 保護的資源的請求進行攔截。 
在 filter 中對請求的 Referer 驗證代碼如下 

清單 1. 在 Filter 中驗證 Referer 

// 從 HTTP 頭中取得 Referer 值 
String referer=request.getHeader("Referer"); 
// 判斷 Referer 是否以 bank.example 開頭 
if((referer!=null) &&(referer.trim().startsWith(“bank.example”))){ 
    chain.doFilter(request, response); 
}else{ 
    request.getRequestDispatcher(“error.jsp”).forward(request,response); 


以上代碼先取得 Referer 值,然後進行判斷,當其非空並以 bank.example 開頭時,則繼續請求,否則的話可能是 CSRF 攻擊,轉到 error.jsp 頁面。 
如果要進一步驗證請求中的 token 值,代碼如下 

清單 2. 在 filter 中驗證請求中的 token 

HttpServletRequest req = (HttpServletRequest)request; 
HttpSession s = req.getSession(); 

// 從 session 中得到 csrftoken 屬性 
String sToken = (String)s.getAttribute(“csrftoken”); 
if(sToken == null){ 

    // 產生新的 token 放入 session 中 
    sToken = generateToken(); 
    s.setAttribute(“csrftoken”,sToken); 
    chain.doFilter(request, response); 
} else{ 

    // 從 HTTP 頭中取得 csrftoken 
    String xhrToken = req.getHeader(“csrftoken”); 

    // 從請求参數中取得 csrftoken 
    String pToken = req.getParameter(“csrftoken”); 
    if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ 
        chain.doFilter(request, response); 
    }else if(sToken != null && pToken != null && sToken.equals(pToken)){ 
        chain.doFilter(request, response); 
    }else{ 
        request.getRequestDispatcher(“error.jsp”).forward(request,response); 
    } 


首先判斷 session 中有沒有 csrftoken,如果沒有,則認为是第一次訪問,session 是新建立的,這時生成一個新的 token,放於 session 之中,並繼續執行請求。如果 session 中已經有 csrftoken,則說明用戶已經與服務器之間建立了一個活躍的 session,這時要看這個請求中有沒有同時附帶這個 token,由於請求可能來自於常規的訪問或是 XMLHttpRequest 異步訪問,我們分別嘗試從請求中獲取 csrftoken 参數以及從 HTTP 頭中獲取 csrftoken 自定義屬性並與 session 中的值進行比較,只要有一個地方帶有有效 token,就判定請求合法,可以繼續執行,否則就轉到錯誤頁面。生成 token 有很多種方法,任何的隨機算法都可以使用,Java 的 UUID 類也是一個不錯的選擇。 
除了在服務器端利用 filter 來驗證 token 的值以外,我們還需要在客戶端给每個請求附加上這個 token,這是利用 js 來给 html 中的鏈接和表單請求地址附加 csrftoken 代碼,其中已定義 token 为全局變量,其值可以從 session 中得到。 

清單 3. 在客戶端對於請求附加 token 

function appendToken(){ 
    updateForms(); 
    updateTags(); 


function updateForms() { 
    // 得到頁面中所有的 form 元素 
    var forms = document.getElementsByTagName('form'); 
    for(i=0; i
        var url = forms[i].action; 

        // 如果這個 form 的 action 值为空,則不附加 csrftoken 
        if(url == null || url == "" ) continue; 

        // 動態生成 input 元素,加入到 form 之後 
        var e = document.createElement("input"); 
        e.name = "csrftoken"; 
        e.value = token; 
        e.type="hidden"; 
        forms[i].appendChild(e); 
    } 


function updateTags() { 
    var all = document.getElementsByTagName('a'); 
    var len = all.length; 

    // 遍曆所有 a 元素 
    for(var i=0; i
        var e = all[i]; 
        updateTag(e, 'href', token); 
    } 


function updateTag(element, attr, token) { 
    var location = element.getAttribute(attr); 
    if(location != null && location != '' '' ) { 
        var fragmentIndex = location.indexOf('#'); 
        var fragment = null; 
        if(fragmentIndex != -1){ 

            //url 中含有只相當頁的錨標記 
            fragment = location.substring(fragmentIndex); 
            location = location.substring(0,fragmentIndex); 
        } 

        var index = location.indexOf('?'); 

        if(index != -1) { 
            //url 中已含有其他参數 
            location = location + '&csrftoken=' + token; 
        } else { 
            //url 中沒有其他参數 
            location = location + '?csrftoken=' + token; 
        } 
        if(fragment != null){ 
            location += fragment; 
        } 

        element.setAttribute(attr, location); 
    } 


在客戶端 html 中,主要是有兩個地方需要加上 token,一個是表單 form,另一個就是鏈接 a。這段代碼首先遍曆所有的 form,在 form 最後添加一隱藏字段,把 csrftoken 放入其中。然後,代碼遍曆所有的鏈接標記 a,在其 href 屬性中加入 csrftoken 参數。注意對於 a.href 來說,可能該屬性已經有参數,或者有錨標記。因此需要分情況討論,以不同的格式把 csrftoken 加入其中。 
如果你的網站使用 XMLHttpRequest,那麼還需要在 HTTP 頭中自定義 csrftoken 屬性,利用 dojo.xhr 给 XMLHttpRequest 加上自定義屬性代碼如下: 

清單 4. 在 HTTP 頭中自定義屬性 

var plainXhr = dojo.xhr; 

// 重寫 dojo.xhr 方法 
dojo.xhr = function(method,args,hasBody) { 
    // 確保 header 對象存在 
    args.headers = args.header || {}; 

    tokenValue = '<%=request.getSession(false).getAttribute("csrftoken")%>'; 
    var token = dojo.getObject("tokenValue"); 
    
    // 把 csrftoken 屬性放到頭中 
    args.headers["csrftoken"] = (token) ? token : "  "; 
    return plainXhr(method,args,hasBody); 
}; 

這裏改寫了 dojo.xhr 的方法,首先確保 dojo.xhr 中存在 HTTP 頭,然後在 args.headers 中添加 csrftoken 字段,並把 token 值從 session 裏拿出放入字段中。 
通過上文討論可知,目前業界應對 CSRF 攻擊有一些克制方法,但是每種方法都有利弊,沒有一種方法是完美的。如何選擇合适的方法非常重要。如果網站是一個現有系統,想要在最短時間內獲得一定程度 的 CSRF 的保護,那麼驗證 Referer 的方法是最方便的,要想增加安全性的話,可以選擇不支持低版本瀏覽器,畢竟就目前來說,IE7+, FF3+ 這類高版本瀏覽器的 Referer 值還無法被篡改。 
如果系統必須支持 IE6,並且仍然需要高安全性。那麼就要使用 token 來進行驗證,在大部分情況下,使用 XmlHttpRequest 並不合适,token 只能以参數的形式放於請求之中,若你的系統不支持用戶自己發布信息,那這種程度的防護已經足夠,否則的話,你仍然難以防範 token 被黑客竊取並發動攻擊。在這種情況下,你需要小心規劃你網站提供的各種服務,從中間找出那些允許用戶自己發布信息的部分,把它們與其他服務分開,使用不同 的 token 進行保護,這样可以有效抵禦黑客對於你關鍵服務的攻擊,把危害降到最低。畢竟,刪除別人一個帖子比直接從別人账號中轉走大筆存款嚴重程度要輕的多。 
如果是開發一個全新的系統,則抵禦 CSRF 的選擇要大得多。筆者建議對於重要的服務,可以盡量使用 XMLHttpRequest 來訪問,這样增加 token 要容易很多。另外盡量避免在 js 代碼中使用复雜邏輯來構造常規的同步請求來訪問需要 CSRF 保護的資源,比如 window.location 和 document.createElement(“a”) 之類,這样也可以減少在附加 token 時產生的不必要的麻煩。 

最後,要記住 CSRF 不是黑客唯一的攻擊手段,無論你 CSRF 防範有多麼嚴密,如果你系統有其他安全漏洞,比如跨站域腳本攻擊 XSS,那麼黑客就可以繞過你的安全防護,展開包括 CSRF 在內的各種攻擊,你的防線將如同虛設。 
可見,CSRF 是一種危害非常大的攻擊,又很難以防範。目前幾種防禦策略雖然可以很大程度上抵禦 CSRF 的攻擊,但並沒有一種完美的解决方案。一些新的方案正在研究之中,比如對於每次請求都使用不同的動態口令,把 Referer 和 token 方案結合起來,甚至嘗試修改 HTTP 規範,但是這些新的方案尚不成熟,要正式投入使用並被業界廣为接受還需時日。在這之前,我們只有充分重視 CSRF,根據系統的實際情況選擇最合适的策略,這样才能把 CSRF 的危害降到最低。 


reference : http://rritw.com/a/ITxinwen/hulianwang/20120620/174026.html
reference 2 : http://www.youtube.com/watch?v=pDXTDR6xew8
en → zh
参数

2013年7月10日 星期三

Python Logging - Sentry

Sentry helps with exception logging and viewing for your Django project. It can handle multiple Django installs where the logs can be managed from a single place, or it can be used on a per-project basis. The latter is how it’s preconfigured in DJ Skeletor.
Besides logging the exceptions, Sentry can also catch normal logs you create with Python’s logging system. This is also preconfigured in DJ Skeletor.

Example

Enough talk, let’s see some action. First, we’ll create a virtual environment (you do use virtualenv, right? if not, you should) and install prerequisite packages:
    virtualenv --no-site-packages myenv
    cd myenv
    source bin/activate
    pip install django django-debug-toolbar south django-sentry
Next, we’ll clone the boilerplate project:
    git clone https://github.com/senko/dj-skeletor.git myproject
    cd myproject
Let’s activate the development environment and prepare the database:
    cd settings
    ln -s dev.py local.py
    cd ..
    python manage.py syncdb
    python manage.py migrate sentry
All done, let’s run it!
    python manage.py runserver
See? Piece of cake. With boring initial set-up out of our way, we can focus on building an awesome web site or app.
DJ Skeletor is open source and is available on GitHub. Feel free to use it or base your own boilerplate on it – if you do, please share your thoughts in the comments.
en → zh

Python Web framework - Pyramid

Django用久了,难免会感觉到自由度不够。不可否认,这种自由度的牺牲能够带来一些好处。比如,一体化的框架(full-stack framework)能够提供全面的solution,你不需要再为cache,session,feed,orm,auth,template等问题困扰,完全按照Django的设计哲学来使用就可以了。使用Django能够快速开发应用,而且技术的统一对于团队开发来说无疑是一件好事,团队成员无需在技术选型上进行过多的争论——Django已经帮我们定好了,just DO it。

但是在提供全面的solution的同时,Django将自己打造成了一个封闭的框架。由于所有的组件都靠自己实现,可能在每一个方面都做得不够好(尽管够用)。同时为了方便使用,Django的各个组件之间耦合比较紧密,你很难将某个组件替换成你自己喜欢的库。比如Django最为人诟病的ORM和Template,如果你想替换成SQLAlchemy和Mako,几乎是不可能的事情。即使勉强使用,也会让你非常别扭。

话说天下大事,分久必合。在python web framework 的战国时代,Django几乎做到了一家独大。但如果其他的框架合纵联合,未必不能与Django一争高下。
现在这种融合的趋势已经显现,而Django如果依然固步自封,即使再强大,也会在历史的尘埃中销声匿迹。如果你不相信,我们可以追索一下事实真相,不难看出这种融合恰恰是从更加强大的Zope的分裂开始的。

《戏说WSGI》一文中,曾经列举了几个python web框架。其中Zope是一个另类,因为Zope非常厚重,适合组织大规模的开发,构建复杂的应用。为了能够面对更高的复杂度,Zope提供了一个组件框架ZCA (Zope Component Architure),引入了接口的概念,使得复杂系统可以基于接口开发。ZCA提供了适配器组件,用于装配整个应用的各个组成部分,还进一步提供了可重用的工具组件,事件组件等。可以说,ZCA是Zope中最有价值的产品。

尽管ZCA能够将Zope提供的认证、对象发布、事务管理、授权等功能组装起来,但是这些功能在Zope之外还是很难使用。于是有了Repoze项目。Repoze的目标是将Zope的功能组件拆解出来,使得其他的python web应用能够使用。说到这里,不禁又要感慨一下WSGI的重要意义:WSGI定义了Web服务器和Web应用以及Web中间件之间的交互协议,只要支持WSGI,各种web服务器、web应用和中间件就能相互对接。而Repoze正是将Zope组件按照WSGI进行封装,从而遵循WSGI的python web 框架可以很容易使用这些组件。

最初,Repoze推出了自己的一个非常轻量级的web框架:repoze.bfg。在repoze.bfg中利用了ZCA的特性。于是python web framework世界中又多了一方势力。但恰恰是这个不起眼的小势力推动了历史的进程。repoze.bfg和Pylons类似, 几乎都是完全的策略自由(policy-free),你可以随意的指定数据持久层,模板系统等外围部分,而其核心就是约定了URL如何映射到 view 代码以及如何调用view,在这个过程中完全遵循WSGI规范。后来repoze.cfg的开发者决定将其合并到Pylons。于是拉开了Python web框架合并的序幕。这个合并的框架叫做Pyramid。无独有偶,原本基于Pylons的TurboGears也宣布开发基于Pyramid的分支。

至此,混乱的江湖看到了统一的希望,相信越来越多的python web框架会融合,或至少能够互通。或许,随着更加开放的框架的出现,Django会慢慢淡出大家的视野,让我们拭目以待。

官網:http://www.pylonsproject.org/
en → zh
和网络

wibiya widget