@@ -27,8 +27,13 @@ implications, please send a description of the issue via email to
27
27
team <https://www.djangoproject.com/foundation/teams/#security-team>`_.
28
28
29
29
Once you've submitted an issue via email, you should receive an acknowledgment
30
- from a member of the security team within 48 hours, and depending on the
31
- action to be taken, you may receive further followup emails.
30
+ from a member of the security team within 3 working days. After that, the
31
+ security team will begin their analysis. Depending on the action to be taken,
32
+ you may receive followup emails. It can take several weeks before the security
33
+ team comes to a conclusion. There is no need to chase the security team unless
34
+ you discover new, relevant information. All reports aim to be resolved within
35
+ the industry-standard 90 days. Confirmed vulnerabilities with a
36
+ :ref:`high severity level <severity-levels>` will be addressed promptly.
32
37
33
38
.. admonition:: Sending encrypted reports
34
39
@@ -38,6 +43,157 @@ action to be taken, you may receive further followup emails.
38
43
39
44
.. _our public Trac instance: https://code.djangoproject.com/query
40
45
46
+ Reporting guidelines
47
+ --------------------
48
+
49
+ Include a runnable proof of concept
50
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51
+
52
+ Please privately share a minimal Django project or code snippet that
53
+ demonstrates the potential vulnerability. Include clear instructions on how to
54
+ set up, run, and reproduce the issue.
55
+
56
+ Please do not attach screenshots of code.
57
+
58
+ User input must be sanitized
59
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60
+
61
+ Reports based on a failure to sanitize user input are not valid security
62
+ vulnerabilities. It is the developer's responsibility to properly handle user
63
+ input. This principle is explained in our :ref:`security documentation
64
+ <sanitize-user-input>`.
65
+
66
+ For example, the following is **not considered valid** because ``email`` has
67
+ not been sanitized::
68
+
69
+ from django.core.mail import send_mail
70
+ from django.http import JsonResponse
71
+
72
+
73
+ def my_proof_of_concept(request):
74
+ email = request.GET.get("email", "")
75
+ send_mail("Email subject", "Email body", email, ["
[email protected] "])
76
+ return JsonResponse(status=200)
77
+
78
+ Developers must **always validate and sanitize input** before using it. The
79
+ correct approach would be to use a Django form to ensure ``email`` is properly
80
+ validated::
81
+
82
+ from django import forms
83
+ from django.core.mail import send_mail
84
+ from django.http import JsonResponse
85
+
86
+
87
+ class EmailForm(forms.Form):
88
+ email = forms.EmailField()
89
+
90
+
91
+ def my_proof_of_concept(request):
92
+ form = EmailForm(request.GET)
93
+ if form.is_valid():
94
+ send_mail(
95
+ "Email subject",
96
+ "Email body",
97
+ form.cleaned_data["email"],
98
+
99
+ )
100
+ return JsonResponse(status=200)
101
+ return JsonResponse(form.errors, status=400)
102
+
103
+ Similarly, as Django's raw SQL constructs (such as :meth:`~.QuerySet.extra` and
104
+ :class:`.RawSQL` expression) provide developers with full control over the
105
+ query, they are insecure if user input is not properly handled. As explained in
106
+ our :ref:`security documentation <sql-injection-protection>`, it is the
107
+ developer's responsibility to safely process user input for these functions.
108
+
109
+ For instance, the following is **not considered valid** because ``query`` has
110
+ not been sanitized::
111
+
112
+ from django.shortcuts import HttpResponse
113
+ from .models import MyModel
114
+
115
+
116
+ def my_proof_of_concept(request):
117
+ query = request.GET.get("query", "")
118
+ q = MyModel.objects.extra(select={"id": query})
119
+ return HttpResponse(q.values())
120
+
121
+ Request headers and URLs must be under 8K bytes
122
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123
+
124
+ To prevent denial-of-service (DoS) attacks, production-grade servers impose
125
+ limits on request header and URL sizes. For example, by default Gunicorn allows
126
+ up to roughly:
127
+
128
+ * `4k bytes for a URL`_
129
+ * `8K bytes for a request header`_
130
+
131
+ Other web servers, such as Nginx and Apache, have similar restrictions to
132
+ prevent excessive resource consumption.
133
+
134
+ Consequently, the Django security team will not consider reports that rely on
135
+ request headers or URLs exceeding 8K bytes, as such inputs are already
136
+ mitigated at the server level in production environments.
137
+
138
+ .. admonition:: :djadmin:`runserver` should never be used in production
139
+
140
+ Django's built-in development server does not enforce these limits because
141
+ it is not designed to be a production server.
142
+
143
+ .. _`4k bytes for a URL`: https://docs.gunicorn.org/en/stable/settings.html#limit-request-line
144
+ .. _`8k bytes for a request header`: https://docs.gunicorn.org/en/stable/settings.html#limit-request-field-size
145
+
146
+ The request body must be under 2.5 MB
147
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148
+
149
+ The :setting:`DATA_UPLOAD_MAX_MEMORY_SIZE` setting limits the default maximum
150
+ request body size to 2.5 MB.
151
+
152
+ As this is enforced on all production-grade Django projects by default, a proof
153
+ of concept must not exceed 2.5 MB in the request body to be considered valid.
154
+
155
+ Issues resulting from large, but potentially reasonable setting values, should
156
+ be reported using the `public ticket tracker`_ for hardening.
157
+
158
+ .. _public ticket tracker: https://code.djangoproject.com/
159
+
160
+ Code under test must feasibly exist in a Django project
161
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162
+
163
+ The proof of concept must plausibly occur in a production-grade Django
164
+ application, reflecting real-world scenarios and following standard development
165
+ practices.
166
+
167
+ Django contains many private and undocumented functions that are not part of
168
+ its public API. If a vulnerability depends on directly calling these internal
169
+ functions in an unsafe way, it will not be considered a valid security issue.
170
+
171
+ Content displayed by the Django Template Language must be under 100 KB
172
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173
+
174
+ The Django Template Language (DTL) is designed for building the content needed
175
+ to display web pages. In particular its text filters are meant for that kind of
176
+ usage.
177
+
178
+ For reference, the complete works of Shakespeare have about 3.5 million bytes
179
+ in plain-text ASCII encoding. Displaying such in a single request is beyond the
180
+ scope of almost all websites, and so outside the scope of the DTL too.
181
+
182
+ Text processing is expensive. Django makes no guarantee that DTL text filters
183
+ are never subject to degraded performance if passed deliberately crafted,
184
+ sufficiently large inputs. Under default configurations, Django makes it
185
+ difficult for sites to accidentally accept such payloads from untrusted
186
+ sources, but, if it is necessary to display large amounts of user-provided
187
+ content, it’s important that basic security measures are taken.
188
+
189
+ User-provided content should always be constrained to known maximum length. It
190
+ should be filtered to remove malicious content, and validated to match expected
191
+ formats. It should then be processed offline, if necessary, before being
192
+ displayed.
193
+
194
+ Proof of concepts which use over 100 KB of data to be processed by the DTL will
195
+ be considered invalid.
196
+
41
197
.. _security-report-evaluation:
42
198
43
199
How does Django evaluate a report
@@ -110,20 +266,15 @@ will not issue patches or new releases for those versions.
110
266
111
267
.. _main development branch: https://github.com/django/django/
112
268
113
- .. _security-disclosure :
269
+ .. _severity-levels :
114
270
115
- How Django discloses security issues
116
- ====================================
271
+ Security issue severity levels
272
+ ==============================
117
273
118
- Our process for taking a security issue from private discussion to
119
- public disclosure involves multiple steps .
274
+ The severity level of a security vulnerability is determined by the attack
275
+ type .
120
276
121
- Approximately one week before public disclosure, we send two notifications:
122
-
123
- First, we notify |django-announce| of the date and approximate time of the
124
- upcoming security release, as well as the severity of the issues. This is to
125
- aid organizations that need to ensure they have staff available to handle
126
- triaging our announcement and upgrade Django as needed. Severity levels are:
277
+ Severity levels are:
127
278
128
279
* **High**
129
280
@@ -144,6 +295,21 @@ triaging our announcement and upgrade Django as needed. Severity levels are:
144
295
* Unvalidated redirects/forwards
145
296
* Issues requiring an uncommon configuration option
146
297
298
+ .. _security-disclosure:
299
+
300
+ How Django discloses security issues
301
+ ====================================
302
+
303
+ Our process for taking a security issue from private discussion to
304
+ public disclosure involves multiple steps.
305
+
306
+ Approximately one week before public disclosure, we send two notifications:
307
+
308
+ First, we notify |django-announce| of the date and approximate time of the
309
+ upcoming security release, as well as the severity of the issues. This is to
310
+ aid organizations that need to ensure they have staff available to handle
311
+ triaging our announcement and upgrade Django as needed.
312
+
147
313
Second, we notify a list of :ref:`people and organizations
148
314
<security-notifications>`, primarily composed of operating-system vendors and
149
315
other distributors of Django. This email is signed with the PGP key of someone
0 commit comments