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 Tim Graham, 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 timeValidate the type of ALLOWED_HOSTS
Triage Stage: UnreviewedAccepted
Version: 2.0master

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.

comment:2 by Adam Johnson, 6 years ago

Cc: Adam Johnson 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 Tim Graham, 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 Herbert Fortes, 6 years ago

Cc: Herbert Fortes added

comment:5 by Nick Mavrakis, 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 Tomasz Kluczkowski, 6 years ago

Owner: changed from nobody to Tomasz Kluczkowski
Status: newassigned

comment:7 by Mariusz Felisiak, 4 years ago

Easy pickings: set
Owner: Tomasz Kluczkowski removed
Status: assignednew

comment:8 by Octavio Peri, 4 years ago

Owner: set to Octavio Peri
Status: newassigned

comment:9 by Octavio Peri, 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 Adam Johnson, 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 Akshat Dixit, 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 Marius Räsener, 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 Adam Johnson, 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 Marius Räsener, 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?

Last edited 4 years ago by Marius Räsener (previous) (diff)

comment:15 by Adam Johnson, 4 years ago

See the PR linked in comment #1 for an intial implementation.

comment:16 by Marius Räsener, 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 Adam Johnson, 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/

comment:18 by Marius Räsener, 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?

Version 0, edited 4 years ago by Marius Räsener (next)

in reply to:  18 comment:19 by Mariusz Felisiak, 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:20 by Marius Räsener, 4 years ago

comment:21 by Marius Räsener, 4 years ago

Should I ping this every ~24 hrs or what could be a good strategy?

comment:22 by Simon Charette, 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 Marius Räsener, 4 years ago

Cc: Marius Räsener added
Owner: changed from Octavio Peri to Marius Räsener
Patch needs improvement: unset

I hope that’s the right thing to do...

comment:24 by Adam Johnson, 4 years ago

Patch needs improvement: set

comment:25 by AdamDonna, 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 Mariusz Felisiak, 4 years ago

Component: Core (System checks)Core (Other)
Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:27 by Mariusz Felisiak <felisiak.mariusz@…>, 4 years ago

Resolution: fixed
Status: assignedclosed

In cdd0b21:

Fixed #29606 -- Added type check for ALLOWED_HOSTS setting.

Note: See TracTickets for help on using tickets.
Back to Top