diff --git a/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml b/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml index fa1a77ebcc1..88cfb99a73e 100644 --- a/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml +++ b/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml @@ -1,4 +1,4 @@ --- title: Add CSRF token verification to API merge_request: 12154 -author: @blackst0ne +author: Vitaliy @blackst0ne Klachkov diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index f7fa6d1c2de..24ff3b924b5 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -16,7 +16,7 @@ OmniAuth.config.allowed_request_methods = [:post] # In case of auto sign-in, the GET method is used (users don't get to click on a button) OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? OmniAuth.config.before_request_phase do |env| - OmniAuth::RequestForgeryProtection.call(env) + GitLab::RequestForgeryProtection.call(env) end if Gitlab.config.omniauth.enabled diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index ab5f4c865e0..b81ce75ef4f 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -328,33 +328,6 @@ module API private - def xor_byte_strings(s1, s2) - s2_bytes = s2.bytes - s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 } - s2_bytes.pack('C*') - end - - # Check if CSRF tokens are equal. - # The header token is masked. - # So, before the comparison it must be unmasked. - def csrf_tokens_valid?(request) - session_token = request.session['_csrf_token'] - header_token = request.headers['X-Csrf-Token'] - - session_token = Base64.strict_decode64(session_token) - header_token = Base64.strict_decode64(header_token) - - # Decoded CSRF token passed from the frontend has to be 64 symbols long. - return false if header_token.size != 64 - - header_token = xor_byte_strings(header_token[0...32], header_token[32..-1]) - - ActiveSupport::SecurityUtils.secure_compare(session_token, header_token) - - rescue - false - end - def private_token params[APIGuard::PRIVATE_TOKEN_PARAM] || env[APIGuard::PRIVATE_TOKEN_HEADER] end @@ -363,10 +336,9 @@ module API env['warden'] end + # Check if CSRF tokens are valid. def verified_request? - request = Grape::Request.new(env) - - request.head? || request.get? || csrf_tokens_valid?(request) + GitLab::RequestForgeryProtection.call(env) end # Check the Rails session for valid authentication details diff --git a/lib/omni_auth/request_forgery_protection.rb b/lib/gitlab/request_forgery_protection.rb similarity index 66% rename from lib/omni_auth/request_forgery_protection.rb rename to lib/gitlab/request_forgery_protection.rb index 69155131d8d..071a72a1f8b 100644 --- a/lib/omni_auth/request_forgery_protection.rb +++ b/lib/gitlab/request_forgery_protection.rb @@ -1,6 +1,8 @@ -# Protects OmniAuth request phase against CSRF. +# A module to check CSRF tokens in requests. +# It's used in API helpers and OmniAuth. +# Usage: GitLab::RequestForgeryProtection.call(env) -module OmniAuth +module GitLab module RequestForgeryProtection class Controller < ActionController::Base protect_from_forgery with: :exception