python: minimal CSRF implementation

- currectly only looks for custom django middleware
This commit is contained in:
Rasmus Lerchedahl Petersen
2022-03-04 12:47:23 +01:00
parent 3fc2f2f3dc
commit 93750fe17f
5 changed files with 150 additions and 0 deletions

View File

@@ -105,6 +105,37 @@ module FileSystemWriteAccess {
} }
} }
/**
* A data-flow node that may set or unset Cross-site request forgery protection.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CSRFProtectionSetting::Range` instead.
*/
class CSRFProtectionSetting extends DataFlow::Node instanceof CSRFProtectionSetting::Range {
/**
* Gets the boolean value corresponding to if CSRF protection is enabled
* (`true`) or disabled (`false`) by this node.
*/
boolean getVerificationSetting() { result = super.getVerificationSetting() }
}
/** Provides a class for modeling new CSRF protection setting APIs. */
module CSRFProtectionSetting {
/**
* A data-flow node that may set or unset Cross-site request forgery protection.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `CSRFProtectionSetting` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the boolean value corresponding to if CSRF protection is enabled
* (`true`) or disabled (`false`) by this node.
*/
abstract boolean getVerificationSetting();
}
}
/** Provides classes for modeling path-related APIs. */ /** Provides classes for modeling path-related APIs. */
module Path { module Path {
/** /**

View File

@@ -2313,4 +2313,35 @@ module PrivateDjango {
.getAnImmediateUse() .getAnImmediateUse()
} }
} }
// ---------------------------------------------------------------------------
// Settings
// ---------------------------------------------------------------------------
/**
* A custom middleware stack
*/
private class DjangoSettingsMiddlewareStack extends CSRFProtectionSetting::Range {
List list;
DjangoSettingsMiddlewareStack() {
this.asExpr() = list and
// we look for an assignment to the `MIDDLEWARE` setting
exists(DataFlow::Node mw, string djangomw |
mw.asVar().getName() = "MIDDLEWARE" and
DataFlow::localFlow(this, mw)
|
// check that the list contains at least one reference to `django`
list.getAnElt().(StrConst).getText() = djangomw and
// TODO: Consider requiring `django.middleware.security.SecurityMiddleware`
// or something indicating that a security middleware is enabled.
djangomw.matches("django.%")
)
}
override boolean getVerificationSetting() {
if list.getAnElt().(StrConst).getText() = "django.middleware.csrf.CsrfViewMiddleware"
then result = true
else result = false
}
}
} }

View File

@@ -0,0 +1,60 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Cross-site request forgery (CSRF) is a type of vulnerability in which an
attacker is able to force a user carry out an action that the user did
not intend.
</p>
<p>
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.
</p>
<p>
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.
</p>
</overview>
<recommendation>
<p>
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.
</p>
</recommendation>
<example>
<p>
The following example shows a case where CSRF protection is disabled by
overriding the default middleware stack and not including the one protecting against CSRF.
</p>
<sample src="examples/settings.py"/>
<p>
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 (or remove the custom middleware stack) will enable CSRF protection.
</p>
</example>
<references>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">Cross-site request forgery</a></li>
<li>OWASP: <a href="https://owasp.org/www-community/attacks/csrf">Cross-site request forgery</a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,19 @@
/**
* @name CSRF protection weakened or disabled
* @description Disabling or weakening CSRF protection may make the application
* vulnerable to a Cross-Site Request Forgery (CSRF) attack.
* @kind problem
* @problem.severity warning
* @security-severity 8.8
* @precision high
* @id py/csrf-protection-disabled
* @tags security
* external/cwe/cwe-352
*/
import python
import semmle.python.Concepts
from CSRFProtectionSetting s
where s.getVerificationSetting() = false
select s, "Potential CSRF vulnerability due to forgery protection being disabled or weakened."

View File

@@ -0,0 +1,9 @@
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',
]