allow 5 minutes of future clock skew when verifying JWTs

fixes CORE-1439

Change-Id: Ia3ed12dd79cee475bedd0879323eacf3a0325476
Reviewed-on: https://gerrit.instructure.com/155617
Tested-by: Jenkins
Reviewed-by: Brent Burgoyne <bburgoyne@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
QA-Review: Cody Cutrer <cody@instructure.com>
This commit is contained in:
Cody Cutrer 2018-06-28 11:12:37 -06:00
parent 66f225be3f
commit 4014a971e0
2 changed files with 18 additions and 10 deletions

View File

@ -329,27 +329,29 @@ module Canvas::Security
class << self class << self
private private
def verify_jwt(body, ignore_expiration: false) def verify_jwt(body, ignore_expiration: false)
verification_time = Time.now.utc
if body[:iat].present?
iat = timestamp_as_integer(body[:iat])
if iat > verification_time.to_i && iat < verification_time.to_i + 300
verification_time = iat
end
end
if body[:exp].present? && !ignore_expiration if body[:exp].present? && !ignore_expiration
if timestamp_is_expired?(body[:exp]) if timestamp_as_integer(body[:exp]) < verification_time.to_i
raise Canvas::Security::TokenExpired raise Canvas::Security::TokenExpired
end end
end end
if body[:nbf].present? if body[:nbf].present?
if timestamp_is_future?(body[:nbf]) if timestamp_as_integer(body[:nbf]) > verification_time.to_i
raise Canvas::Security::InvalidToken raise Canvas::Security::InvalidToken
end end
end end
end end
def timestamp_is_expired?(exp_val) def timestamp_as_integer(timestamp)
now = Time.zone.now timestamp.is_a?(Time) ? timestamp.to_i : timestamp
(exp_val.is_a?(Time) && exp_val <= now) || exp_val <= now.to_i
end
def timestamp_is_future?(nbf_val)
now = Time.zone.now
(nbf_val.is_a?(Time) && nbf_val > now) || nbf_val > now.to_i
end end
def services_encryption_secret def services_encryption_secret

View File

@ -136,6 +136,12 @@ describe Canvas::Security do
) )
end end
it "allows 5 minutes of future clock skew" do
back_to_the_future_jwt = test_jwt(exp: 1.hour.from_now, nbf: 1.minutes.from_now, iat: 1.minutes.from_now)
body = Canvas::Security.decode_jwt(back_to_the_future_jwt, [ key ])
expect(body[:a]).to eq 1
end
it "produces an InvalidToken error if string isn't a jwt (even if it looks like one)" do it "produces an InvalidToken error if string isn't a jwt (even if it looks like one)" do
# this is an example token which base64_decodes to a thing that looks like a jwt because of the periods # this is an example token which base64_decodes to a thing that looks like a jwt because of the periods
not_a_jwt = Canvas::Security.base64_decode("1050~LvwezC5Dd3ZK9CR1lusJTRv24dN0263txia3KF3mU6pDjOv5PaoX8Jv4ikdcvoiy") not_a_jwt = Canvas::Security.base64_decode("1050~LvwezC5Dd3ZK9CR1lusJTRv24dN0263txia3KF3mU6pDjOv5PaoX8Jv4ikdcvoiy")