python: require local protection to be absent

for CSRF to be likely
This commit is contained in:
Rasmus Lerchedahl Petersen
2022-03-22 13:42:52 +01:00
parent f5b53083ae
commit 0f2c21c8bd
4 changed files with 59 additions and 3 deletions

View File

@@ -106,7 +106,8 @@ module FileSystemWriteAccess {
} }
/** /**
* A data-flow node that may set or unset Cross-site request forgery protection. * A data-flow node that may set or unset Cross-site request forgery protection
* in a global manner.
* *
* Extend this class to refine existing API models. If you want to model new APIs, * Extend this class to refine existing API models. If you want to model new APIs,
* extend `CSRFProtectionSetting::Range` instead. * extend `CSRFProtectionSetting::Range` instead.
@@ -122,7 +123,8 @@ class CSRFProtectionSetting extends DataFlow::Node instanceof CSRFProtectionSett
/** Provides a class for modeling new CSRF protection setting APIs. */ /** Provides a class for modeling new CSRF protection setting APIs. */
module CSRFProtectionSetting { module CSRFProtectionSetting {
/** /**
* A data-flow node that may set or unset Cross-site request forgery protection. * A data-flow node that may set or unset Cross-site request forgery protection
* in a global manner.
* *
* Extend this class to model new APIs. If you want to refine existing API models, * Extend this class to model new APIs. If you want to refine existing API models,
* extend `CSRFProtectionSetting` instead. * extend `CSRFProtectionSetting` instead.
@@ -136,6 +138,39 @@ module CSRFProtectionSetting {
} }
} }
/**
* A data-flow node that provides Cross-site request forgery protection
* for a specific part of an application.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CSRFProtection::Range` instead.
*/
class CSRFProtection extends DataFlow::Node instanceof CSRFProtection::Range {
/**
* Gets a `Function` representing the protected interaction
* (probably a request handler).
*/
Function getProtected() { result = super.getProtected() }
}
/** Provides a class for modeling new CSRF protection setting APIs. */
module CSRFProtection {
/**
* A data-flow node that provides Cross-site request forgery protection
* for a specific part of an application.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `CSRFProtection` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets a `Function` representing the protected interaction
* (probably a request handler).
*/
abstract Function getProtected();
}
}
/** Provides classes for modeling path-related APIs. */ /** Provides classes for modeling path-related APIs. */
module Path { module Path {
/** /**

View File

@@ -2346,3 +2346,20 @@ module PrivateDjango {
} }
} }
} }
private class DjangoCSRFDecorator extends CSRFProtection::Range {
Function function;
DjangoCSRFDecorator() {
this =
API::moduleImport("django")
.getMember("views")
.getMember("decorators")
.getMember("csrf")
.getMember("csrf_protect")
.getAUse() and
this.asExpr() = function.getADecorator()
}
override Function getProtected() { result = function }
}

View File

@@ -15,5 +15,7 @@ import python
import semmle.python.Concepts import semmle.python.Concepts
from CSRFProtectionSetting s from CSRFProtectionSetting s
where s.getVerificationSetting() = false where
s.getVerificationSetting() = false and
not exists(CSRFProtection p)
select s, "Potential CSRF vulnerability due to forgery protection being disabled or weakened." select s, "Potential CSRF vulnerability due to forgery protection being disabled or weakened."

View File

@@ -1,5 +1,6 @@
from django.http.response import HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, JsonResponse, HttpResponseNotFound from django.http.response import HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, JsonResponse, HttpResponseNotFound
from django.views.generic import RedirectView from django.views.generic import RedirectView
from django.views.decorators.csrf import csrf_protect
import django.shortcuts import django.shortcuts
import json import json
@@ -117,6 +118,7 @@ class CustomJsonResponse(JsonResponse):
def __init__(self, banner, content, *args, **kwargs): def __init__(self, banner, content, *args, **kwargs):
super().__init__(content, *args, content_type="text/html", **kwargs) super().__init__(content, *args, content_type="text/html", **kwargs)
@csrf_protect
def safe__custom_json_response(request): def safe__custom_json_response(request):
return CustomJsonResponse("ACME Responses", {"foo": request.GET.get("foo")}) # $HttpResponse mimetype=application/json MISSING: responseBody=Dict SPURIOUS: responseBody="ACME Responses" return CustomJsonResponse("ACME Responses", {"foo": request.GET.get("foo")}) # $HttpResponse mimetype=application/json MISSING: responseBody=Dict SPURIOUS: responseBody="ACME Responses"