Tajuk HTTP_HOST Tidak Valid SuspiciousOperation Django

96

Setelah meningkatkan ke Django 1.5, saya mulai mendapatkan kesalahan seperti ini:

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)}>

Saya telah mengatur ALLOWED_HOSTS = ['.derekkwok.net'] di file settings.py saya.

Apa yang terjadi disini? Apakah seseorang yang berpura-pura menjadi Google dan mengakses situs saya? Atau apakah ini kasus jinak dari seseorang yang salah menyetel header HTTP_HOST mereka?

Derek Kwok
sumber
Apakah Anda mengetahui cara memperbaikinya? Menghadapi masalah yang sama. Mencatat sekitar seratus kesalahan ini setiap hari. Tidak tahu apakah itu sesuatu yang perlu saya khawatirkan.
blinduck
3
Posting blog ini memberikan cara yang bagus untuk menghentikan email: tiwoc.de/blog/2013/03/…
Derek Kwok

Jawaban:

64

Jika Anda ALLOWED_HOSTS disetel dengan benar, maka mungkin seseorang sedang menyelidiki situs Anda untuk mengetahui kerentanannya dengan memalsukan header.

Ada diskusi sekarang oleh para pengembang Django untuk mengubah ini dari kesalahan server internal 500 menjadi respon 400. Lihat tiket ini .

Brian Neal
sumber
1
Saya pikir penjelasan yang lebih mungkin adalah perayap web (robot) hanya merayapi alamat IP publik pada port 80 - dalam hal ini Anda ingin mengizinkannya.
markmnl
16
@markmnl Penjelajah web yang sah tidak boleh memalsukan tajuk host.
Brian Neal
1
Itu hanya menghubungkan menggunakan alamat IP bukan nama domain dan alamat IP tidak ada di ALLOWED_HOSTS - atau setidaknya itulah yang terjadi dengan saya - saya bisa repro dengan mengarahkan browser saya ke alamat IP.
markmnl
Ya. Dan di situs mana pun yang setengah sibuk, ini terjadi sepanjang hari setiap hari. Mereka telah memperbaikinya sekarang, tapi berikut adalah aplikasi "drop-in" yang memilah-milahnya di semua versi bersama dengan filter tingkat kesalahan. github.com/litchfield/django-safelogging
s29
Setelah menyebarkan situs web saya di internet. Saya menemukan banyak orang mencoba mendapatkan akses ke situs web saya menggunakan host yang tidak valid. Tidak hanya menggunakan alamat IP. Saya pikir ini mungkin beberapa orang yang mencoba menemukan situs web yang tidak dapat mempertahankan serangan csrf.
ramwin
130

Jika Anda menggunakan Nginx untuk meneruskan permintaan ke Django yang berjalan pada Gunicorn / Apache / uWSGI, Anda dapat menggunakan yang berikut ini untuk memblokir permintaan yang buruk. Terima kasih kepada @PaulM untuk saran dan posting blog ini sebagai contoh.

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;
        ...
    }

}
Brent O'Connor
sumber
7
Akan sangat baik untuk melihat ini sebagai peningkatan pada petunjuk petunjuk dokumen :)
Paul McMillan
1
@webjunkie, Dari tautan Anda, "Ada kasus di mana Anda tidak dapat menghindari penggunaan jika, misalnya jika Anda perlu menguji variabel yang tidak memiliki perintah yang setara." Contoh saya menggunakannya dengan benar dan berfungsi dengan baik di lingkungan produksi saya. Jadi kesimpulannya, lakukan seperti ini! :)
Brent O'Connor
2
Anda dapat dengan mudah menghindarinya: Cukup tentukan hanya nama_server yang Anda butuhkan, dan biarkan sisanya ditangani oleh penangan server default.
webjunkie
1
Lihat jawaban ini untuk konfigurasi Apache yang serupa: stackoverflow.com/a/18792080
Denilson Sá Maia
1
Dari tautan yang disediakan oleh webjunkie: "Petunjuk jika mengalami masalah saat digunakan dalam konteks lokasi". Contoh yang diberikan oleh Brent menggunakan bagian ifdalam serverblok dan bukan di dalam locationblok. Apakah itu berarti iftidak apa-apa dalam hal ini?
Brian buck
31

Saat menggunakan Nginx Anda dapat menyiapkan server Anda dengan cara yang hanya meminta ke host yang Anda inginkan ke Django terlebih dahulu. Itu seharusnya tidak memberi Anda kesalahan SuspiciousOperation lagi.

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
}
webjunkie
sumber
2
Saya suka pendekatan ini daripada menggunakan ifpendekatan yang disarankan oleh Brent, tetapi saya tidak bisa membuatnya bekerja dengan port 443. Saya mencoba meniru saran Anda (dengan port mendengarkan berubah), dan situs SSL saya yang sebenarnya tidak memuat - itu tertangkap oleh entri ini saya tambahkan. ada ide bagaimana cara memperbaikinya?
Dolan Antenucci
1
Poster lain di ServerFault.com memiliki masalah serupa, jadi saya mengikuti rekomendasinya tentang pendekatan pernyataan-if hanya untuk lalu lintas 443
Dolan Antenucci
1
Tampaknya Anda harus menentukan jalur ke file sertifikat jika Anda ingin menangkap permintaan SSL juga (meskipun Anda hanya ingin membuangnya): server { listen 80 default_server; listen 443; server_name _; ssl_certificate /path/to/file.crt; ssl_certificate_key /path/to/file.key; return 444; }
n__o
Apa yang akan Nginx kembalikan jika request's HOST tidak valid? 50x atau 40x?
laike9m
Apa tambahannya dalam konfigurasi ini? Saya memiliki nama server yang disetel baik dalam pengalihan dan bagian aplikasi, saya masih mendapatkan Invalid HTTP_HOST header(dengan Django 1.8.x)
Csaba Toth
16

Ini diperbaiki dalam versi terbaru Django, tetapi jika Anda menggunakan versi yang terpengaruh (mis. 1.5) Anda dapat menambahkan filter ke penangan pencatat Anda untuk menyingkirkan ini, seperti yang dijelaskan dalam posting blog ini .

Spoiler:

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,
        },
    }
}
mgalgs
sumber
1
Ada tautan untuk perbaikan atau versi yang diterapkan? Thx
Marc
1
Saya memilikinya di versi 2.0.5
mehmet
Ini tidak diperbaiki pada versi terbaru dari Django. Saya menggunakan Django 2.0.10
javidazac