django-增加功能-百度推送工具開發

娃哈哈店長發表於2020-01-19

百度站長平臺官方提供的推送介面 工具的作用是批次提交網站連結,推送給百度收錄網站連結 。

百度主動推送工具

增加文章的render:

在檢視函式中新增html對介面的對映。
tool/views.py:


# 百度主動推送
def BD_pushview(request):
    return render(request, 'tool/bd_push.html')

在前端中設計ui

tool/bd_push.html:

<div class="mb-3">
    <div class="form-group">
        <label>介面呼叫地址:</label>
        <input type="url" class="form-control rounded-0" id="form-url"
               placeholder="例:http://data.zz.baidu.com/urls?site=www.example.com&token=NpU0****tTQAlBV"
               required>
        <small class="form-text text-muted">注:請填寫百度站長平臺提供給您的介面呼叫地址</small>
    </div>
    <div class="form-group">
        <label>網址連結:</label>
        <textarea class="form-control rounded-0" id="form-urls" rows="5" required
                  placeholder="示例如下:&#10;http://www.example.com/mip/&#10;http://www.example.com/mip/1.html&#10;http://www.example.com/mip/2.php"></textarea>
        <small class="form-text text-muted">注:提交的連結的域名必須與介面中site保持一直才能推送成功</small>
    </div>
    <button type="submit" class="btn btn-info btn-sm rounded-0" id="start-push">開始推送</button>
</div>

透過ajax將我們輸入的資訊來進行資料打包並傳送到後端

ajax
Ajax 即“Asynchronous Javascript And XML”(非同步 JavaScript 和 XML),是指一種建立互動式網頁應用的網頁開發技術。
Ajax = 非同步 JavaScript 和 XML 或者是 HTML(標準通用標記語言的子集)。
Ajax 是一種用於建立快速動態網頁的技術。
Ajax 是一種在無需重新載入整個網頁的情況下,能夠更新部分網頁的技術。
透過在後臺與伺服器進行少量資料交換,Ajax 可以使網頁實現非同步更新。這意味著可以在不重新載入整個網頁的情況下,對網頁的某部分進行更新。
傳統的網頁(不使用 Ajax)如果需要更新內容,必須過載整個網頁頁面。

當然我們需要對傳入的資料進行簡單的空值判斷:

var url = $('#form-url').val();
    var urls = $('#form-urls').val();
    if (url.length == 0 | urls.length == 0) {
        alert('介面地址和網址連結內容都不能為空!');
        return false
    };

呼叫csrf格式的引用:

$.ajaxSetup({
        data: {
            csrfmiddlewaretoken: CSRF
        }
    });

csrfCSRF跨站點請求偽造(Cross—Site Request Forgery)

其實對於csrf相信大家使用django的時候應該不會陌生。
在django教程中是這樣說的

CSRF
背景知識:瀏覽器在傳送請求的時候,會自動帶上當前域名對應的cookie內容,傳送給服務端,不管這個請求是來源A網站還是其它網站,只要請求的是A網站的連結,就會帶上A網站的cookie。瀏覽器的同源策略並不能阻止CSRF攻擊,因為瀏覽器不會停止js傳送請求到服務端,只是在必要的時候攔截了響應的內容。或者說瀏覽器收到響應之前它不知道該不該拒絕。

攻擊過程:使用者登陸A網站後,攻擊者自己開發一個B網站,這個網站會透過js請求A網站,比如使用者點選了某個按鈕,就觸發了js的執行。

預防:

  • Double Submit Cookie

攻擊者是利用cookie隨著http請求傳送的特性來攻擊。但攻擊都不知道 cookie裡面是什麼。

Django中是在表單中加一個隱藏的 csrfmiddlewaretoken,在提交表單的時候,會有 cookie 中的內容做比對,一致則認為正常,不一致則認為是攻擊。由於每個使用者的 token 不一樣,B網站上的js程式碼無法猜出token內容,對比必然失敗,所以可以起到防範作用。

  • Synchronizer Token

和上面的類似,但不使用 cookie,服務端的資料庫中儲存一個 session_csrftoken,表單提交後,將表單中的 token 和 session 中的對比,如果不一致則是攻擊。

這個方法實施起來並不困難,但它更安全一些,因為網站即使有 xss 攻擊,也不會有洩露token的問題。

通縮的說也就是:
django 第一次響應來自某個客戶端的請求時,會在伺服器端隨機生成一個 token,把這個 token 放在 cookie 裡。然後每次 POST 請求都會帶上這個 token,
這樣就能避免被 CSRF 攻擊。

這樣子看起來似乎沒毛病,但是評論中的第三個問題,每次重新整理頁面,form表單中的token都會重新整理,而cookie中的token卻只在每次登入時重新整理。我對csrftoken的驗證方式起了疑問,後來看了一段官方文件的解釋。

When validating the ‘csrfmiddlewaretoken’ field value, only the secret, not the full token, is compared with the secret in the cookie value. This allows the use of ever-changing tokens. While each request may use its own token, the secret remains common to all.
This check is done by CsrfViewMiddleware.

官方文件中說到,檢驗token時,只比較secret是否和cookie中的secret值一樣,而不是比較整個token。
我又有疑問了,同一次登入,form表單中的token每次都會變,而cookie中的token不便,django把那個salt儲存在哪裡才能保證驗證透過呢。直到看到原始碼。

csrf案例分析

受害者 Bob 在銀行有一筆存款,透過對銀行的網站傳送請求 http://bank.example/withdraw?account=bob&a... 可以使 Bob 把 1000000 的存款轉到 bob2 的賬號下。通常情況下,該請求傳送到網站後,伺服器會先驗證該請求是否來自一個合法的 session,並且該 session 的使用者 Bob 已經成功登陸。

    駭客 Mallory 自己在該銀行也有賬戶,他知道上文中的 URL 可以把錢進行轉帳操作。Mallory 可以自己傳送一個請求給銀行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是這個請求來自 Mallory 而非 Bob,他不能透過安全認證,因此該請求不會起作用。

    這時,Mallory 想到使用 CSRF 的攻擊方式,他先自己做一個網站,在網站中放入如下程式碼: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,並且透過廣告等誘使 Bob 來訪問他的網站。當 Bob 訪問該網站時,上述 url 就會從 Bob 的瀏覽器發向銀行,而這個請求會附帶 Bob 瀏覽器中的 cookie 一起發向銀行伺服器。大多數情況下,該請求會失敗,因為他要求 Bob 的認證資訊。但是,如果 Bob 當時恰巧剛訪問他的銀行後不久,他的瀏覽器與銀行網站之間的 session 尚未過期,瀏覽器的 cookie 之中含有 Bob 的認證資訊。這時,悲劇發生了,這個 url 請求就會得到響應,錢將從 Bob 的賬號轉移到 Mallory 的賬號,而 Bob 當時毫不知情。等以後 Bob 發現賬戶錢少了,即使他去銀行查詢日誌,他也只能發現確實有一個來自於他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而 Mallory 則可以拿到錢後逍遙法外。 
def _compare_salted_tokens(request_csrf_token, csrf_token):
    # Assume both arguments are sanitized -- that is, strings of
    # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS.
    return constant_time_compare(
        _unsalt_cipher_token(request_csrf_token),
        _unsalt_cipher_token(csrf_token),
    )

def _unsalt_cipher_token(token):
    """
    Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length
    CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt
    the second half to produce the original secret.
    """
    salt = token[:CSRF_SECRET_LENGTH]
    token = token[CSRF_SECRET_LENGTH:]
    chars = CSRF_ALLOWED_CHARS
    pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt))
    secret = ''.join(chars[x - y] for x, y in pairs)  # Note negative values are ok
    return secret

token字串的前32位是salt, 後面是加密後的token, 透過salt能解密出唯一的secret。
django會驗證表單中的token和cookie中token是否能解出同樣的secret,secret一樣則本次請求合法。
同樣也不難解釋,為什麼ajax請求時,需要從cookie中拿取token新增到請求頭中。

增加tool.js檔案程式碼,對文內容id為form-url和form-urls的部分進行資料獲取,並透過post的方式將資料進行ajax打包傳送。並編輯簡單的回撥資訊,當資訊傳輸成功的時候,顯示$('.push-result').html(ret.msg);

//baidu links push api
function push_spider(CSRF, URL) {
    var url = $('#form-url').val();
    var urls = $('#form-urls').val();
    if (url.length == 0 | urls.length == 0) {
        alert('介面地址和網址連結內容都不能為空!');
        return false
    };

    $.ajaxSetup({
        data: {
            csrfmiddlewaretoken: CSRF
        }
    });
    $('.push-result').html('<i class="fa fa-spinner fa-pulse fa-3x"></i>');
    $.ajax({
        type: 'post',
        url: URL,
        data: {
            'url': url,
            'url_list': urls
        },
        dataType: 'json',
        success: function(ret) {
            $('.push-result').html(ret.msg);
        },
    })
}
}

在tool中透過id="form-url"進行選擇獲取百度站長平臺提供的介面呼叫地址 id="form-urls"獲取要提交的連結地址。對資料進行重組在透過呼叫。

在apis中編寫路由函式

編寫push_urls構造推送頭,對資料進行post,回去回撥資訊。

def push_urls(url, urls):
    '''根據百度站長提供的API推送連結'''
    headers = {
        'User-Agent': 'curl/7.12.1',
        'Host': 'data.zz.baidu.com',
        'Content - Type': 'text / plain',
        'Content - Length': '83'
    }
    try:
        html = requests.post(url, headers=headers, data=urls, timeout=5).text
        return html
    except:
        return "{'error':404,'message':'請求超時,介面地址錯誤!'}"

對上述重組的資料進行post,函式呼叫:


from .apis.bd_push import push_urls

# 百度主動推送
def BD_pushview(request):
    return render(request, 'tool/bd_push.html')

@require_POST
def bd_api_view(request):
    if request.is_ajax():
        data = request.POST
        url = data.get('url')
        urls = data.get('url_list')
        info = push_urls(url, urls)
        return JsonResponse({'msg': info})
    return JsonResponse({'msg': 'miss'})

透過view檢視函式呼叫路由api。

工具的來源剛開始是在https://tendcode.com/中,這篇文章簡單的解析一些功能的實現流程,和開發步驟。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
文章!!首發於我的部落格Stray_Camel(^U^)ノ~YO

相關文章