-
Notifications
You must be signed in to change notification settings - Fork 1.7k
python: minimal CSRF implementation #8340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- currectly only looks for custom django middleware
for CSRF to be relevant
for CSRF to be likely
|
Evaluation looks fine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
besides the in-line comments, can we move the new concepts to HTTP::Server? (that seems most appropriate)
| * Gets the boolean value corresponding to if CSRF protection is enabled | ||
| * (`true`) or disabled (`false`) by this node. | ||
| */ | ||
| boolean getVerificationSetting() { result = super.getVerificationSetting() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we benefit from this true/false result, or could it be a plain predicate csrfEnabled()? (a bit of a nitpick)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was copied from Ruby, but I think a predicate would work just as well and am happy to change it. We can unify now or once we try to share.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Then it would be good to align with ruby on this, since the 3-state of true/false/none() could be important for them (such as app.setCsrfEnabled(<unknown value>)) -- maybe that IS actually a good thing to be able to model from the get go. I think it is, so ignore this comment ✔️
| /** | ||
| * Gets a `Function` representing the protected interaction | ||
| * (probably a request handler). | ||
| */ | ||
| Function getProtected() { result = super.getProtected() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since request handlers can also be made exempt from CSRF with a decorator (see https://docs.djangoproject.com/en/4.0/ref/csrf/#django.views.decorators.csrf.csrf_exempt), I think it would be better if this concept would capture that the CSRF protection was changed, and whether it is now enabled/disabled.
This also means we need to change the predicate that gets the request handler(s) affected.
| /** | |
| * Gets a `Function` representing the protected interaction | |
| * (probably a request handler). | |
| */ | |
| Function getProtected() { result = super.getProtected() } | |
| /** | |
| * Gets a request handler whose CSRF protection is changed. | |
| */ | |
| Function getRequestHandler() { result = super.getProtected() } |
You initially said "probably a request handler", are there any cases where you would care to change CSRF protection that is NOT a request handler? 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good point, this concept should be broader.
I would expect that we only put such decorators on request handlers, but do we want to require that our request handler concept includes the function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect that we only put such decorators on request handlers, but do we want to require that our request handler concept includes the function?
No. I would expect that only a few request handlers have their CSRF settings manually changed, so I think it's fine to have it like this. (with the changes I suggested)
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
removing custom middleware stack will _not_ enable CSRF protection
|
QHelp previews: python/ql/src/Security/CWE-352/CSRFProtectionDisabled.qhelpCSRF protection weakened or disabledCross-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. RecommendationIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks. ExampleThe following example shows a case where CSRF protection is disabled by overriding the default middleware stack and not including the one protecting against CSRF. MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]The protecting middleware was probably commented out during a testing phase, when server-side token generation was not set up. Simply commenting it back in will enable CSRF protection. References
ruby/ql/src/queries/security/cwe-352/CSRFProtectionDisabled.qhelpCSRF protection weakened or disabledCross-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. RecommendationIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks. ExampleThe following example shows a case where CSRF protection is disabled by skipping token verification. class UsersController < ApplicationController
skip_before_action :verify_authenticity_token
endVerification can be re-enabled by removing the call to Care should be taken when using the Rails References
|
9c37873 to
d39410a
Compare
|
QHelp previews: python/ql/src/Security/CWE-352/CSRFProtectionDisabled.qhelpCSRF protection weakened or disabledCross-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. RecommendationIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks. ExampleThe following example shows a case where CSRF protection is disabled by overriding the default middleware stack and not including the one protecting against CSRF. MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]The protecting middleware was probably commented out during a testing phase, when server-side token generation was not set up. Simply commenting it back in will enable CSRF protection. References
ruby/ql/src/queries/security/cwe-352/CSRFProtectionDisabled.qhelpCSRF protection weakened or disabledCross-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. RecommendationIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks. ExampleThe following example shows a case where CSRF protection is disabled by skipping token verification. class UsersController < ApplicationController
skip_before_action :verify_authenticity_token
endVerification can be re-enabled by removing the call to Care should be taken when using the Rails References
|
|
force pushed to skip the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides an extended comment, and discussion about the value to use for @precision, this all looks good to me 👍
Explain why `TestScope` is not used. Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
|
QHelp previews: python/ql/src/Security/CWE-352/CSRFProtectionDisabled.qhelpCSRF protection weakened or disabledCross-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. RecommendationIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks. ExampleThe following example shows a case where CSRF protection is disabled by overriding the default middleware stack and not including the one protecting against CSRF. MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]The protecting middleware was probably commented out during a testing phase, when server-side token generation was not set up. Simply commenting it back in will enable CSRF protection. References
ruby/ql/src/queries/security/cwe-352/CSRFProtectionDisabled.qhelpCSRF protection weakened or disabledCross-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. RecommendationIn many web frameworks, CSRF protection is enabled by default. In these cases, using the default configuration is sufficient to guard against most CSRF attacks. ExampleThe following example shows a case where CSRF protection is disabled by skipping token verification. class UsersController < ApplicationController
skip_before_action :verify_authenticity_token
endVerification can be re-enabled by removing the call to Care should be taken when using the Rails References
|
at least all thos not in tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides the merge conflict, this LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found 2 vulnerabilities.
| if p.csrfEnabled() | ||
| then tag = "CsrfLocalProtectionEnabled" | ||
| else tag = "CsrfLocalProtectionDisabled" | ||
|
|
||
| class XmlParsingTest extends InlineExpectationsTest { | ||
| XmlParsingTest() { this = "XmlParsingTest" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this bit is not going to work after the merge 😳 my approval was just a second too fast 😂
caused by an optimistic attempt at solving a merge conflict in the online GUI.
Only handles django projects. Criteria are:
This is expressed via concepts, specifically the
CsrfProtectionSettingconcept is copied from Ruby to allow sharing down the road. I added theCsrfLocalProtectionconcept to tighten the query.