Opened 6 years ago
Closed 4 years ago
#29606 closed New feature (fixed)
Validate the type of ALLOWED_HOSTS
Reported by: | rafis | Owned by: | Marius Räsener |
---|---|---|---|
Component: | Core (Other) | Version: | dev |
Severity: | Normal | Keywords: | allowed_hosts |
Cc: | Adam Johnson, Herbert Fortes, Marius Räsener | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
Python has soft behavior for conducting iteration process over lists and over strings making them look the same:
for char_or_item in str_or_list: -- `char_or_item` can be character or list item
It would be better if it would have more strict behavior, for example,
for char in some_str.chars(): -- now `char` can be only of string type and `list` class would not have `chars` method
and for list
for item in some_list.list_items(): -- `string` class would not have `list_items` method
This soft behavior usually leads to many nasty bugs to appear. Our two software engineers from our team wasted about 1 hour debugging the issue with ALLOWED_HOSTS being initialized with string in local_settings.py
which is included at the end of settings.py
. Django was matching each separate character of ALLOWED_HOSTS string against the "Host:" header from an incoming HTTP request.
An obvious self-suggesting solution is to add a new system check that will check the type of ALLOWED_HOSTS if it is string or not and notify the developer about possible improper configuration. I think blacklist checking (string or not) is more appropiate here, but I can be wrong.
Change History (27)
comment:1 by , 6 years ago
Easy pickings: | unset |
---|---|
Has patch: | set |
Patch needs improvement: | set |
Summary: | Validate the type of ALLOWED_HOSTS to prevent possible issues and wasted time → Validate the type of ALLOWED_HOSTS |
Triage Stage: | Unreviewed → Accepted |
Version: | 2.0 → master |
comment:2 by , 6 years ago
Cc: | added |
---|
Although it may or may not have helped, I believe in general we should check as much configuration as possible up front. Another error I myself have made with ALLOWED_HOSTS is trying "*.example.com" instead of ".example.com" (confusion with "*" being an allowed value) - this (or in general valid/invalid characters?) could also be checked for.
comment:3 by , 6 years ago
I'm not opposed to some validation, my suggestion was merely that maybe we could be more helpful than a system check.
comment:4 by , 6 years ago
Cc: | added |
---|
comment:5 by , 6 years ago
Sorry for the interfere but I would like to add my opinion on this. If this New Feature is added, then proportionate validations should apply on other settings.py
variables (such as INTERNAL_IPS
to be a list of valid IPs, STATIC_ROOT
to be a string of valid pathname etc). See where it's going? I am not opposed on this improvement but if it's going to happen, IMO it should happen if not on the whole settings list, at least on most of them.
Best regards, Nick
comment:6 by , 6 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:7 by , 4 years ago
Easy pickings: | set |
---|---|
Owner: | removed |
Status: | assigned → new |
comment:8 by , 4 years ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:9 by , 4 years ago
Hi, I've assigned this ticket to myself and would like to check my idea with you before writing any code.
I'm not sure about what Nick Mavrakis stated, maybe that'd be a good idea.
But sticking to this ticket, I believe the best way to proceed is checking the setting is both iterable and not a str.
I would do so in two places:
1 - In runserver
, where there's already one check related to ALLOWED_HOSTS
2 - As a deployment check, that would raise an error if the constraints are not satisfied.
If you could confirm this would be the way to go, I'd be more than happy to add them in a PR.
comment:10 by , 4 years ago
It needs only be in one place, as a system check. runserver
runs system checks at startup already. IMO it should not be a deployment check, as ALLOWED_HOSTS
affects use of runserver
locally too if DEBUG = False
.
comment:11 by , 4 years ago
Hello, I am Akshat, new to this organisation and I would like to contribute on this bug. I do understand what rafis is trying to say here, but I don't understand how to proceed with fixing with this issue. Can someone tell me what files need to be modified or how to figure that out.
comment:12 by , 4 years ago
Wouldn't it be best to check if the entries in ALLOWED_HOSTS
are actually valid hosts?
So either IPv4/IPv6 Addresses or some dot separated string with valid characters.
Maybe I didn't understand how the bug appeared. Or was it something like:
# wrong ALLOWED_HOSTS="127.0.0.1"
vs.
ALLOWED_HOSTS=["127.0.0.1",]
also, doesn't work a tuple too?
thx for any hints giving me a better understanding...
comment:13 by , 4 years ago
The validation basically only needs to check it's an iterable of strings.
Checking that everything looks like a hostname is very complicated - we also support wildcards. And it would probably break for someone out there using a proxy server or middleware that adds something not-quite-host-looking in the Host header.
comment:14 by , 4 years ago
ok, so here's what I came up with for the check:
def check_allowedhosts(ALLOWED_HOSTS): if isinstance(ALLOWED_HOSTS,(list,tuple)): return all(isinstance(element,str) for element in ALLOWED_HOSTS) else: return False
for this use-case all([])
returning True isn't really helpful, so the if else clause is needed I think ... but of course I'm happy to get hints for a better implementation.
Besides, where would I put the check and the test?
comment:16 by , 4 years ago
I didn't see the PR earlier, thank you for pointing to it.
Alright, so I could recreate or copy the PR, but there are things still open to discuss. Can someone point me in the right direction what Tim Graham is referring to?
Like how could this be more helpful than a system check - maybe something that's hooked into a test run?
(for a better understanding, I'm trying since yesterday my luck with "easy picking" issues to contribute something to Django - so please keep in mind I'm a beginner.)
comment:17 by , 4 years ago
Personally I think a system check would be fine. We point users to run them during deployment in the deplyoment checklist: https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
follow-up: 19 comment:18 by , 4 years ago
well, having a app running in production for several years now I never knew this exists :P
but, whatever - that's my responsibility.
Anyways, I don't have a strong opinion I just have an interest in closing this issue since I want to be some kind of contributor and head to the next issue.
So, what needs to be done to merge this and close the issue?
comment:19 by , 4 years ago
Replying to Marius Räsener:
So, what needs to be done to merge this and close the issue?
The second step is to send PR via GitHub.
comment:22 by , 4 years ago
Please refer to the contributing documentation to make sure your patch appears in the review queue.
In this case you'll want to uncheck Patch needs improvement
comment:23 by , 4 years ago
Cc: | added |
---|---|
Owner: | changed from | to
Patch needs improvement: | unset |
I hope that’s the right thing to do...
comment:24 by , 4 years ago
Patch needs improvement: | set |
---|
comment:25 by , 4 years ago
I've recreated the PR and incorporated the feedback from the reviewers. It's ready for another review at https://github.com/django/django/pull/14149.
Functionally i think it's good; the docs will need some review (especially if they needed to be included in other places)
comment:26 by , 4 years ago
Component: | Core (System checks) → Core (Other) |
---|---|
Patch needs improvement: | unset |
Triage Stage: | Accepted → Ready for checkin |
PR
I'm not sure if a system check is the way to go. The deployment checks must be run manually
manage.py check --deploy
. The developer might not do that as part of a debugging workflow.