国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

從 WTForm 的 URLXSS 談開源組件的安全性

lordharrd / 635人閱讀

摘要:能夠開發開源組件的開發者本身素質相對較高,代碼質量較高,也使開源組件出漏洞的可能性較小。這種對比明顯反應出開源組件和開源應用在安全漏洞關注度上的差距。另一個例子,我們通過修改邏輯運算符改變開發者正常的判斷流程,造成安全問題。

開源組件是我們大家平時開發的時候必不可少的工具,所謂『不要重復造輪子』的原因也是因為,大量封裝好的組件我們在開發中可以直接調用,減少了重復開發的工作量。
開源組件和開源程序也有一些區別,開源組件面向的使用者是開發者,而開源程序就可以直接面向用戶。開源組件,如JavaScript里的uploadify,php里的PHPExcel等;開源程序,如php寫的wordpress、joomla,node.js寫的ghost等。
就安全而言,毋庸置疑,開源組件的漏洞影響面遠比開源軟件要大。但大量開源組件的漏洞卻很少出現在我們眼中,我總結了幾條原因:

開源程序的漏洞具有通用性,很多可以通過一個通用的poc來測試全網,更具『商業價值』;而開源組件由于開發者使用方法不同,導致測試方法不統一,利用門檻也相對較高

大眾更熟悉開源軟件,如wordpress,而很少有人知道wordpress內部使用了哪些開源組件。相應的,當出現漏洞的時候人們也只會認為這個漏洞是wordpress的漏洞。

慣性思維讓人們認為:『庫』里應該不會有漏洞,在代碼審計的時候也很少會關注import進來的第三方庫的代碼缺陷。所以,開源組件爆出的漏洞也較少。

能夠開發開源組件的開發者本身素質相對較高,代碼質量較高,也使開源組件出漏洞的可能性較小。

組件漏洞多半有爭議性,很多鍋分不清是組件自身的還是其使用者的,很多問題我們也只能稱其為『特性』,但實際上這些特性反而比某些漏洞更可怕。

特別是現在國內浮躁的安全氛圍,可以明顯感受到第一條原因。就前段時間出現的幾個影響較大的漏洞:Java反序列化漏洞、joomla的代碼執行、redis的寫ssh key,可以明顯感覺到后兩者炒的比前者要響,而前者不慍不火的,曝光了近一年才受到廣泛關注。
Java反序列化漏洞,恰好就是典型的『組件』特性造成的問題。早在2015年的1月28號,就有白帽子報告了利用Apache Commons Collections這個常用的Java庫來實現任意代碼執行的方法,但并沒有太多關注(原來國外也是這樣)。直到11月有人提出了用這個方法攻擊WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等應用的時候,才被突然炒起來。
這種對比明顯反應出『開源組件』和『開源應用』在安全漏洞關注度上的差距。
我個人在烏云上發過幾個組件漏洞,從前年發的ThinkPHP框架注入,到后面的Tornado文件讀取,到slimphp的XXE,基本都是我自己在使用完這些組件后,對整體代碼做code review的時候發現的。
這篇文章以一個例子,簡單地談談如何對第三方庫進行code review,與如何正確使用第三方庫。

WTForm中的弱validator

WTForms是python web開發中重要的一個組件,它提供了簡單的表單生成、驗證、轉換等功能,是眾多python web框架(特別是flask)不可缺少的輔助庫之一。
WTForms中有一個重要的功能就是對用戶輸入進行檢查,在文檔中被稱為validator:
http://wtforms.readthedocs.org/en/latest/validators.html

A validator simply takes an input, verifies it fulfills some criterion, such as a maximum length for a string and returns. Or, if the validation fails, raises a ValidationError. This system is very simple and flexible, and allows you to chain any number of validators on fields.

我們可以簡單地使用其內置validator對數據進行檢查,比如我們需要用戶輸入一個『不為空』、『最短10個字符』、『最長64個字符』的『URL地址』,那么我們就可以編寫如下class:

class MyForm(Form):
    url = StringField("Link", validators=[DataRequired(), Length(min=10, max=64), URL()])

以flask為例,在view視圖中只需調用validate()函數即可檢查用戶的輸入是否合法:

@app.route("/", methods=["POST"])
def check():
    form = MyForm(flask.request.form)
    if form.validate():
        pass # right input
    else:
        pass # bad input

典型的敏捷開發手段,減少了大量開發工作量。
但我自己在做code review的過程中發現,WTForms的內置validators并不可信,與其說是不可信,不如說在安全性上部分validator完全不起任何作用。
就拿上訴代碼為例子,這段代碼真的可以檢查用戶輸入的數據是否是一個『URL』么?我們看到wtforms.validators.URL()類:

class URL(Regexp):
    """
    Simple regexp based url validation. Much like the email validator, you
    probably want to validate the url later by other means if the url must
    resolve.

    :param require_tld:
        If true, then the domain-name portion of the URL must contain a .tld
        suffix.  Set this to false if you want to allow domains like
        `localhost`.
    :param message:
        Error message to raise in case of a validation error.
    """
    def __init__(self, require_tld=True, message=None):
        regex = r"^[a-z]+://(?P[^/:]+)(?P:[0-9]+)?(?P/.*)?$"
        super(URL, self).__init__(regex, re.IGNORECASE, message)
        self.validate_hostname = HostnameValidation(
            require_tld=require_tld,
            allow_ip=True,
        )

    def __call__(self, form, field):
        message = self.message
        if message is None:
            message = field.gettext("Invalid URL.")

        match = super(URL, self).__call__(form, field, message)
        if not self.validate_hostname(match.group("host")):
            raise ValidationError(message)

其繼承了Rexexp類,實際上就是對用戶輸入進行正則匹配。我們看到它的正則:

regex = r"^[a-z]+://(?P[^/:]+)(?P:[0-9]+)?(?P/.*)?$"

可見,這個正則與開發者理解的URL嚴重的不匹配。大部分的開發者希望獲得的URL是一個『HTTP網址』,但這個正則匹配到的卻寬泛的太多了,最大特點就是其可匹配任意protocol。
最容易想到的一個攻擊方式就是利用Javascript協議觸發的XSS,比如我傳入的url是

javascript://...xss code

WTForms將認為這是一個合法的URL,并存入數據庫。而在業務邏輯中URL通常是輸出在超鏈接的href屬性中,而href屬性支持利用Javascript偽協議執行JavaScript代碼。那么,這里就有極大的可能構造一個XSS攻擊。
另一個草草編寫的validator是wtforms.validators.Email()類,查看其代碼:

class Email(Regexp):
    """
    Validates an email address. Note that this uses a very primitive regular
    expression and should only be used in instances where you later verify by
    other means, such as email activation or lookups.

    :param message:
        Error message to raise in case of a validation error.
    """
    def __init__(self, message=None):
        self.validate_hostname = HostnameValidation(
            require_tld=True,
        )
        super(Email, self).__init__(r"^.+@([^.@][^@]+)$", re.IGNORECASE, message)

    def __call__(self, form, field):
        message = self.message
        if message is None:
            message = field.gettext("Invalid email address.")

        match = super(Email, self).__call__(form, field, message)
        if not self.validate_hostname(match.group(1)):
            raise ValidationError(message)

看看他的正則^.+@([^.@][^@]+)$,這個正則根本無法檢測用戶的輸入是否是Email。最前面的.+就讓一切壞字符全進入了數據庫。
所以我私下稱URL()和Email()為URL Finder和Email Finder,而非validator,因為他們根本無法驗證用戶輸入,倒是更適合作為爬蟲查找目標的finder。

利用弱validator構造XSS

這個漏洞實際上是出現在我寫的某個網站中。這個網站允許訪客輸入其博客地址,而后臺使用URL()對地址的合法性進行驗證,在用戶主頁其他用戶可以點擊其頭像訪問博客。
整個過程如下: https://gist.github.com/phith0n/807869afbe1365015627

#(?ˉωˉ?) coding:utf8 (?ˉωˉ?)
import os
import flask
from flask import Flask
from wtforms.form import Form
from wtforms.validators import DataRequired, URL
from wtforms import StringField
app = Flask(__name__)

class UrlForm(Form):
    url = StringField("Link", validators=[DataRequired(), URL()])

@app.route("/", methods=["GET", "POST"])
def show_data():
    form = UrlForm(flask.request.form)
    if flask.request.method == "POST" and form.validate():
        url = form.url.data
    else:
        url = flask.request.url
    return flask.render_template("form.html", url=url, form=form)

if __name__ == "__main__":
    app.debug = False
    app.run(os.getenv("IP", "0.0.0.0"), int(os.getenv("PORT", 8080)))


    
        
        test
    
    
        

{% if form.url.errors %} {{ form.url.errors|join(" ") }} {% endif %}

your input url {{ url }}

demo頁面: https://flask-form-phith0n.c9users.io/ 可供測試。
那么,這段代碼存在漏洞嗎?回顧URL的正則:

regex = r"^[a-z]+://(?P[^/:]+)(?P:[0-9]+)?(?P/.*)?$"
super(URL, self).__init__(regex, re.IGNORECASE, message)

有個//,實在討厭,將后面的內容全部注釋掉了,導致我不能直接執行JavaScript。繞過方法也簡單,因為//是單行注釋,所以只需換行即可。
但這里正則修飾符是re.IGNORECASE,并沒有re.S,這就導致一旦出現換行這個正則將不再匹配。
不過這個問題很快也有了答案,在JavaScript中,可以代表換行的字符有 u2028和u2029,而在正則里換行僅僅是 ,所以我只要通過u2028或u2029這兩個字符代替換行即可。(u2028的url編碼為%E2%80%A8)
所以,傳入url如下即可:

javascript://www.baidu.com/?alert(1)

輸入以上url,提交后點擊鏈接即可觸發:

這個漏洞很典型,任何開發者都不會想到如此平凡的一段代碼竟然隱藏著深層次的威脅。
有些人可能會覺得我這個demo并不能說明實際問題,我簡單翻了一下github,不到5分鐘就找到了一個存在同樣問題的項目: https://github.com/1jingdian/1jingdian 。(雖然其站點已經關閉,但代碼可以瀏覽)

https://github.com/1jingdian/1jingdian/blob/master/application/forms/user.py

class SettingsForm(Form):
    motto = StringField("座右銘")
    blog = StringField("博客", validators=[Optional(), URL(message="鏈接格式不正確")])
    weibo = StringField("微博", validators=[Optional(), URL(message="鏈接格式不正確")])
    douban = StringField("豆瓣", validators=[Optional(), URL(message="鏈接格式不正確")])
    zhihu = StringField("知乎", validators=[Optional(), URL(message="鏈接格式不正確")])

這里4個鏈接,全是用URL()來進行驗證。validate()通過后存入數據庫。
之后在個人頁面,提取出用戶信息傳入模板user/profile.html
https://github.com/1jingdian/1jingdian/blob/master/application/controllers/user.py#L14

def profile(uid, page):
    user = User.query.get_or_404(uid)
    votes = user.voted_pieces.paginate(page, 20)
    return render_template("user/profile.html", user=user, votes=votes)

跟進一下profile.html
https://github.com/1jingdian/1jingdian/blob/master/application/templates/user/profile.html

{% from "macros/_user.html" import render_user_profile_header %}
...
{{ render_user_profile_header(user, active="votes") }}

調用了marco,傳入render_user_profile_header函數,繼續跟進:
https://github.com/1jingdian/1jingdian/blob/master/application/templates/macros/_user.html#L37

{% macro render_user_profile_header(user, active="creates") %}
   ...
         
{% if user.blog %} {% endif %} {% if user.weibo %} {% endif %} {% if user.douban %} {% endif %}

這里將user.blog、user.weibo、user.douban都放入了a標簽的href屬性。這一系列操作實際上就是我之前那個demo的縮影,最終導致傳入的url過濾不嚴產生XSS。

開源組件漏洞到底是誰的鍋?

這是屢次受到爭議的話題之一,很多人認為開源組件之所以造成了漏洞,都是因為開發者不規范使用組件導致的。
我覺得認定一個問題是開源組件的鍋,那么必須滿足以下條件:

開發者按照文檔常規的方法進行開發

文檔并沒有說明如此開發會存在什么安全問題

同樣的開發方式在其他同類組件中沒有漏洞,而在該組件中產生漏洞

舉幾個例子,這個漏洞: http://www.wooyun.org/bugs/wooyun-2015-0101728 。首先滿足第一個條件,正常使用S函數。當然文檔中也對安全進行了說明:

但這個說明,我覺得是不夠的。你『可以』設置..參數,避免緩存文件名『被猜測到』。文檔并沒有說明緩存文件名被猜測到有什么危害,也沒有強制要求設置這個參數。所以這個鍋,官方至少背一半。
再舉個例子: http://www.wooyun.org/bugs/wooyun-2010-0156208 ,很明顯的一個框架鍋,開發者在正常接收POST參數的時候就可以造成XXE漏洞,這個漏洞和開發者是沒有任何關系的。
另一個例子: http://www.wooyun.org/bugs/wooyun-2010-086742 ,我們通過修改邏輯運算符改變開發者正常的判斷流程,造成安全問題。我們對比一下ThinkPHP和Codeigniter,CI中對于邏輯運算符的位置就和TP不相同,它在『key』的位置:

正常情況下key位置是不會被用戶控制的。所以,同樣的開發方式在CI里不存在問題,而在TP里就存在問題,這樣的地方我認為也是ThinkPHP的鍋。
我們看本文提出的WTForm的問題,這個鍋其實WTForm可以不用獨自背。我們在文檔中,可以看到它有模模糊糊地提到過validater不嚴謹的問題:

當然,這個模糊的提示對于很多沒有安全基礎的人來說,很難起到作用。

開發者如何應對潛在的組件『安全特性』

那么,沒有安全基礎的開發者,如何去應對潛在的組件安全特性。
首先,我覺得經常做code review是很有必要的,我會經常把自己寫的代碼也當做一個開源應用進行閱讀與審計,此時會經常發現一些之前沒注意到過的安全問題。
code review的過程中,要深入地跟進一下第三方庫的源代碼,而不能僅僅是看自己寫的代碼,這樣才能發現一些潛在的特性。這些特性往往是造成漏洞的罪魁禍首。
另外,文檔的閱讀能力也是極其重要的一點。其實大量的『框架特性』,框架文檔中都有一定的說明。很多開發者更喜歡去看example,覺得看代碼比看文字(也許與英文閱讀能力也有關系)更直觀,而不愿詳細閱讀說明。這種做法實際上在安全上是非常危險的,因為示例代碼通常都是官方給出的最簡陋的代碼,可能會忽略很多必要的安全措施。
另外,具備一定的安全基礎是每個開發必要的素質,原因不必贅述。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37763.html

相關文章

  • flask + wtform + google storage

    摘要:項目需要使用上傳下載文件到上,搜了一圈沒有能直接結合使用的插件,所以動手造了個輪子。只實現了基本的上傳,下載的功能,后續可能會完善預覽權限控制等功能。然后在中使用該并傳遞給存到最后像使用普通的一樣在中即可。 項目需要使用 flask 上傳、下載文件到 google storage 上, 搜了一圈沒有能直接結合 wtform 使用的插件,所以動手造了個輪子。 只實現了基本的上傳,下載的功...

    Cristic 評論0 收藏0
  • 獨家專訪阿里高級技術專家北緯:Dubbo開源重啟半年來快意江湖

    摘要:年,阿里巴巴在上開源,許多開發者及公司都青睞于使用來解決服務化問題。首先阿里巴巴將開源提到了新的戰略高度,去年云棲大會上阿里云宣布了加大技術投入擁抱開源的策略。自去年開源重啟以來,上的數增長接近,達到了。 摘要: 羅毅,花名北緯。這個名字,如果是混過天涯論壇的大齡網民應該都不陌生,北緯67度3分周公子(簡稱北緯)虐殺易燁卿MM的世紀大戰至今還是天涯神貼 ,當時更是有看客賦詩形容:目睹此...

    Sanchi 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<