From e7c53abe4d89eb41419492db5e42e3305d489df9 Mon Sep 17 00:00:00 2001 From: Delian Asparouhov Date: Sun, 7 Jul 2019 15:58:30 -0700 Subject: [PATCH] Switch to a better geolocation API --- db.sqlite3 | Bin 135168 -> 143360 bytes pixels/migrations/0002_requests_region.py | 19 ++++ pixels/migrations/0003_auto_20190707_2211.py | 31 ++++++ pixels/models.py | 4 + pixels/templates/index.html | 7 ++ pixels/templates/pixel.html | 25 +++-- pixels/views.py | 97 ++++++++++++------- requirements.txt | 3 +- supertracker/settings.py | 2 + 9 files changed, 145 insertions(+), 43 deletions(-) create mode 100644 pixels/migrations/0002_requests_region.py create mode 100644 pixels/migrations/0003_auto_20190707_2211.py diff --git a/db.sqlite3 b/db.sqlite3 index a471fcc5925c7c1d54d4ccccf7e1faf609981266..c02e629065db1340e8f7b961491c233183611495 100644 GIT binary patch literal 143360 zcmeI5eQX=&eaCs?n<$AqS+dMHwjxThY_qbcdq0wC)+n-~D7L6rmSf9ka6OVI>TJF! zBxPA?v4M({4(&Ez8CswPwr&FkWY~ah-GE}~+F=>G?yYMvq#L%j!?u6A257qtX!=K1 zV9)dL$U7cMk#Wo*wts|txqF`9^LxI}=XvhAdtO3bzI0J9YoV<|F{hS8ykpowQI01= zA&0|p75$q=|7xFgbmFRAp#M^q^LD3K9T6eq#>VtJ*d|E7;~yVc(jObN`i~fpaY4Y9rgxaK;l;q6JPa&bSGJkNhP;pt#u#iN_Ug9PLgX0 zklGt`f3V_=Q3umq$!#dC!lcqXCng!Qj|u0;p+@)k}BlOTE485_jfehq_{0XmNX$rs>ZaVThxgetF-fC(QR7e_G3$cWM1}rgZs18 z0c}=^nWQO$R3WY9)tsg6`IkCp~TuoC~@WD#n5x> zE2|6Zo1qI!o1uj(8*3{Gv4Nx#CoNO0FFn1qzLZ#8x@^hR$Riv&k7L(~q?N?V z#>&FQi?^al!FwU6ko zR)}qv$X@=C&l{YWpkAGBL{@Kn18pGP6`dWmRN5;PQH#8#7E{}5@hrE3vDqg=N1BFd({ zWlhMT7Ftf#vt~}RXy$~{W1(erYpYo#icwQ#{T5Pb*N;oA+$iXI4IyeOewZEb1eYc# z2k8z=FJ{r}nu00{*`xOxd}h4hu=UfygG9eK7>iMNWV3#&ZOOd5sy9YcsINuMHyPbk zGpKsG=ZzUrHw%SdpnB2ARE9m?;N&D#xoHDRn3Ixu+kv|)SdDEk+T5PQyw?+4o;=iadNY@$VyRnc9g7|)Urwry@_NMSxTn) zG@m4|AuSTP4VaB1iWY{4d#=8Pv`B`_Ue|;N7DN_$t;#sPL7t}$c1ex5NZBID)mZZ_ zEbc+=vsUCtu&KUvt<=lZIy(GZp9eSXO;S#yzbt7l?rQmzX1jDYl+PA<7T<&CP|9#+ zlkx`V=cxnL$}Xp8ifS1T#tbfIKwpF3siye5qK3Gb&mB%|eDE~(7xq|`G^;l~T~I$T z_Lv_Xj}H9EL4Te8ES;t&1OF8GrNC4EpZUM+-}4JY-yizI(DOq_2ERG@`N7QKIb;YQ zAOHk_01yBIKmZ5;0U&VS5cu#>x8wNZ3p@I4En8w3h9@&A3dgW9MrLG%<5;vJ!N%qo z8U2MgF3xgsMv6#mjANwOC`v7wsX3*H#}%b=37urnNWhwujf-qtU?Z|5@hr!VprkWq zQdZIPdRbSq^(0b=g{t$n4Tiv2c6evcglcW@~z2rC4l{Mjlpc9vDTt0O*zBAT2_rxmSOEEJU-0avTRI_t+?HeAkvi1>3MRSqse8oxK-RI^^7NC z3wdQLt7eqcc0ot;0yRgJWpM9WynJJS^>Ll=^`o!tjIW#i-GQAVqg^_i8R+sRWc_GEZRozA&_ zBZACZ;5myi$~j-NHg_r<9YBt~^qzzM5&d`cztUf!f0N#$KmGoJgPec>5C8%|00;m9 zAOHk_01yBIKmZ6lzywBJ$H$FXf!ZD-?@`zI397c;$M~=3q-$oHvhIkmoV)$5u`x1@ zfM*C?0oPcNT#@Mm=csFJl3dm&4Dx+PT)}Z4nG&e&V!-?Vz4R>y{X_a+=)a}^jQ#@s zDRc=RAOHk_01yBIKmZ5;0U!VbfB+Bx0zlwB6BzNj9LLA2y8>$C|51<2F@C}}{y*V% zIcBCUWB!ume8}Z;jE&(f0(kuIccQBxz9QrQkv?=ei7(CZ{}IaN2#z<5|MB|&N&nwF z=pUoi|F6+sq<@`$h0f7e>1A4=C+T6D3j8?my}&mDUkdz2;FUl=@X^3>fD23nhWu~) z{~aZS4-fzXKmZ5;0U!VbfB+Bx0zd!=yzc}?UDO%MdrL>#&})OrlVdJwmhz%+Ce+Z^ zhSow)R725svF0;o=%XVpYKHRSZ$hXU9w$K_;?fwIK62DWg(;8W(FhDy0}Y2p;IShv zYMSz3e@4jIF&8yQxrsZ=IQHlxE{dhxhD%GtXf?ucYl#>k1#n~6me3=#i<+QZ#62Dn z4-)|{!!Hh@t09I%91;k)sBy}LJ&{55u#1|coWwf00e*l5C8%|-~&wH z;Bm^mddIyGJu=!F(NWAp?_VwHR2eUERp$u5YaDCpIqes~eY?#QF8@wX0Xe)t7eUm27M~eQmvPjeq{7 z>(`#$Qm0`o+a(w}{=-F*e0LzkiL} z-cE5Dl;^WISC`jsCax|Cs~b1>5*w?xSFb*uUERp+uWqb}tIMn68Y;_XZk6A>adZFr zc~p+&>o->N%oDZmCgd3Wou;B3=i~^>#DtiL*Z(Q{yAJf{{Qy@oNCN^u00;m9AOHk_ z01yBIKmZ5;0U!Vb+7Os_uKGI7is1Es$8Z}55DWx>01yBIKmZ5;0U!VbfB+Bx0zd!= zJP-uP{(t(T4)g~fAOHk_01yBIKmZ5;0U!VbfB+Bx0zlv{2vjC1zoQaf@E!Nh_rQClS3++y7s;W$^xg zE-D-Q|KE1dZ#(e+3IHD<00e*l5C8%|00;m9AOHk_01yBIK;VHP;By}LRVN2v|NjG1 zsZbCg00e*l5C8%|00;m9AOHk_01yBIZ3)2mzbzVw00KY&2mk>f00e*l5C8%|00;m9 zAn?Etz?=S0(l0pZ|D%6O|1bSR`up^M(BGlIO@EXA8~QKluh3tj|A_uQ`giCr(4V7! zjs9i&lXQjtIK4|3Xq`@>gzy0ZKmZ5;0U!VbfB+Bx0zd!=00AKIuoG}QDaw1298Zwr zqvUuTA3cwdV~`w=k>eOXx{s3MC^?Rh;}Lvx4U;2HjsbG?GQ$*|HIx$q5MDq2mk>f00e*l5C8%|00;m9An-5{AYb|i zzyJ3zR5g?l2mk>f00e*l5C8%|00;m9AOHj&VgfM!e~2p|iVg&T01yBIKmZ5;0U!Vb zfB+Bx0uK!V82>*sRSg9N0zd!=00AHX1b_e#00KY&2mpbHm;j9bAL7b~q5}aS00e*l z5C8%|00;m9AOHk_z(Yd-#{UmZRYO6601yBIKmZ5;0U!VbfB+Bx0zlv)CgAt}#6eSE zchIK-|LFU+=MDef(5E~{-B(?o8T_g5BmGN*A9nsV_4U3r>NUr=98cIl4gSdUN^otC zs<3)Kt=(34%iGFsNh>OuVqtfuR6WhE_UNYc zQ=zp)v)NSW>=emWPfvv_9~tomZz4&h!zB@g3epx6c#%mlDP=dWzi3x5Y6!fNSh;ki zv(DB|JF7kynyMv5C5RvK1~W0LqM9YB#_njvoL(yFg?vdd&v7B5FaP{w-r(FE^~w3N zn#^kH8)`mNP}Foz&nwwN#(q6+lxt(*sf$aYDSI>~ieQHHd|AtA#ZY2xBb2yu@nYz? z^_A6y_07xl-NKeP*Y{3OeyO*Z7P&j%NjnbCq)K@sQbW1I!w$Ul=pWu zl0#iOmc738^wRoLVsYtmy{y&*MsdTT^Vn{kFj+~gY^*F?ytruuE}ciY)LL0H7;6so zM%1-m1Kn0u%}j+#IW?O_@kG$m<;e|Fd#d;#@9t7cnjN>}aUap#j{IVP3dinZ2#q$}N3 z_ZDZJB-av5g%6lP()HaLC0(D{kcmmjem!L9F}gl`v~kZi-{FUKeWXdZ>@eS3zfdn^ zvz#W!#J0O0Cp>uC@mz3Xf_inj5m_C^KpRMH=v4N``hykU0(CHNwip7%ezVsf=?!D$h zXWk?+SrlWUAk+~X??}g5+#V#mb`I=%t9Do=IV1h&L89LqjK!!svRS{?SW|xW#%K!l zwW#?fqc+Xp@b`ndynEZ2o9d(s_a+6SBpn|8Vf5a6EI2tyRc=~=R-(te!4!JtZrUop zZ97zBqNb)<%Zsqx^mx1WU^lF{?V3+}!>}i?Hso++c+~qrdvm`*qfW#bqYkD`oJ~#7 zyraxY#1A1RmF77y$(TJzQ_|t~y+@O6SJ_yIH#umyA)DH0_~6=*H@L7s-Fe(>P^7}_ zwPyAg_85PQJGG^YySiJ6rye}g8fvY@2&3t_W+v$JUVX056P%r;HVN3$i&?#_DJAX2 zT`ix|Y?sakgto}Dc{Qhf00e*l5C8%| z-~lAycP^v#|I?0v5Bq+%|9kx-p108If6nA?8Va<%BrOAjD| zE{aq+8ubP*hp9@8OidaSJ|(mojhDQ&+dFzuQ}E=9qKYYrlO(>;BxFno85qC|>jgmnYj5(Nv_c=J^E6dxA?7 zhbAHuv(+^CbnxXc=Ot@-lO_!uLA>IVce$2#_#)1ZS}N@oifAgawo`-Uq75^VS+!Kg zTTb-6b-EGl!a!|CFDa#6G@*-SO@)$$LbkrQ0mb6b&Zv>$mR>}8G@A03N?K@01%4GZh$ z{I+7E?swB5>zKOacG~w%#0T=4BVm28^z3; z+%D$jV=X!mGxn}}5j*C?*C~xY&zi`{wZmGawan&CYTHiL*ho3+VnVp`%qg^!h^Hza zw?6BvLKL)~pk&pgmQ`}7?z4(4CDVMGPm+pnq1@H7@>>`l?z!QaXcdIX)HUH%dLj!o zz31XtZJ*vaZ+~vg$kh>dV0?Q4+MI zD%lNA8pUdS1!im-F&&YZ+kJW zad|gwUADF5k}c#j9ZZT^28}>0FFH0CDYWjJFSn0RYq#{2O~T7Js&)D5-pe0DXXugn z>K^GoZ53*}wAH087OL^En(jq4T3@XTr8Z3f*8jU0{euY*00KY&2mk>f00e*l5C8%| z00;nq`<(#n|G(dB5cC59AOHk_01yBIKmZ5;0U!VbfB+EaOaR9Jof&}z5C8%|00;m9 zAOHk_01yBIKmZ5;f%}~RjQ{WV8U+1700;m9AOHk_01yBIKmZ5;0U!VbIun5Le`iKu z0R(^m5C8%|00;m9AOHk_01yBIK;V8S;Kv0RCZ#xujwi?&9Do2200KY&2mk>f00e*l z5C8%|00;nq2Z{h5|DPOu!$E(S{v!R0bdrwKLxFz_{7K-mfnp#Lhy+Ib|Ly+={~!8a z@o)I2hu$7~bLjVn?hJiw=)*%Jga3sR!UqTd0U!VbfB+Bx0zd!=0Dx(Ly+t)2!U3ULz}XYXYL@q+pX=1n&s5ifv0XLDw0yK`nc7zKnMTWMP~CFs zII^6X^Q!4|gF|#}+B`yRJVc%Go6&V~#`WnSvI(;uLz~6QkXExhR<$#fS?mm9Mo4H3 z*-g)Sur7-cmStSd9z_;&vYV(fek`)QzoXTa8DSwDI6sPv*~i_6Je#>8u_3`oHG!ei zmcYAEeruwn}Mp0 zp`dPKNFdiIeaL3wj1z098(r%|1b6#XQFZUUG%m{32h%y7u zd8*ciGBW~O8}iJ+({5xPX8W*4GZdR*p=QV_m)qex!xFLRcd^mWF&6jhQc*yNgq(FE zt69+yYPBjb;Bo{XTYDkQERB0zd!=00AHX z1b_e#00KY&2mk>f@IDg2<9~|&PY3#g4-fzXKmZ5;0U!VbfB+Bx0zd!=00AIy-x3&i zF8DZxjmE-uP(PL8llOo)kYR4$5s2bXS|ekZWu|AGJK{l|x1ACd?EYVgXy zKM$F>+Q(C`Q{#@;?j@W`|AgOhd?7Y{ z=ETJ0@!&Yx|Ddb+S_v2#mw=Pwq7;!OR$}C*^h{P$w?YZ6yjLjRES(Fjx{|_2B#9NGy`^%rD=AEb=lN)lo9$>vDXbLd(v1`@8j;y(RP1eA8SYAo7~uq#WqK=x?nVkDL}Cmt z2|c};`vQK4-+Xmyd}(6&xWGr4XcRTsUiK(`{=-Eud_?4U#G;2HJ~3o!2>QouAht%8N6cJ9B1y5>;VC9kM95xPTr*tSm}R zNH3!u;7=FTd`d5+3XNkAITB?Uk(pyT4h=rg>yGZhT}M3=+fYXeaeSPO#d#(o%Yw)> z!F*R)xjSct>WxJ$Q)Jnu4&q&9+)_sJBw)yOF~3ktipl9WOm?wvoA6yI!j^W zELyAMS+Tdv_5HIQrSNF4CXd#tqi82xPpN#UvlJ0;xH-@*_9M7!XhEf`#SDv1%*0GiN*!AgE}T~T+?2Hdno1q0k;Xtga7~l delta 292 zcmZp8z|pXPV}i7xIs*fPA`ruX+e95>M)i#eOZb_ExYH-I3#e{ZRIujWoG!hdWwWAz zH@~PbAG0=NL1sm2PH}uuYGG+=aY-@T&>`(x|;{123J#xCA+w`He*ZucJ(~Q7{=}Y^BEZ@ zi0}f9VdI^~z(14Ui0?FC3h!UuX+YOB@p7lKaWY6c>Z+^DDo*d^Ws2cWWkm@501L1n d1d{ncDwq)h=fDC?2mwoekP1eGz!G_;7y!1eL{0zz diff --git a/pixels/migrations/0002_requests_region.py b/pixels/migrations/0002_requests_region.py new file mode 100644 index 0000000..2f21430 --- /dev/null +++ b/pixels/migrations/0002_requests_region.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.3 on 2019-07-07 21:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pixels', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='requests', + name='region', + field=models.CharField(default='', max_length=200), + preserve_default=False, + ), + ] diff --git a/pixels/migrations/0003_auto_20190707_2211.py b/pixels/migrations/0003_auto_20190707_2211.py new file mode 100644 index 0000000..632ae81 --- /dev/null +++ b/pixels/migrations/0003_auto_20190707_2211.py @@ -0,0 +1,31 @@ +# Generated by Django 2.2.3 on 2019-07-07 22:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pixels', '0002_requests_region'), + ] + + operations = [ + migrations.AddField( + model_name='requests', + name='client', + field=models.CharField(default='', max_length=200), + preserve_default=False, + ), + migrations.AddField( + model_name='requests', + name='device', + field=models.CharField(default='', max_length=200), + preserve_default=False, + ), + migrations.AddField( + model_name='requests', + name='os', + field=models.CharField(default='', max_length=200), + preserve_default=False, + ), + ] diff --git a/pixels/models.py b/pixels/models.py index 3a4d0d0..27de641 100644 --- a/pixels/models.py +++ b/pixels/models.py @@ -6,6 +6,10 @@ class Requests(models.Model): username = models.CharField(max_length=200) time_opened = models.DateTimeField('time opened') isp = models.CharField(max_length=200) + client = models.CharField(max_length=200) + os = models.CharField(max_length=200) + device = models.CharField(max_length=200) + region = models.CharField(max_length=200) city = models.CharField(max_length=200) country_name = models.CharField(max_length=200) latitude = models.CharField(max_length=200) diff --git a/pixels/templates/index.html b/pixels/templates/index.html index 3a42403..037b69a 100644 --- a/pixels/templates/index.html +++ b/pixels/templates/index.html @@ -23,6 +23,13 @@

How supertracker worksBy hitting that server, you are revealing the device, location, and time you are opening that email
  • Supertracker lets anyone make their own tracking pixel and then see everywhere that pixel has been accessed from
  • +

    How to prevent yourself from being tracked

    +
      +
    • Use an email client that does proxy image loading so your location isn't revealed since they load the image onto their own server before sending it to you
    • + +
    • Disable remote image loading in your email client. Here's a guide for GMail (link)
    • +
    • And the real permanent solution is to demand that your email clients immediately load all external images the moment the email is received rather than opened, so none of your information by using email. For example I believe this is the head of GMail and could implement this feature (link)
    • +

    Choose your Pixel and Username!

    diff --git a/pixels/templates/pixel.html b/pixels/templates/pixel.html index 962b868..f1b0e45 100644 --- a/pixels/templates/pixel.html +++ b/pixels/templates/pixel.html @@ -9,11 +9,12 @@

    @{{ username }} pixel

    -
    -

    How to use your pixel

    +
    +

    How to pixel

    • Copy and paste the image below by right clicking below this text and selecting copy image
    • -
    • Paste the image into your email and send it off the email
    • +
    • This will only work on desktop
    • +
    • Paste the image into your email and send off the email

    @@ -21,22 +22,26 @@

    How to use your pixel

    image is in the space above

    -
    +

    Where your pixel has been

    + + - {% for request in requests %} + {% for request in requests reversed %} - - + + + + @@ -51,7 +56,11 @@

    Where your pixel has been< {% endblock %} \ No newline at end of file diff --git a/pixels/views.py b/pixels/views.py index d1e95b8..6d7874b 100644 --- a/pixels/views.py +++ b/pixels/views.py @@ -3,11 +3,13 @@ from django.shortcuts import render from django.utils import timezone + from pixels.models import Requests import requests import logging -import xmltodict +import json +from ua_parser import user_agent_parser logger = logging.getLogger('pixels') @@ -35,44 +37,71 @@ def show_image(request): # logger.error(ip) # logger.error("AFTER") - ipstack_url = "http://api.geoiplookup.net/?query=" + ip + # if you are trying to shut me down, this is one of the more vulnerable parts, just fire off enough requests here: + ipdata_url = "https://api.ipdata.co/"+ip+"?api-key=2426c12568210a843d3ac8e14d9933764e73f0d8e84fc92834314a58" # logger.error("IP URL") # logger.error(ipstack_url) - ip_info = requests.get(ipstack_url) - - json_info = xmltodict.parse(ip_info.content) - + ip_info = requests.get(ipdata_url) + ip_content = json.loads(ip_info.content) # logger.error("IP INFO") - logger.error(json_info) + logger.error(ip_content) + + logger.error("META") + logger.error(request.META) + + client = '' + os = '' + device = '' + + if 'HTTP_USER_AGENT' in request.META.keys(): + ua_string = request.META['HTTP_USER_AGENT'] + parsed_string = user_agent_parser.Parse(ua_string) + logger.error("PARSED USER AGENT") + logger.error(parsed_string) + if 'user_agent' in parsed_string.keys(): + agent_data = parsed_string['user_agent'] + if 'family' in agent_data.keys(): + client = agent_data['family'] + + if 'os' in parsed_string.keys(): + os_data = parsed_string['os'] + if 'family' in os_data.keys(): + os = os_data['family'] + + if 'device' in parsed_string.keys(): + device_data = parsed_string['device'] + if 'family' in device_data.keys(): + device = device_data['family'] # get all the info VERY inefficiently - if 'ip' in json_info.keys(): - ip = json_info['ip'] - if 'results' in ip.keys(): - results = ip['results'] - if 'result' in results.keys(): - result = results['result'] - if 'countryname' in result.keys(): - countryname = result['countryname'] - else: - countryname = '' - if 'city' in result.keys(): - city = result['city'] - else: - city = '' - if 'latitude' in result.keys(): - latitude = result['latitude'] - else: - latitude = '' - if 'longitude' in result.keys(): - longitude = result['longitude'] - else: - longitude = '' - if 'isp' in result.keys(): - isp = result['isp'] - else: - isp = '' + + if 'ip' in ip_content.keys(): + result = ip_content + if 'region' in result.keys(): + region = result['region'] + else: + region = '' + if 'country_name' in result.keys(): + country_name = result['country_name'] + else: + country_name = '' + if 'city' in result.keys(): + city = result['city'] + else: + city = '' + if 'latitude' in result.keys(): + latitude = result['latitude'] + else: + latitude = '' + if 'longitude' in result.keys(): + longitude = result['longitude'] + else: + longitude = '' + if 'organisation' in result.keys(): + isp = result['organisation'] + else: + isp = '' # logger.error(request.META['HTTP_USER_AGENT']) # logger.error(request.META['HTTP_X_FORWARDED_FOR']) @@ -82,7 +111,7 @@ def show_image(request): pixel = request.GET.get('pixel','') # STORE THE REQUEST INFO - r = Requests(username=username, isp=isp, city=city, country_name=countryname, latitude=latitude, longitude=longitude, time_opened=timezone.now()) + r = Requests(username=username, isp=isp, client=client, os=os, device=device, city=city, region=region, country_name=country_name, latitude=latitude, longitude=longitude, time_opened=timezone.now()) r.save() if pixel == 'pikachu': diff --git a/requirements.txt b/requirements.txt index 12bb43a..133bf30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests -xmltodict \ No newline at end of file +json +ua-parser \ No newline at end of file diff --git a/supertracker/settings.py b/supertracker/settings.py index b371101..f48902d 100644 --- a/supertracker/settings.py +++ b/supertracker/settings.py @@ -52,6 +52,8 @@ ROOT_URLCONF = 'supertracker.urls' +USE_TZ = True + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates',

    Time Opened DeviceOSClient Country City Latitude Longitude GMaps link
    3:00pm iPhone {{ request.time_opened.timestamp }} {{ request.device }} {{ request.os }} {{ request.client }} {{ request.country_name }} {{ request.city }} {{ request.latitude }}