Skip to content

Conversation

smowton
Copy link
Contributor

@smowton smowton commented Sep 29, 2025

Autofix is confused about how the protect_from_forgery method works in Rails >= 5: GPT-5 says:

In modern Rails versions (>=5, including 6 and 7 which this gem permits), ActionController::Base already enables CSRF protection by default with the :exception strategy; an explicit call to protect_from_forgery without options does not weaken security.

This is false: manual testing confirms that it actually does downgrade from :exception to :null-session behaviour when a manual call is made.

I can't find any authoritative source showing this gotcha, so I can see how the AI is confused and how humans might also struggle to verify the truth.

Autofix is confused about how the `protect_from_forgery` method works in Rails >= 5: GPT-5 says:

> In modern Rails versions (>=5, including 6 and 7 which this gem permits), ActionController::Base already enables CSRF protection by default with the `:exception` strategy; an explicit call to `protect_from_forgery` without options does not weaken security.

This is false: manual testing confirms that it actually does downgrade from `:exception` to `:null-session` behaviour when a manual call is made.

I can't find any authoritative source showing this gotcha, so I can see how the AI is confused and how humans might also struggle to verify the truth.
@smowton smowton requested a review from a team as a code owner September 29, 2025 17:42
@Copilot Copilot AI review requested due to automatic review settings September 29, 2025 17:42
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates documentation for the CSRF protection disabled query to clarify how Rails' protect_from_forgery method behaves in version 5 and later. The change corrects a common misconception about the default behavior of manual protect_from_forgery calls.

Key Changes

  • Adds clarification that Rails 5+ defaults to :exception strategy for CSRF protection
  • Documents that manually calling protect_from_forgery without arguments downgrades to :null_session behavior
  • Addresses confusion about how explicit calls interact with Rails' automatic protection

Note this remains true even in Rails version 5 and later: these versions
automatically run <code>protect_from_forgery with: :exception</code>
by default, but manually calling <code>protect_from_forgery</code> with
no <code>with</code> argument will still downgrade protection to null the
Copy link
Preview

Copilot AI Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrase 'null the session' is grammatically incorrect. It should be 'nullify the session' or 'set the session to null'.

Copilot uses AI. Check for mistakes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this reads a bit weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded.

Copy link
Contributor

github-actions bot commented Sep 29, 2025

QHelp previews:

ruby/ql/src/queries/security/cwe-352/CSRFProtectionDisabled.qhelp

CSRF protection weakened or disabled

Cross-site request forgery (CSRF) is a type of vulnerability in which an attacker is able to force a user to carry out an action that the user did not intend.

The attacker tricks an authenticated user into submitting a request to the web application. Typically this request will result in a state change on the server, such as changing the user's password. The request can be initiated when the user visits a site controlled by the attacker. If the web application relies only on cookies for authentication, or on other credentials that are automatically included in the request, then this request will appear as legitimate to the server.

A common countermeasure for CSRF is to generate a unique token to be included in the HTML sent from the server to a user. This token can be used as a hidden field to be sent back with requests to the server, where the server can then check that the token is valid and associated with the relevant user session.

Recommendation

In many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks.

Example

The following example shows a case where CSRF protection is disabled by skipping token verification.

class UsersController < ApplicationController
  skip_before_action :verify_authenticity_token
end

Verification can be re-enabled by removing the call to skip_before_action.

Care should be taken when using the Rails protect_from_forgery method to prevent CSRF. The default behaviour of this method is to null the session when an invalid CSRF token is provided. This may not be sufficient to avoid a CSRF vulnerability - for example if parts of the session are memoized. Calling protect_from_forgery with: :exception can help to avoid this by raising an exception on an invalid CSRF token instead. Note this remains true even in Rails version 5 and later: these versions automatically run protect_from_forgery with: :exception by default, but manually calling protect_from_forgery with no with argument will still downgrade protection to provide an empty session rather than raise an exception.

References

ruby/ql/src/queries/security/cwe-352/CSRFProtectionNotEnabled.qhelp

CSRF protection not enabled

Cross-site request forgery (CSRF) is a type of vulnerability in which an attacker is able to force a user to carry out an action that the user did not intend.

The attacker tricks an authenticated user into submitting a request to the web application. Typically this request will result in a state change on the server, such as changing the user's password. The request can be initiated when the user visits a site controlled by the attacker. If the web application relies only on cookies for authentication, or on other credentials that are automatically included in the request, then this request will appear as legitimate to the server.

A common countermeasure for CSRF is to generate a unique token to be included in the HTML sent from the server to a user. This token can be used as a hidden field to be sent back with requests to the server, where the server can then check that the token is valid and associated with the relevant user session.

Recommendation

In the Rails web framework, CSRF protection is enabled by the adding a call to the protect_from_forgery method inside an ActionController class. Typically this is done in the ApplicationController class, or an equivalent class from which other controller classes are subclassed. The default behaviour of this method is to null the session when an invalid CSRF token is provided. This may not be sufficient to avoid a CSRF vulnerability - for example if parts of the session are memoized. Calling protect_from_forgery with: :exception can help to avoid this by raising an exception on an invalid CSRF token instead. Note that Rails versions 5 and later automatically run protect_from_forgery with: :exception by default, but manually calling protect_from_forgery with no with argument will downgrade protection to provide an empty session rather than raise an exception.

Example

The following example shows a case where CSRF protection is enabled with a secure request handling strategy of :exception.

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end
  

References

by raising an exception on an invalid CSRF token instead.

Note that Rails version 5 and later
automatically run <code>protect_from_forgery with: :exception</code>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run -> runs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed version -> versions instead

Note this remains true even in Rails version 5 and later: these versions
automatically run <code>protect_from_forgery with: :exception</code>
by default, but manually calling <code>protect_from_forgery</code> with
no <code>with</code> argument will still downgrade protection to null the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this reads a bit weird.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants