Django'da kullanıcı IP adresini nasıl alabilirim?


288

Kullanıcının IP'sini django'da nasıl edinebilirim?

Ben böyle bir görüş var:

# Create your views
from django.contrib.gis.utils import GeoIP
from django.template import  RequestContext
from django.shortcuts import render_to_response


def home(request):
  g = GeoIP()
  client_ip = request.META['REMOTE_ADDR']
  lat,long = g.lat_lon(client_ip)
  return render_to_response('home_page_tmp.html',locals())

Ama bu hatayı alıyorum:

KeyError at /mypage/
    'REMOTE_ADDR'
    Request Method: GET
    Request URL:    http://mywebsite.com/mypage/
    Django Version: 1.2.4
    Exception Type: KeyError
    Exception Value:    
    'REMOTE_ADDR'
    Exception Location: /mysite/homepage/views.py in home, line 9
    Python Executable:  /usr/bin/python
    Python Version: 2.6.6
    Python Path:    ['/mysite', '/usr/local/lib/python2.6/dist-packages/flup-1.0.2-py2.6.egg', '/usr/lib/python2.6', '/usr/lib/python2.6/plat-linux2', '/usr/lib/python2.6/lib-tk', '/usr/lib/python2.6/lib-old', '/usr/lib/python2.6/lib-dynload', '/usr/local/lib/python2.6/dist-packages', '/usr/lib/python2.6/dist-packages', '/usr/lib/pymodules/python2.6']
    Server time:    Sun, 2 Jan 2011 20:42:50 -0600


2
['HTTP_COOKIE', 'SCRIPT_NAME', 'REQUEST_METHOD', 'PATH_INFO', 'SERVER_PROTOCOL', 'QUERY_STRING', 'CONTENT_LENGTH', 'HTTP_ACCEPT_CHARSET', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'HTTP_CONNECTION'. , 'SERVER_PORT', 'wsgi.input', 'HTTP_HOST', 'wsgi.multithread', 'HTTP_CACHE_CONTROL', 'HTTP_ACCEPT', 'wsgi.version', 'wsgi.run_once', 'wsgi.errors', 'wsgi. çoklu işlem ',' HTTP_ACCEPT_LANGUAGE ',' CONTENT_TYPE ',' CSRF_COOKIE ',' HTTP_ACCEPT_ENCODING ']
avatar

2
Bu harika soru için teşekkürler. Fastcgi dosyam REMOTE_ADDR meta anahtarını geçirmiyordu. Aşağıdaki satırı nginx.conf dosyasına ekledim ve sorunu düzelttim: fastcgi_param REMOTE_ADDR $ remote_addr;
avatar

Yanıtlar:


435
def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

Ters proxy'nin (varsa) doğru yapılandırıldığından emin olun (örn mod_rpaf. Apache için yüklenmiş).

Not: yukarıdaki ilk öğeyi içindeki öğeyi kullanır X-Forwarded-For, ancak son öğeyi kullanmak isteyebilirsiniz (örneğin, Heroku söz konusu olduğunda: Müşterinin Heroku'daki gerçek IP adresini alın )

Ve sonra sadece isteği argüman olarak iletin;

get_client_ip(request)

8
ip = get_client_ip(request)Görünüm işlevinizde arayın .
yanchenko

4
Gerçek istemci IP adresi HTTP_X_FORWARDED_FOR'daki ilk değil
sonuncudur

5
@jujule Bu doğru değil. Biçim genellikle X-Forwarded-For: client, proxy1, proxy2. Yani ilk adres müşterinin.
Michael Waterfall

51
Bu işlev tehlikelidir. Birçok kurulumda, kötü niyetli bir kullanıcı bu işlevin istediği adresi (gerçek adresi yerine) döndürmesine kolayca neden olabilir. Bkz. Esd.io/blog/flask-apps-heroku-real-ip-spoofing.html
Eli

8
Django dokümanlardan ( "REMOTE_ADDR veya benzer değerlere dayanarak yaygın bir kötü uygulama olarak biliniyor" djangoproject.com/weblog/2009/jul/28/security/#secondary-issue )
zags

209

Sen kullanabilirsiniz django-ipware Python destekleyen 2 & 3 ve kolları IPv4 & IPv6'yı .

Yüklemek:

pip install django-ipware

Basit Kullanım:

# In a view or a middleware where the `request` object is available

from ipware import get_client_ip
ip, is_routable = get_client_ip(request)
if ip is None:
    # Unable to get the client's IP address
else:
    # We got the client's IP address
    if is_routable:
        # The client's IP address is publicly routable on the Internet
    else:
        # The client's IP address is private

# Order of precedence is (Public, Private, Loopback, None)

Gelişmiş Kullanım:

  • Özel Başlık - Ipware'in bakması için özel istek başlığı:

    i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR'])
    i, r = get_client_ip(request, request_header_order=['X_FORWARDED_FOR', 'REMOTE_ADDR'])
  • Proxy Count - Django sunucusu sabit sayıda proxy'nin arkasında:

    i, r = get_client_ip(request, proxy_count=1)
  • Güvenilir Proxy'ler - Django sunucusu bir veya daha fazla bilinen ve güvenilir proxy'nin arkasında:

    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2'))
    
    # For multiple proxies, simply add them to the list
    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.2.2', '177.3.3.3'))
    
    # For proxies with fixed sub-domain and dynamic IP addresses, use partial pattern
    i, r = get_client_ip(request, proxy_trusted_ips=('177.2.', '177.3.'))

Not: bu bildirimi okuyun .


17
Kaynak koduna bir göz atın. Buradaki diğer cevaplar tarafından tanımlanan tüm komplikasyonları ele alır.
HostedMetrics.com

5
Thx @Heliodor - Evet, modülü ortalama bir kullanım çantası için çok basit ve karmaşık bir kullanım çantası için çok esnek yaptım. En azından, kendi yuvarlanmadan önce github sayfasına bakmak istersiniz.
un33k

3
Django-ipware ayarlarının varsayılan olarak güvenli olmadığını unutmayın! Herkes diğer değişkenlerden birini geçebilir ve siteniz bu IP'yi kaydeder. Her zaman IPWARE_META_PRECEDENCE_LISTkullandığınız değişkene ayarlayın veya pypi.python.org/pypi/WsgiUnproxy
vdboor

@vdboor Biraz ayrıntı verebilir misiniz? Depoda IPWARE_META_PRECEDENCE_LIST bulamıyorum.
Monolith

2
@ThaJay Lütfen 2.0.0 itibariyle kullanmanız gerektiğini unutmayın get_client_ip(). get_real_ipkullanımdan kaldırıldı ve 3.0'da kaldırılacak.
un33k

77

Alexander'ın cevabı harika, ancak bazen HTTP_X_FORWARDED_FOR başlığında birden fazla IP döndüren proxy'lerin işlenmesinden yoksundur.

Gerçek IP genellikle burada açıklandığı gibi listenin sonundadır: http://en.wikipedia.org/wiki/X-Forwarded-For

Çözüm, İskender'in kodunun basit bir modifikasyonudur:

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[-1].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

5
Evet, ip listenin başında . Bu yanlış.
Pykler

4
Aslında, kullanıcı bir proxy'nin arkasındaysa kullanıcının dahili IP adresini, yani bir RFC 1918 adresini alırsınız. Çoğu durumda, bu hiç arzu edilmez. Bu çözüm en doğru adres olan istemcinin harici IP adresini (proxy adresi) almaya odaklanır.
Sævar

2
Teşekkür ederim. Genellikle anahtarları istediğimde, request.METAüstbilgiler sık ​​sık yanlış yaptığından varsayılan bir değer eklerim:request.META.get('REMOTE_ADDR', None)
Carl G

2
@CarlG kodunuz daha şeffaftır, ancak get yöntemi django.utils.datastructures.MultiValueDict öğesinden devralınır ve varsayılan değer Yok'tur. Ancak, aslında Yok'tan başka bir şey olmasını istiyorsanız, varsayılan bir değer eklemek kesinlikle mantıklıdır.
Sævar

2
İstekler ilk sunucunuza ulaştığında X-Forwarded-For'u temizlemediğiniz sürece, bu listedeki ilk değer kullanıcı tarafından sağlanır . Kötü niyetli bir kullanıcı istediği herhangi bir IP adresini kolayca taklit edebilir. İstediğiniz adres, sunucudaki herhangi birinden önceki ilk IP'dir, listede ilk olması gerekmez.
Eli

12

Yanchenko'nun cevabında bir gelişme önermek istiyorum.

X_FORWARDED_FOR listesindeki ilk ipi almak yerine, bazı yönlendiriciler protokole saygı duymadığından ve dahili ips'i listenin ilk değeri olarak görebildiğiniz için, bilinen bir dahili ipte olmayan ilk ipi alıyorum.

PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', )

def get_client_ip(request):
    """get the client ip from the request
    """
    remote_address = request.META.get('REMOTE_ADDR')
    # set the default value of the ip to be the REMOTE_ADDR if available
    # else None
    ip = remote_address
    # try to get the first non-proxy ip (not a private ip) from the
    # HTTP_X_FORWARDED_FOR
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        proxies = x_forwarded_for.split(',')
        # remove the private ips from the beginning
        while (len(proxies) > 0 and
                proxies[0].startswith(PRIVATE_IPS_PREFIX)):
            proxies.pop(0)
        # take the first ip which is not a private one (of a proxy)
        if len(proxies) > 0:
            ip = proxies[0]

    return ip

Umarım bu aynı sorunu yaşayan Google çalışanlarına yardımcı olur.


Bu kod, muhtemelen olması gerektiği gibi HTTP_X_FORWARDED_FOR alanını kontrol etmeden önce REMOTE_ADDR'den gelen ipin özel olup olmadığını kontrol etmez (ayrıca, '127.0.0.1' veya '127.' de muhtemelen IPIV6 eşdeğerleriyle birlikte PRIVATE_IPS_PREFIX'te olmalıdır.
Rasmus Kaj

1
Teknik olarak, bu önekler (172, 192) mutlaka özel adresler anlamına gelmez.
maniexx

2
Özel ağlar için atanan adres aralıkları şunlardır: 172.16.0.0–172.31.255.255 (16 “B sınıfı” ağlar), 192.168.0.0–192.168.255.255 (1 “B sınıfı” ağ) ve 10.0.0.0–10.255.255.255 (1 "A sınıfı" veya 256 "B sınıfı" ağ).
tzot

is_valid_ip tanımlanmamış
Prosenjit

7

İşte bunu başarmak için kısa bir astar:

request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR', '')).split(',')[0].strip()

3
Her ikisi de None döndürürse bir hata alırsınız.
Gourav Chawla

6

En basit çözüm (fastcgi + nignx kullanıyorsanız) itgorilla'nın yorumladığı şeydir:

Bu harika soru için teşekkürler. Fastcgi dosyam REMOTE_ADDR meta anahtarını geçirmiyordu. Aşağıdaki satırı nginx.conf dosyasına ekledim ve sorunu düzelttim: fastcgi_param REMOTE_ADDR $ remote_addr; - itgorilla

Ps: Bu cevabı sadece çözümünü daha görünür hale getirmek için ekledim.


1
Nginx (ters proxy) ve gunicorn için karşılaştırılabilir bir çözüm nedir? proxy_set_header REMOTE_ADDR $remote_addr;nginx.conf dosyasına eklendiğinde sorunu gidermez.
Hassan Baig

6

Daha Fazla Karışıklık Yok Django'nun son sürümlerinde, istemcinin Ip adresinin

request.META.get("REMOTE_ADDR")

daha fazla bilgi için Django Dokümanlarını kontrol edin


5

Benim durumumda yukarıdaki hiçbiri çalışır, bu yüzden uwsgi+ djangokaynak kodu kontrol etmek ve nginx statik param geçmek ve neden / nasıl ve aşağıda bulduğum olduğunu görmek zorunda.

Env info:
python sürümü: 2.7.5
Django sürümü: (1, 6, 6, 'final', 0)
nginx sürümü: nginx/1.6.0
uwsgi:2.0.7

Env ayar bilgisi:
nginx, 80 yukarı yönlü unix soketi olarak port uwsgi'de ters proxy dinleme olarak, isteğe eninde sonunda cevap verecektir

Django yapılandırma bilgileri:

USE_X_FORWARDED_HOST = True # with or without this line does not matter

nginx yapılandırma:

uwsgi_param      X-Real-IP              $remote_addr;
// uwsgi_param   X-Forwarded-For        $proxy_add_x_forwarded_for;
// uwsgi_param   HTTP_X_FORWARDED_FOR   $proxy_add_x_forwarded_for;

// hardcode for testing
uwsgi_param      X-Forwarded-For        "10.10.10.10";
uwsgi_param      HTTP_X_FORWARDED_FOR   "20.20.20.20";

django app tüm params elde:

X-Forwarded-For :       10.10.10.10
HTTP_X_FORWARDED_FOR :  20.20.20.20

Sonuç:

Temel olarak, nginx'te tam olarak aynı alan / param adını belirtmeniz ve request.META[field/param] django uygulamasında .

Ve şimdi bir ara katman (önleyici) eklemeye veya yalnızca HTTP_X_FORWARDED_FORbelirli görünümlerde ayrıştırma yapmaya karar verebilirsiniz .


2

İşlevselliğin Django'dan kaldırılmasının nedeni, başlığa en sonunda güvenilememesiydi. Nedeni parodi kolay. Örneğin, bir nginx ters proxy'sini yapılandırmanın önerilen yolu:

add_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-Real-Ip       $remote_addr;

Yaptığınızda:

curl -H 'X-Forwarded-For: 8.8.8.8, 192.168.1.2' http://192.168.1.3/

Myhost.com'daki nginx'iniz gönderecek:

X-Forwarded-For: 8.8.8.8, 192.168.1.2, 192.168.1.3

X-Real-IPKörü körüne talimatları izlerseniz ilk önceki vekil IP olacaktır.

Durumda kim kullanıcılardır bir konudur güvenerek gibi bir şey deneyebilirsiniz django-xff: https://pypi.python.org/pypi/django-xff/


1

Yukarıdaki cevapta da proxy eksikti. Ben kullanılan get_ip_address_from_requestden django_easy_timezones .

from easy_timezones.utils import get_ip_address_from_request, is_valid_ip, is_local_ip
ip = get_ip_address_from_request(request)
try:
    if is_valid_ip(ip):
        geoip_record = IpRange.objects.by_ip(ip)
except IpRange.DoesNotExist:
    return None

Ve işte yöntem get_ip_address_from_request, IPv4 ve IPv6 hazır:

def get_ip_address_from_request(request):
    """ Makes the best attempt to get the client's real IP or return the loopback """
    PRIVATE_IPS_PREFIX = ('10.', '172.', '192.', '127.')
    ip_address = ''
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
    if x_forwarded_for and ',' not in x_forwarded_for:
        if not x_forwarded_for.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_forwarded_for):
            ip_address = x_forwarded_for.strip()
    else:
        ips = [ip.strip() for ip in x_forwarded_for.split(',')]
        for ip in ips:
            if ip.startswith(PRIVATE_IPS_PREFIX):
                continue
            elif not is_valid_ip(ip):
                continue
            else:
                ip_address = ip
                break
    if not ip_address:
        x_real_ip = request.META.get('HTTP_X_REAL_IP', '')
        if x_real_ip:
            if not x_real_ip.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(x_real_ip):
                ip_address = x_real_ip.strip()
    if not ip_address:
        remote_addr = request.META.get('REMOTE_ADDR', '')
        if remote_addr:
            if not remote_addr.startswith(PRIVATE_IPS_PREFIX) and is_valid_ip(remote_addr):
                ip_address = remote_addr.strip()
    if not ip_address:
        ip_address = '127.0.0.1'
    return ip_address

0

Django.VERSION (2, 1, 1, 'final', 0) istek işleyicisinde

sock=request._stream.stream.raw._sock
#<socket.socket fd=1236, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.111', 8000), raddr=('192.168.1.111', 64725)>
client_ip,port=sock.getpeername()

yukarıdaki kodu iki kez ararsanız,

AttributeError ("'_ io.BytesIO' nesnesinin 'stream'" özelliği yok,)

AttributeError ("'LimitedStream' nesnesinin 'raw'" özelliği yok)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.