CVE-2025-58067
Description
Basecamp's Google Sign-In adds Google sign-in to Rails applications. Prior to version 1.3.1, it is possible to redirect a user to another origin if the "proceed_to" value in the session store is set to a protocol-relative URL. Normally the value of this URL is only written and read by the library or the calling application. However, it may be possible to set this session value from a malicious site with a form submission. Any Rails applications using the google_sign_in gem may be vulnerable, if this vector can be chained with another attack that is able to modify the OAuth2 request parameters. This issue has been patched in version 1.3.1. There are no workarounds.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
google_sign_inRubyGems | < 1.3.1 | 1.3.1 |
Affected products
1Patches
2e7f2a9ab59feversion bump to v1.3.1
8 files changed · +8 −16
Gemfile.lock+1 −1 modified@@ -1,7 +1,7 @@ PATH remote: . specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0)
gemfiles/rails_6.1.gemfile.lock+1 −1 modified@@ -1,7 +1,7 @@ PATH remote: .. specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0)
gemfiles/rails_7.0.gemfile.lock+1 −2 modified@@ -1,7 +1,7 @@ PATH remote: .. specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0) @@ -247,7 +247,6 @@ DEPENDENCIES byebug google_sign_in! jwt (>= 1.5.6) - mutex_m rails (~> 7.0) rake webmock (>= 3.4.2)
gemfiles/rails_7.1.gemfile.lock+1 −2 modified@@ -1,7 +1,7 @@ PATH remote: .. specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0) @@ -247,7 +247,6 @@ DEPENDENCIES byebug google_sign_in! jwt (>= 1.5.6) - mutex_m rails (~> 7.1) rake webmock (>= 3.4.2)
gemfiles/rails_7.2.gemfile.lock+1 −3 modified@@ -1,7 +1,7 @@ PATH remote: .. specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0) @@ -137,7 +137,6 @@ GEM minitest (5.25.5) multi_xml (0.7.2) bigdecimal (~> 3.1) - mutex_m (0.3.0) net-http (0.6.0) uri net-imap (0.5.9) @@ -242,7 +241,6 @@ DEPENDENCIES byebug google_sign_in! jwt (>= 1.5.6) - mutex_m rails (~> 7.2) rake webmock (>= 3.4.2)
gemfiles/rails_8.0.gemfile.lock+1 −3 modified@@ -1,7 +1,7 @@ PATH remote: .. specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0) @@ -137,7 +137,6 @@ GEM minitest (5.25.5) multi_xml (0.7.2) bigdecimal (~> 3.1) - mutex_m (0.3.0) net-http (0.6.0) uri net-imap (0.5.9) @@ -242,7 +241,6 @@ DEPENDENCIES byebug google_sign_in! jwt (>= 1.5.6) - mutex_m rails (~> 8.0) rake webmock (>= 3.4.2)
gemfiles/rails_edge.gemfile.lock+1 −3 modified@@ -102,7 +102,7 @@ GIT PATH remote: .. specs: - google_sign_in (1.3.0) + google_sign_in (1.3.1) google-id-token (>= 1.4.0) oauth2 (>= 1.4.0) rails (>= 6.1.0) @@ -169,7 +169,6 @@ GEM minitest (5.25.5) multi_xml (0.7.2) bigdecimal (~> 3.1) - mutex_m (0.3.0) net-http (0.6.0) uri net-imap (0.5.9) @@ -253,7 +252,6 @@ DEPENDENCIES byebug google_sign_in! jwt (>= 1.5.6) - mutex_m rails! rake webmock (>= 3.4.2)
google_sign_in.gemspec+1 −1 modified@@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'google_sign_in' - s.version = '1.3.0' + s.version = '1.3.1' s.authors = ['David Heinemeier Hansson', 'George Claghorn'] s.email = ['david@basecamp.com', 'george@basecamp.com'] s.summary = 'Sign in (or up) with Google for Rails applications'
e97aef4626b1Merge pull request #75 from basecamp/flavorjones/handle-invalid-paths
3 files changed · +89 −8
lib/google_sign_in/redirect_protector.rb+14 −7 modified@@ -9,20 +9,27 @@ class Violation < StandardError; end QUALIFIED_URL_PATTERN = /\A#{URI::DEFAULT_PARSER.make_regexp}\z/ def ensure_same_origin(target, source) - if (target =~ QUALIFIED_URL_PATTERN && origin_of(target) == origin_of(source)) || - target =~ URI::DEFAULT_PARSER.regexp[:ABS_PATH] - return + unless uri_same_origin?(target, source) || absolute_path?(target) + raise Violation, "Redirect target #{target.inspect} does not have same origin as request #{source.inspect}" end - - raise Violation, "Redirect target #{target.inspect} does not have same origin as request (expected #{origin_of(source)})" end private + def uri_same_origin?(target, source) + target =~ QUALIFIED_URL_PATTERN && origin_of(target) == origin_of(source) + rescue ArgumentError, URI::Error + false + end + + def absolute_path?(target) + target =~ URI::DEFAULT_PARSER.regexp[:ABS_PATH] && URI(target).host.nil? && !target.start_with?("//") + rescue ArgumentError, URI::Error + false + end + def origin_of(url) uri = URI(url) "#{uri.scheme}://#{uri.host}:#{uri.port}" - rescue ArgumentError - nil end end end
test/controllers/callbacks_controller_test.rb+42 −0 modified@@ -103,6 +103,48 @@ class GoogleSignIn::CallbacksControllerTest < ActionDispatch::IntegrationTest assert_response :bad_request end + test "protecting against open redirects given a malformed URI" do + post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com\n\r@\n\revil.example.org/login' } + assert_response :redirect + + get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state]) + assert_response :bad_request + end + + test "rejects proceed_to paths if they are relative" do + post google_sign_in.authorization_url, params: { proceed_to: 'login' } + assert_response :redirect + + get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state]) + assert_response :bad_request + end + + test "accepts proceed_to paths if they are absolute" do + post google_sign_in.authorization_url, params: { proceed_to: '/login' } + assert_response :redirect + + stub_token_for '4/SgCpHSVW5-Cy', access_token: 'ya29.GlwIBo', id_token: 'eyJhbGciOiJSUzI' + + get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state]) + assert_redirected_to 'http://www.example.com/login' + end + + test "protecting against open redirects given a double-slash net path" do + post google_sign_in.authorization_url, params: { proceed_to: '//evil.example.org' } + assert_response :redirect + + get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state]) + assert_response :bad_request + end + + test "protecting against open redirects given a triple-slash net path" do + post google_sign_in.authorization_url, params: { proceed_to: '///evil.example.org' } + assert_response :redirect + + get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state]) + assert_response :bad_request + end + test "receiving no proceed_to URL" do get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: 'invalid') assert_response :bad_request
test/models/redirect_protector_test.rb+33 −1 modified@@ -44,9 +44,41 @@ class GoogleSignIn::RedirectProtectorTest < ActiveSupport::TestCase end end - test "allows path target" do + test "disallows relative path target" do + assert_raises GoogleSignIn::RedirectProtector::Violation do + GoogleSignIn::RedirectProtector.ensure_same_origin 'callback', 'https://basecamp.com' + end + end + + test "allows absolute path target" do assert_nothing_raised do GoogleSignIn::RedirectProtector.ensure_same_origin '/callback', 'https://basecamp.com' end end + + test "disallows double-slash path target" do + assert_raises GoogleSignIn::RedirectProtector::Violation do + GoogleSignIn::RedirectProtector.ensure_same_origin '//evil.example.org', 'https://basecamp.com' + end + end + + test "disallows triple-slash path target" do + assert_raises GoogleSignIn::RedirectProtector::Violation do + GoogleSignIn::RedirectProtector.ensure_same_origin '///evil.example.org', 'https://basecamp.com' + end + end + + test "disallows invalid paths" do + assert_raises GoogleSignIn::RedirectProtector::Violation do + GoogleSignIn::RedirectProtector.ensure_same_origin '/a path with spaces is invalid', 'https://basecamp.com' + end + + assert_raises GoogleSignIn::RedirectProtector::Violation do + GoogleSignIn::RedirectProtector.ensure_same_origin '/path#with-fragment', 'https://basecamp.com' + end + + assert_raises GoogleSignIn::RedirectProtector::Violation do + GoogleSignIn::RedirectProtector.ensure_same_origin '/path?with=query', 'https://basecamp.com' + end + end end
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-5jch-xhw4-r43vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-58067ghsaADVISORY
- github.com/basecamp/google_sign_in/commit/e97aef4626b1bcbd2c6f01f7dd25f12ac855d4ccnvdWEB
- github.com/basecamp/google_sign_in/pull/75nvdWEB
- github.com/basecamp/google_sign_in/releases/tag/v1.3.1nvdWEB
- github.com/basecamp/google_sign_in/security/advisories/GHSA-5jch-xhw4-r43vnvdWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/google_sign_in/CVE-2025-58067.ymlghsaWEB
News mentions
0No linked articles in our index yet.