2014-10-18 110 views
7

我確實遇到了這個問題,在這種情況下,我不想跳過verify_authenticity_token過濾器,也不會更改爲protect_from_forgery with: :null_session無法驗證CSRF令牌的真實性Rails 4 Ajax即使設置了標頭時也是如此

在我的請求的方法,我設置的文件頭和CSRF令牌如下:

var token = document.querySelector("meta[name='csrf-token']").content; 
xhr.setRequestHeader("X-CSRF-Token", token); 

而且在我的控制器中插入一個斷點,像這樣:

def verify_authenticity_token 
    binding.pry 
    super 
end 

我已驗證標頭設置爲:

[1] pry(#<MyController>)> request.headers 
=> #<ActionDispatch::Http::Headers:0x007fb227cbf490 
@env= 
    {"CONTENT_LENGTH"=>"202", 
    . 
    . 
    . 
    # omitted headers 
    . 
    . 
    . 
    "HTTP_X_CSRF_TOKEN"=>"the-correct-token-from-meta-tag", 
    . 
    . 
    . 
    } 

我也試過將標記作爲參數傳遞給密鑰authenticity_token(與Rails表單一樣),並將X-CSRF-Param標記設置爲匹配(從meta[name="csrf-param"])。

但我仍然得到:

Can't verify CSRF token authenticity 
Completed 422 Unprocessable Entity in 14638ms 

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken 

沒有人見過這個?有什麼想法可能會導致這種情況?

在此先感謝!

編輯:

繼marflar的答案的意見討論,它看起來像當請求時令牌已過期(通過比較form_authenticity_token測試)。這使我更加困惑,因爲在下一個請求進入時,<%= csrf_meta_tags %>中設置的令牌已過期。有什麼想法?

EDIT2:按照下面marflar的建議下,我增加了以下after_filter到我的應用程序控制器:

def set_csrf_headers 
    response.headers['X-CSRF-Param'] = request_forgery_protection_token.to_s 
    response.headers['X-CSRF-Token'] = form_authenticity_token 
end 

而且我在我的請求方法更新xhr.onload如下:

namespace.request = = function (type, url, opts, callback) { 

// code omitted 

    xhr.onload = function() { 
    setCSRFHeaders(xhr); 
    var res = {data: JSON.parse(xhr.response), status: xhr.status}; 
    return callback.call(xhr, null, res); 
    }; 

// code omitted 

} 

function setCSRFHeaders (xhr) { 
    var csrf_param = xhr.getResponseHeader('X-CSRF-Param'); 
    var csrf_token = xhr.getResponseHeader('X-CSRF-Token'); 

    if (csrf_param) { 
    document.querySelector("meta[name='csrf-param']").content = csrf_param; 
    } 
    if (csrf_token) { 
    document.querySelector("meta[name='csrf-token']").content = csrf_token; 
    } 
} 

我驗證了響應標頭,然後元標記得到正確重置,但是,到下一個請求進入時,這個新的令牌會再次過期。思考?

回答

1

我的猜測是Rails可能期望令牌位於HTML中,而不是頭部。你可以試試嗎? 希望它有幫助。

其實我認爲你可能會使用一個陳舊的CRSF令牌,因爲你是從你的模板中複製它。

我通常設置它像這樣在我的控制器動作:

response.headers['X-CSRF-Param'] = "#{request_forgery_protection_token}" 
response.headers['X-CSRF-Token'] = "#{form_authenticity_token}" 

是否在你的頁面令牌匹配調用form_authenticity_token返回的一個?

UPDATE

在回答您的評論(引用如下):

我剛纔檢查和你說得對,它是一個陳舊的令牌,不幸的是讓我更糊塗了。具有CSRF數據的元標記在初始頁面加載時設置,這時它們與form_authenticity_token匹配,但在第一次發出ajax請求時該標記已過時。因此,將它們設置爲HTML還是作爲標題並不重要,因爲這會同時發生,因此會在下一個請求發生之前遇到與令牌過期相同的問題。感謝您的幫助至今 - 這裏的任何想法?

我在實現AJAX登錄時遇到了這樣的問題。我發現我無法登錄後,使任何類型的POST請求,而我需要下面的代碼刷新我的令牌:

var update_csrf_token_and_param_after_ajax_login = function() { 
    $(document).on("ajaxComplete", function(event, xhr, settings) { 
    var csrf_param = xhr.getResponseHeader('X-CSRF-Param'); 
    var csrf_token = xhr.getResponseHeader('X-CSRF-Token'); 

    if (csrf_param) { 
     $('meta[name="csrf-param"]').attr('content', csrf_param); 
    } 
    if (csrf_token) { 
     $('meta[name="csrf-token"]').attr('content', csrf_token); 
    } 
    }); 
} 

我想你可能只需要之前寫一個新的令牌到您的網頁做POST ...

+0

我不確定我是否理解你在'HTML'中的含義 - 你能給我一個例子嗎? – mattmattmatt 2014-10-18 21:35:56

+0

@fancycoder我更新了我的答案,我不知道你是否可能會使用陳舊的標記 – stephenmurdoch 2014-10-18 21:46:26

+0

我剛剛檢查過,你說的是一個陳舊的標記,這不幸讓我更加困惑。具有CSRF數據的元標記在初始頁面加載時設置,這時它們與'form_authenticity_token'匹配,但在第一個Ajax請求發出時該標記已過時。因此,將它們設置爲HTML還是作爲標題並不重要,因爲這會同時發生,因此會在下一個請求發生之前遇到與令牌過期相同的問題。感謝您的幫助至今 - 這裏的任何想法? – mattmattmatt 2014-10-18 22:02:08

1

我有同樣的問題。我檢查Rails的源代碼,並在下一個結論:

  • authenticity_token本身不是過期,所以無需在每次Ajax請求到服務器後,更新它
  • 不需要同時發送params[:authenticity_token]header['x-csrf-token'],只是一個他們的導軌將首先檢查params,而不是header
  • 頁面刷新,authenticity_token會不同,但它並不重要,因爲它每次都會產生一次性pad和真正的csrf令牌(在服務器上)是時間獨立的
  • real csrf標記已保存在session[:_csrf_token]

正如你可以看到令牌被保存在session,我的問題是,24小時(可能是用戶停留頁面上一天不刷新)

如果用戶是通過cookie的登錄後,我的會話已失效或其他一些令牌參數,無論如何他會得到新的會話,並且會生成新的CSRF令牌,並且舊的authenticity_token的任何請求都將無效。

所以,主要問題是會話,它已過期或重置。

相關問題