FIX: Return 422 instead of 500 for invalid SSO signature (#6738)

This commit is contained in:
David Taylor 2018-12-07 15:01:44 +00:00 committed by GitHub
parent 6c71395bf6
commit f7ce607e5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 4 deletions

View File

@ -444,7 +444,11 @@ class Admin::UsersController < Admin::AdminController
def sync_sso
return render body: nil, status: 404 unless SiteSetting.enable_sso
sso = DiscourseSingleSignOn.parse("sso=#{params[:sso]}&sig=#{params[:sig]}")
begin
sso = DiscourseSingleSignOn.parse("sso=#{params[:sso]}&sig=#{params[:sig]}")
rescue DiscourseSingleSignOn::ParseError => e
return render json: failed_json.merge(message: I18n.t("sso.login_error")), status: 422
end
begin
user = sso.lookup_or_create_user

View File

@ -111,7 +111,17 @@ class SessionController < ApplicationController
params.require(:sso)
params.require(:sig)
sso = DiscourseSingleSignOn.parse(request.query_string)
begin
sso = DiscourseSingleSignOn.parse(request.query_string)
rescue DiscourseSingleSignOn::ParseError => e
if SiteSetting.verbose_sso_logging
Rails.logger.warn("Verbose SSO log: Signature parse error\n\n#{e.message}\n\n#{sso.diagnostics}")
end
# Do NOT pass the error text to the client, it would give them the correct signature
return render_sso_error(text: I18n.t("sso.login_error"), status: 422)
end
if !sso.nonce_valid?
if SiteSetting.verbose_sso_logging
Rails.logger.warn("Verbose SSO log: Nonce has already expired\n\n#{sso.diagnostics}")

View File

@ -1,5 +1,7 @@
class SingleSignOn
class ParseError < RuntimeError; end
ACCESSORS = %i{
add_groups
admin moderator
@ -61,9 +63,9 @@ class SingleSignOn
if sso.sign(parsed["sso"]) != parsed["sig"]
diags = "\n\nsso: #{parsed["sso"]}\n\nsig: #{parsed["sig"]}\n\nexpected sig: #{sso.sign(parsed["sso"])}"
if parsed["sso"] =~ /[^a-zA-Z0-9=\r\n\/+]/m
raise RuntimeError, "The SSO field should be Base64 encoded, using only A-Z, a-z, 0-9, +, /, and = characters. Your input contains characters we don't understand as Base64, see http://en.wikipedia.org/wiki/Base64 #{diags}"
raise ParseError, "The SSO field should be Base64 encoded, using only A-Z, a-z, 0-9, +, /, and = characters. Your input contains characters we don't understand as Base64, see http://en.wikipedia.org/wiki/Base64 #{diags}"
else
raise RuntimeError, "Bad signature for payload #{diags}"
raise ParseError, "Bad signature for payload #{diags}"
end
end

View File

@ -846,6 +846,19 @@ RSpec.describe Admin::UsersController do
expect(response.status).to eq(403)
expect(JSON.parse(response.body)["message"]).to include("Primary email can't be blank")
end
it 'should return the right message if the signature is invalid' do
sso.name = "Dr. Claw"
sso.username = "dr_claw"
sso.email = "dr@claw.com"
sso.external_id = "2"
correct_payload = Rack::Utils.parse_query(sso.payload)
post "/admin/users/sync_sso.json", params: correct_payload.merge(sig: "someincorrectsignature")
expect(response.status).to eq(422)
expect(JSON.parse(response.body)["message"]).to include(I18n.t('sso.login_error'))
expect(JSON.parse(response.body)["message"]).not_to include(correct_payload["sig"])
end
end
describe '#disable_second_factor' do

View File

@ -557,6 +557,26 @@ RSpec.describe SessionController do
expect(response.status).to eq(419)
end
it 'returns the correct error code for invalid signature' do
sso = get_sso('/hello/world')
sso.external_id = '997'
sso.sso_url = "http://somewhere.over.com/sso_login"
correct_params = Rack::Utils.parse_query(sso.payload)
get "/session/sso_login", params: correct_params.merge("sig": "thisisnotthesigyouarelookingfor"), headers: headers
expect(response.status).to eq(422)
expect(response.body).not_to include(correct_params["sig"]) # Check we didn't send the real sig back to the client
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
expect(logged_on_user).to eq(nil)
correct_params = Rack::Utils.parse_query(sso.payload)
get "/session/sso_login", params: correct_params.merge("sig": "thisisasignaturewith@special!characters"), headers: headers
expect(response.status).to eq(422)
expect(response.body).not_to include(correct_params["sig"]) # Check we didn't send the real sig back to the client
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
expect(logged_on_user).to eq(nil)
end
describe 'local attribute override from SSO payload' do
before do
SiteSetting.email_editable = false