Làm thế nào để tôi có được địa chỉ IP người dùng trong django?


287

Làm cách nào để có IP của người dùng trong django?

Tôi có một cái nhìn như thế này:

# 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())

Nhưng tôi nhận được lỗi này:

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
Hãy thử yêu cầu bán phá giá.META.keys ()
Martin v. Löwis

2
['HTTP_COOKIE', 'SCRIPT_NAME', 'YÊU CẦU , 'SERVER_PORT', 'wsgi.input', 'HTTP_HOST', 'wsgi.multithread', 'HTTP_CACHE_CONTROL', 'HTTP_ACCEPT', 'wsgi.version', 'wsgi.run_once', ' đa xử lý ',' HTTP_ACCEPT_LANGUAGE ',' CONTENT_TYPE ',' CSRF_COOKIE ',' HTTP_ACCEPT_ENCODING ']
avatar

2
Cảm ơn bạn cho câu hỏi tuyệt vời này. Fastcgi của tôi đã không vượt qua khóa meta REMOTE_ADDR. Tôi đã thêm dòng dưới đây trong nginx.conf và đã khắc phục sự cố: fastcgi_param REMOTE_ADDR $ remote_addr;
avatar

Câu trả lời:


434
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

Đảm bảo rằng bạn có proxy ngược (nếu có) được định cấu hình chính xác (ví dụ: mod_rpafđược cài đặt cho Apache).

Lưu ý: ở trên sử dụng mục đầu tiên trong X-Forwarded-For, nhưng bạn có thể muốn sử dụng mục cuối cùng (ví dụ: trong trường hợp Heroku: Nhận địa chỉ IP thực của khách hàng trên Heroku )

Và sau đó chỉ cần chuyển yêu cầu làm đối số cho nó;

get_client_ip(request)

8
Gọi ip = get_client_ip(request)trong chức năng xem của bạn.
yanchenko

4
Địa chỉ IP của máy khách thực không phải là địa chỉ đầu tiên mà là địa chỉ cuối cùng trong HTTP_X_FORWARDED_FOR (xem trang wikipedia)
jujule

5
@jujule Điều đó không đúng. Các định dạng thường X-Forwarded-For: client, proxy1, proxy2. Vì vậy, địa chỉ đầu tiên là của khách hàng.
Thác nước Michael

51
Chức năng này là nguy hiểm. Với nhiều thiết lập, người dùng độc hại có thể dễ dàng khiến chức năng này trả về bất kỳ địa chỉ nào họ muốn (thay vì địa chỉ thật). Xem esd.io/blog/flask-apps-heroku-real-ip-spoofing.html
Eli

8
Từ các tài liệu django "dựa vào REMOTE_ADDR hoặc các giá trị tương tự được biết đến rộng rãi là một thực tiễn tồi tệ nhất" ( djangoproject.com/weblog/2009/jul/28/security/#secondary- phát hành )
Zags

209

Bạn có thể sử dụng django-ipware hỗ trợ Python 2 & 3 và xử lý IPv4 & IPv6 .

Tải về:

pip install django-ipware

Cách sử dụng đơn giản:

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

Sử dụng nâng cao:

  • Tiêu đề tùy chỉnh - Tiêu đề yêu cầu tùy chỉnh cho ipware để xem:

    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'])
  • Số lượng proxy - Máy chủ Django đứng sau một số proxy cố định:

    i, r = get_client_ip(request, proxy_count=1)
  • Proxy đáng tin cậy - Máy chủ Django đứng sau một hoặc nhiều proxy được biết đến & đáng tin cậy:

    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.'))

Ghi chú: đọc thông báo này .


17
Hãy xem mã nguồn của nó. Nó xử lý tất cả các biến chứng được xác định bởi các câu trả lời khác ở đây.
HostedMetrics.com

5
Thx @Heliodor - Đúng, tôi đã làm cho mô-đun rất đơn giản cho trường hợp sử dụng trung bình và rất linh hoạt cho trường hợp sử dụng phức tạp. Tối thiểu, bạn muốn xem trang github của nó trước khi tự lăn.
un33k

3
LƯU Ý rằng các cài đặt của django-ipware không an toàn theo mặc định! Bất cứ ai cũng có thể vượt qua một trong các biến khác và trang web của bạn sẽ đăng nhập IP đó. Luôn đặt thành IPWARE_META_PRECEDENCE_LISTbiến mà bạn sử dụng hoặc sử dụng thay thế như pypi.python.org/pypi/WsgiUnproxy
vdboor

@vdboor Bạn có thể giải thích một chút không? Tôi không thể tìm thấy IPWARE_META_PRECEDENCE_LIST trong repo.
Monolith

2
@ThaJay Xin lưu ý rằng kể từ 2.0.0, bạn nên sử dụng get_client_ip(). get_real_ipkhông được chấp nhận và sẽ bị xóa trong 3.0.
un33k

77

Câu trả lời của Alexanderr rất hay, nhưng thiếu xử lý các proxy đôi khi trả về nhiều IP trong tiêu đề HTTP_X_FORWARDED_FOR.

IP thực thường nằm ở cuối danh sách, như được giải thích ở đây: http://en.wikipedia.org/wiki/X-Forwarded-For

Giải pháp là một sửa đổi đơn giản về mã của Alexandre:

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
Yup, ip là ở đầu danh sách. Điều này ở đây là sai.
Pykler

4
Trên thực tế, nếu người dùng đứng sau proxy, bạn sẽ nhận được địa chỉ IP nội bộ của người dùng, tức là địa chỉ RFC 1918. Trong hầu hết các trường hợp, điều đó không được mong muốn chút nào. Giải pháp này tập trung vào việc lấy địa chỉ IP bên ngoài của máy khách (địa chỉ proxy), đây là địa chỉ đúng nhất.
Sævar

2
Cảm ơn bạn. Thông thường khi tôi yêu cầu các khóa từ request.METAtôi bao gồm một giá trị mặc định vì các tiêu đề thường bắt chước:request.META.get('REMOTE_ADDR', None)
Carl G

2
@CarlG mã của bạn trong suốt hơn, nhưng phương thức get được kế thừa từ django.utils.datastructures.MultiValueDict và giá trị mặc định là Không có. Nhưng nó chắc chắn có ý nghĩa bao gồm một giá trị mặc định nếu bạn thực sự muốn nó là một cái gì đó khác hơn là Không có.
Sævar

2
Trừ khi bạn đang kiểm tra X-Forwarded-For khi các yêu cầu tấn công máy chủ đầu tiên của bạn, thì giá trị đầu tiên trong danh sách đó là do người dùng cung cấp . Một người dùng độc hại có thể dễ dàng giả mạo bất kỳ địa chỉ IP nào họ muốn. Địa chỉ bạn muốn là IP đầu tiên trước bất kỳ máy chủ nào của bạn, không nhất thiết phải là địa chỉ đầu tiên trong danh sách.
Eli

12

Tôi muốn đề xuất một cải tiến cho câu trả lời của yanchenko.

Thay vì lấy ip đầu tiên trong danh sách X_FORWARDED_FOR, tôi lấy ip đầu tiên mà không phải là ip nội bộ đã biết, vì một số bộ định tuyến không tôn trọng giao thức và bạn có thể xem ips nội bộ là giá trị đầu tiên của danh sách.

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

Tôi hy vọng điều này sẽ giúp các nhân viên Google có cùng vấn đề.


Mã này không kiểm tra xem ip từ REMOTE_ADDR là tin trước khi kiểm tra các lĩnh vực HTTP_X_FORWARDED_FOR, vì nó có lẽ nên (cũng, '127.0.0.1' hoặc '127.' lẽ phải ở trong PRIVATE_IPS_PREFIX, cùng với các khoản tương đương IPv6.
Rasmus Kaj

1
Về mặt kỹ thuật, các tiền tố đó (172, 192) không nhất thiết có nghĩa là địa chỉ riêng.
maniexx

2
Phạm vi địa chỉ được chỉ định cho các mạng riêng là: 172.16.0.0 .172.31.255.255 (16 mạng lớp Biêu), 192.168.0.01919.168.255.255 (mạng 1 B lớp lớp 1) và 10.0.0.0 Khăn10.255.255.255 (1 Các lớp mạng N hạng A hoặc 256 mạng Biên mạng.
tzot

is_valid_ip không được xác định
Prosenjit

7

đây là một đoạn ngắn để thực hiện điều này:

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

3
Nếu cả hai trả về Không thì bạn sẽ gặp lỗi.
Gourav Chawla

6

Giải pháp đơn giản nhất (trong trường hợp bạn đang sử dụng fastcgi + nignx) là những gì itgorilla nhận xét:

Cảm ơn bạn cho câu hỏi tuyệt vời này. Fastcgi của tôi đã không vượt qua khóa meta REMOTE_ADDR. Tôi đã thêm dòng dưới đây trong nginx.conf và đã khắc phục sự cố: fastcgi_param REMOTE_ADDR $ remote_addr; - itgorilla

Ps: Tôi đã thêm câu trả lời này chỉ để làm cho giải pháp của anh ấy rõ hơn.


1
Giải pháp tương đương cho nginx (proxy ngược) và gunicorn là gì? proxy_set_header REMOTE_ADDR $remote_addr;không làm giảm bớt vấn đề khi được thêm vào nginx.conf.
Hassan Baig

6

Không còn nhầm lẫn Trong các phiên bản gần đây của Django, có đề cập rõ ràng rằng địa chỉ Ip của máy khách có sẵn tại

request.META.get("REMOTE_ADDR")

để biết thêm thông tin, hãy kiểm tra Django Docs


5

Trong trường hợp của tôi, không có cách nào ở trên hoạt động, vì vậy tôi phải kiểm tra uwsgi+ djangomã nguồn và truyền tham số tĩnh trong nginx và xem tại sao / như thế nào, và dưới đây là những gì tôi đã tìm thấy.

Thông tin Env:
phiên bản python : Phiên bản 2.7.5
Django: (1, 6, 6, 'final', 0)
phiên bản nginx: nginx/1.6.0
uwsgi:2.0.7

Thông tin cài đặt Env:
nginx làm proxy ngược nghe tại cổng 80 uwsgi dưới dạng ổ cắm unix ngược dòng, sẽ đáp ứng yêu cầu cuối cùng

Thông tin cấu hình Django:

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

cấu hình nginx:

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";

nhận được tất cả các thông số trong ứng dụng django:

X-Forwarded-For :       10.10.10.10
HTTP_X_FORWARDED_FOR :  20.20.20.20

Phần kết luận:

Về cơ bản, bạn phải chỉ định chính xác cùng tên trường / param trong nginx và sử dụng request.META[field/param] trong ứng dụng django.

Và bây giờ bạn có thể quyết định có nên thêm phần mềm trung gian (đánh chặn) hay chỉ phân tích cú pháp HTTP_X_FORWARDED_FORtrong một số khung nhìn nhất định.


2

Lý do chức năng đã bị xóa khỏi Django ban đầu là vì tiêu đề cuối cùng không thể tin cậy được. Lý do là nó dễ bị giả mạo. Ví dụ: cách được đề xuất để định cấu hình proxy ngược nginx là:

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

Khi bạn làm:

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

Nginx của bạn trong myhost.com sẽ gửi tiếp:

X-Forwarded-For: 8.8.8.8, 192.168.1.2, 192.168.1.3

Các X-Real-IP sẽ là IP của proxy trước tiên nếu bạn làm theo hướng dẫn một cách mù quáng.

Trong trường hợp tin tưởng người dùng của bạn là một vấn đề, bạn có thể thử một cái gì đó như django-xff: https://pypi.python.org/pypi/django-xff/


1

Tôi cũng bị thiếu proxy trong câu trả lời ở trên. Tôi đã sử dụng get_ip_address_from_requesttừ 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

Và đây là phương thức get_ip_address_from_request, IPv4 và IPv6 đã sẵn sàng:

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

Trong trình xử lý yêu cầu django.VERSION (2, 1, 1, 'Final', 0)

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

nếu bạn gọi mã trên hai lần, bạn có thể nhận được

Đối tượng AttributionError ("'_ io.BytesIO' không có thuộc tính 'stream'",)

AttributionError (đối tượng "'LimitedStream' không có thuộc tính 'raw'")

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.