ส่วนหัว HTTP_HOST ที่สงสัยของ Django ไม่ถูกต้อง


98

หลังจากอัปเกรดเป็น Django 1.5 ฉันเริ่มได้รับข้อผิดพลาดดังนี้:

Traceback (most recent call last):

File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 92, in get_response
response = middleware_method(request)

File "/usr/local/lib/python2.7/dist-packages/django/middleware/common.py", line 57, in process_request
host = request.get_host()

File "/usr/local/lib/python2.7/dist-packages/django/http/request.py", line 72, in get_host
"Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)

SuspiciousOperation: Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): www.google.com

<WSGIRequest
path:/,
GET:<QueryDict: {}>,
POST:<QueryDict: {}>,
COOKIES:{},
META:{'CONTENT_LENGTH': '',
'CONTENT_TYPE': '',
'DOCUMENT_ROOT': '/etc/nginx/html',
'HTTP_ACCEPT': 'text/html',
'HTTP_HOST': 'www.google.com',
'HTTP_PROXY_CONNECTION': 'close',
'HTTP_USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
'PATH_INFO': u'/',
'QUERY_STRING': '',
'REMOTE_ADDR': '210.245.91.104',
'REMOTE_PORT': '49347',
'REQUEST_METHOD': 'GET',
'REQUEST_URI': '/',
u'SCRIPT_NAME': u'',
'SERVER_NAME': 'www.derekkwok.net',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.0',
'uwsgi.node': 'derekkwok',
'uwsgi.version': '1.4.4',
'wsgi.errors': <open file 'wsgi_errors', mode 'w' at 0xb6d99c28>,
'wsgi.file_wrapper': <built-in function uwsgi_sendfile>,
'wsgi.input': <uwsgi._Input object at 0x953e698>,
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)}>

ฉันตั้งค่าไว้ALLOWED_HOSTS = ['.derekkwok.net'] ในไฟล์ settings.py

เกิดขึ้นที่นี่คืออะไร? มีใครแอบอ้างเป็น Google และเข้าถึงไซต์ของฉัน หรือเป็นกรณีที่ไม่เป็นอันตรายเมื่อมีผู้ตั้งค่าส่วนหัว HTTP_HOST ไม่ถูกต้อง?


คุณคิดหาวิธีแก้ไขปัญหานี้หรือไม่? เผชิญปัญหาเดียวกัน. บันทึกเกี่ยวกับข้อผิดพลาดหลายร้อยรายการทุกวัน ไม่รู้ว่าเป็นสิ่งที่ฉันต้องกังวลหรือไม่
blinduck

3
โพสต์บล็อกนี้เป็นวิธีที่ดีในการหยุดอีเมล: tiwoc.de/blog/2013/03/…
Derek Kwok

คำตอบ:


64

หากคุณALLOWED_HOSTSตั้งค่าอย่างถูกต้องอาจเป็นไปได้ว่ามีคนกำลังตรวจสอบไซต์ของคุณเพื่อหาช่องโหว่โดยการปลอมแปลงส่วนหัว

ขณะนี้มีการพูดคุยกันโดยนักพัฒนา Django เพื่อเปลี่ยนสิ่งนี้จากข้อผิดพลาดเซิร์ฟเวอร์ภายใน 500 เป็นการตอบสนอง 400 ดูตั๋วนี้


1
ฉันคิดว่าคำอธิบายที่เป็นไปได้มากกว่านั้นคือโปรแกรมรวบรวมข้อมูลเว็บ (โรบ็อต) เพียงแค่รวบรวมข้อมูลที่อยู่ IP สาธารณะบนพอร์ต 80 ซึ่งในกรณีนี้คุณต้องการอนุญาต
markmnl

16
@markmnl โปรแกรมรวบรวมข้อมูลเว็บที่ถูกต้องไม่ควรปลอมแปลงส่วนหัวของโฮสต์
Brian Neal

1
มันเป็นเพียงการเชื่อมต่อโดยใช้ที่อยู่ IP ไม่ใช่ชื่อโดเมนและที่อยู่ IP ไม่ได้อยู่ใน ALLOWED_HOSTS - หรืออย่างน้อยก็คือสิ่งที่เกิดขึ้นกับฉัน - ฉันสามารถทำซ้ำได้โดยชี้เบราว์เซอร์ของฉันไปที่ที่อยู่ IP
markmnl

ใช่. และในไซต์ที่มีงานยุ่งเพียงครึ่งเดียวสิ่งนี้จะเกิดขึ้นทุกวันทุกวัน พวกเขาได้แก้ไขแล้ว แต่นี่คือแอป "ดรอปอิน" ที่จะแยกประเภทออกจากทุกเวอร์ชันพร้อมกับตัวกรองอัตราข้อผิดพลาด github.com/litchfield/django-safelogging
s29

หลังจากปรับใช้เว็บไซต์ของฉันบนอินเทอร์เน็ต ฉันพบว่ามีคนจำนวนมากพยายามเข้าถึงเว็บไซต์ของฉันโดยใช้โฮสต์ที่ไม่ถูกต้อง ไม่เพียง แต่ใช้ที่อยู่ IP เท่านั้น ฉันคิดว่านี่อาจเป็นบางคนที่พยายามค้นหาเว็บไซต์ที่ไม่สามารถป้องกันการโจมตี csrf ได้
รามวิน

135

หากคุณใช้ Nginx เพื่อส่งต่อคำขอไปยัง Django ที่ทำงานบน Gunicorn / Apache / uWSGI คุณสามารถใช้สิ่งต่อไปนี้เพื่อบล็อกคำขอที่ไม่ดี ขอบคุณ@PaulMสำหรับข้อเสนอแนะ

upstream app_server {
    server unix:/tmp/gunicorn_mydomain.com.sock fail_timeout=0;
}

server {

    ...

    ## Deny illegal Host headers
    if ($host !~* ^(mydomain.com|www.mydomain.com)$ ) {
        return 444;
    }

    location  / {
        proxy_pass               http://app_server;
        ...
    }

}

7
จะเป็นการดีที่จะเห็นว่านี่เป็นการปรับปรุงคำใบ้คำใบ้ของเอกสาร:)
Paul McMillan

1
@webjunkie จากลิงก์ของคุณ "มีบางกรณีที่คุณไม่สามารถหลีกเลี่ยงการใช้ if ได้เช่นหากคุณต้องการทดสอบตัวแปรที่ไม่มีคำสั่งเทียบเท่า" ตัวอย่างของฉันใช้อย่างถูกต้องและทำงานได้ดีในสภาพแวดล้อมการผลิตของฉัน สรุปแล้วทำแบบนี้! :)
Brent O'Connor

2
คุณสามารถหลีกเลี่ยงได้อย่างง่ายดาย: เพียงระบุเฉพาะ server_name ที่คุณต้องการและปล่อยให้ส่วนที่เหลือจัดการโดยตัวจัดการเซิร์ฟเวอร์เริ่มต้น
webjunkie

1
ดูคำตอบนี้สำหรับการกำหนดค่า Apache ที่คล้ายกัน: stackoverflow.com/a/18792080
Denilson Sá Maia

1
จากลิงก์ที่ webjunkie ให้มา: "คำสั่งหากมีปัญหาเมื่อใช้ในบริบทสถานที่" ตัวอย่างที่ให้โดย Brent ใช้ifด้านในserverบล็อกไม่ใช่ในlocationบล็อก หมายความว่าifโอเคในกรณีนี้หรือไม่?
brian buck

31

เมื่อใช้ Nginx คุณสามารถตั้งค่าเซิร์ฟเวอร์ของคุณด้วยวิธีการร้องขอไปยังโฮสต์ที่คุณต้องการเข้าสู่ Django ในตอนแรกเท่านั้น ซึ่งจะทำให้คุณไม่มีข้อผิดพลาด S สงสัยOperationอีกต่อไป

server {
    # default server

    listen 80;
    server_name _ default;

    return 444;
}
server {
    # redirects

    listen 80;
    server_name example.com old.stuff.example.com;

    return 301 http://www.example.com$request_uri;
}
server {
    # app

    listen 80;
    server_name www.example.com; # only hosts in ALLOWED_HOSTS here

    location  / {
        # ...
    }
    # ... your config/proxy stuff
}

2
ฉันชอบวิธีนี้มากกว่าการใช้ifแนวทางที่แนะนำโดย Brent แต่ฉันไม่สามารถใช้งานกับพอร์ต 443 ได้ฉันพยายามเลียนแบบคำแนะนำของคุณ (โดยเปลี่ยนพอร์ตการฟัง) และไซต์ SSL จริงของฉันไม่โหลด - มัน ถูกจับโดยรายการนี้ที่ฉันเพิ่ม มีแนวคิดในการแก้ไขอย่างไร
Dolan Antenucci

1
โปสเตอร์อื่นในServerFault.comมีปัญหาที่คล้ายกันดังนั้นฉันจึงทำตามคำแนะนำของเขาเกี่ยวกับวิธีการ if-statement สำหรับการรับส่งข้อมูล 443 เท่านั้น
Dolan Antenucci

1
ดูเหมือนว่าคุณต้องระบุเส้นทางไปยังไฟล์ใบรับรองหากคุณต้องการตรวจจับคำขอ SSL ด้วย (แม้ว่าคุณจะต้องการทิ้งserver { listen 80 default_server; listen 443; server_name _; ssl_certificate /path/to/file.crt; ssl_certificate_key /path/to/file.key; return 444; }
ก็ตาม

Nginx จะส่งคืนอะไรหาก HOST ของคำขอไม่ถูกต้อง 50x หรือ 40x?
laike9m

มีอะไรพิเศษในการกำหนดค่านี้? ฉันมีชื่อเซิร์ฟเวอร์ที่ตั้งไว้ทั้งในส่วนการเปลี่ยนเส้นทางและส่วนแอปฉันยังคงได้รับInvalid HTTP_HOST header(กับ Django 1.8.x)
Csaba Toth

16

นี้ได้รับการแก้ไขในรุ่นใหม่ของ Django แต่ถ้าคุณกำลังใช้รุ่นที่ได้รับผลกระทบ (เช่น 1.5) คุณสามารถเพิ่มตัวกรองเพื่อจัดการคนตัดไม้ของคุณเพื่อกำจัดเหล่านี้ตามที่ระบุไว้ในนี้โพสต์บล็อก

สปอยเลอร์:

from django.core.exceptions import SuspiciousOperation

def skip_suspicious_operations(record):
  if record.exc_info:
    exc_value = record.exc_info[1]
    if isinstance(exc_value, SuspiciousOperation):
      return False
  return True

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse',
        },
        # Define filter
        'skip_suspicious_operations': {
            '()': 'django.utils.log.CallbackFilter',
            'callback': skip_suspicious_operations,
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            # Add filter to list of filters
            'filters': ['require_debug_false', 'skip_suspicious_operations'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}

1
ลิงก์สำหรับการแก้ไขหรือเวอร์ชันที่ใช้งานได้หรือไม่ Thx
Marc

1
ฉันมีเวอร์ชัน 2.0.5
mehmet

สิ่งนี้ไม่ได้รับการแก้ไขใน Django เวอร์ชันใหม่กว่า ฉันใช้ Django 2.0.10
javidazac
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.