<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[The ipdata Blog]]></title><description><![CDATA[Build location aware applications on the most reliable IP Geolocation API]]></description><link>https://ipdata.co/blog/</link><image><url>https://ipdata.co/blog/favicon.png</url><title>The ipdata Blog</title><link>https://ipdata.co/blog/</link></image><generator>Ghost 4.42</generator><lastBuildDate>Fri, 16 Oct 2026 16:26:44 GMT</lastBuildDate><atom:link href="https://ipdata.co/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[C vs Golang Python extension performance]]></title><description><![CDATA[<p>I recently stumbled across the <a href="https://github.com/go-python/gopy">GoPy</a> module that makes it dead easy to convert any Golang package into a Python module.</p><p><br>Here&apos;s how you would use it</p><pre><code class="language-bash">go get github.com/jonathan-kosgei/geodist
gopy build -output=geodist -vm=python3 github.com/jonathan-kosgei/geodist</code></pre><p>then you can import the</p>]]></description><link>https://ipdata.co/blog/c-vs-golang-python-extensions/</link><guid isPermaLink="false">62a8664ec5f6a53650ab7d48</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[user]]></dc:creator><pubDate>Tue, 14 Jun 2022 10:57:02 GMT</pubDate><content:encoded><![CDATA[<p>I recently stumbled across the <a href="https://github.com/go-python/gopy">GoPy</a> module that makes it dead easy to convert any Golang package into a Python module.</p><p><br>Here&apos;s how you would use it</p><pre><code class="language-bash">go get github.com/jonathan-kosgei/geodist
gopy build -output=geodist -vm=python3 github.com/jonathan-kosgei/geodist</code></pre><p>then you can import the module in Python</p><pre><code class="language-python">&gt;&gt; from geodist import geodist
&gt;&gt; dir(geodist)
[&apos;Coord&apos;, &apos;VincentyDistance&apos;, &apos;__builtins__&apos;, &apos;__cached__&apos;, &apos;__doc__&apos;, &apos;__file__&apos;, &apos;__loader__&apos;, &apos;__name__&apos;, &apos;__package__&apos;, &apos;__spec__&apos;, &apos;_collections_abc&apos;, &apos;_geodist&apos;, &apos;collections&apos;, &apos;currentdir&apos;, &apos;cwd&apos;, &apos;go&apos;, &apos;inspect&apos;, &apos;os&apos;, &apos;sys&apos;]</code></pre><p>Naturally I was curious whether Python extensions written in Golang have comparable performance to those written in C, which is what we explore in this post.<br>I&apos;ll be testing the Vincenty algorithm for calculating the distance between 2 co-ordinates using:<br></p><p>1. Native Python - using the <a href="https://pypi.org/project/geopy/">geopy</a> module</p><p>2. Golang extension created with GoPy - I forked John Taylor&apos;s <a href="https://github.com/jftuga">geodist</a> package and slightly modified it to make it compatible with GoPy</p><p>3. C extension - using <a href="https://github.com/vivescere/uvincenty">uvincenty</a></p><p><br>Here are the results using iPython</p><pre><code class="language-python">&gt;&gt; from geodist import geodist
&gt;&gt; import uvincenty
&gt;&gt; import geopy.distance

&gt;&gt; boston_go = geodist.Coord(42.3541165, -71.0693514)
&gt;&gt; newyork_go = geodist.Coord(40.7791472, -73.9680804)

&gt;&gt; %timeit geodist.VincentyDistance(boston_go, newyork_go)
6.24 &#xB5;s &#xB1; 29.2 ns per loop (mean &#xB1; std. dev. of 7 runs, 100,000 loops each)

&gt;&gt; boston = (42.3541165, -71.0693514)
&gt;&gt; newyork = (40.7791472, -73.9680804)

&gt;&gt; %timeit uvincenty.vincenty(*boston, *newyork)
732 ns &#xB1; 2.98 ns per loop (mean &#xB1; std. dev. of 7 runs, 1,000,000 loops each)

&gt;&gt; %timeit geopy.distance.geodesic(boston, newyork)
190 &#xB5;s &#xB1; 1.51 &#xB5;s per loop (mean &#xB1; std. dev. of 7 runs, 10,000 loops each)</code></pre><p>The CPython extension (uvincenty) is ~260 times faster than the pure Python implementation from geopy.<br>The Golang Python extension (geodist) is ~30 times faster than the pure Python implementation.<br>The CPython extension is 8 times faster than the Golang Python extension.</p><p><br>I&apos;m not certain why the Golang Python extension is slower than the C version given that GoPy generates a CPython extension, my guess is that the generated code is less efficient than the native C approach. However this still shows that a sizeable speedup can be achieved using GoPy generated Python extensions with the added bonus of lowered development time and fewer memory bugs by coding in Golang.</p>]]></content:encoded></item><item><title><![CDATA[Top 10 IP Geolocation API Services]]></title><description><![CDATA[<p>As a result of the 2020 pandemic, customers are now more likely to conduct business online as opposed to visiting brick-and-mortar stores. This is a huge shift, and now more than ever, big and small online retailers need to reach out and bridge the gap that local businesses have always</p>]]></description><link>https://ipdata.co/blog/best-ip-geolocation-services/</link><guid isPermaLink="false">624afb5a24d10526e99d8d6b</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Wed, 27 Jan 2021 12:01:51 GMT</pubDate><content:encoded><![CDATA[<p>As a result of the 2020 pandemic, customers are now more likely to conduct business online as opposed to visiting brick-and-mortar stores. This is a huge shift, and now more than ever, big and small online retailers need to reach out and bridge the gap that local businesses have always enjoyed: building a relationship with their customers.</p><p>Thankfully, online businesses can establish and hold onto that personal connection by using IP geolocation. Not only can local businesses provide products and services to a wider customer-base, but they can also identify their local customers and develop/maintain a relationship with them.</p><p>Naturally, there are many companies that provide IP geolocation services, and choosing one can be a balancing act between price, reliability, and speed. But fear not; we&#x2019;ve composed the following list of the 10 best IP geolocation services for you to review.</p><ol><li>IPData.co</li><li>Maxmind</li><li>Abstract&#x2019;s IP Geolocation API</li><li>IP Geolocation API</li><li>ipapi.co</li><li>ipstack.com</li><li>ip-api.com</li><li>ipgeolocation.io</li><li>IP2Location</li><li>ipify.org</li></ol><h1 id="ipdata-co"><a href="https://ipdata.co">IPData.co</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-49-00.png" class="kg-image" alt loading="lazy"></figure><p>When it comes to geolocation data, IPData&#x2019;s API provides just about everything you need without breaking the bank. In addition to accurate, basic geolocation data such as continent, city, region, etc, IPData provides threat intelligence data, ISP and company information, and internationalization data like currencies, time zones, and languages. While other services would charge extra for this additional information, IPData exposes all of their data across all pricing tiers. This sounds like a lot of data (it is), but you can easily filter your request results to just the information you need.</p><p>IPData&#x2019;s services are trusted by many large corporations, and they are hosted in 11 AWS data centers around the world. So, it is reliable and fast&#x2014; averaging 60ms per request without caching or CDNs.</p><p>IPData is very developer-friendly. The API is well documented, offering a complete list of URLs and query parameters, as well as useful examples to get you started. You can also consume their API by making your own raw HTTP requests, or you can use one of their official libraries for C#, Python, JavaScript, Go, PHP, and Java.</p><p>Pricing starts at $10/month for 2,500 daily requests, but they also offer a free tier for testing and non-commercial use. Unlike many other services, you can lookup multiple IP addresses in a single request, making efficient use of your daily limit.</p><h1 id="maxmind"><a href="https://www.maxmind.com">Maxmind</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-51-07.png" class="kg-image" alt loading="lazy"></figure><p>Maxmind is regarded as one of the best IP address lookups and geolocation APIs. They offer an array of products, ranging from IP geolocation web services to downloadable databases that you can use in your applications.</p><p>Their GeoIP2 web services are served from four endpoints (hosted behind Cloudflare) in the USA, UK, and Singapore. Maxmind uses latency-based routing that supposedly sends your requests to the region closest to you.</p><p>Maxmind&#x2019;s pricing structure depends upon the product you choose. Their web services use a metered model, where you pay for each request you send to the service, and different types of requests have different costs. Their downloadable databases use a subscription model, or you can purchase a database outright without future updates. They do, however, have a free service and database dubbed GeoLite2, but you do have to create an account to use them.</p><h1 id="ipinfo-io"><a href="https://ipinfo.io/">IPinfo.io</a></h1><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_2E4AD0CC1B3A207472FF1C80EA726B15787EAE86F1ED1AB94220ED4DB62F1C68_1611892180863_image.png" class="kg-image" alt loading="lazy"></figure><p>ipinfo.io isn&#x2019;t just an IP geolocation API company; they provide several APIs that they bundle into different monthly plans. Their geolocation API provides accurate basic geolocation data, and it is available in all plans&#x2014;including including the free plan. The Basic plan includes more detailed ASN information, and higher-tier plans include even more data, such as company info, domains that are hosted at the IP, and much more.<br>ipinfo.io is hosted on Google Cloud, and their infrastructure automatically scales with customer demand. They also provide SSL encryption for all their data transfers, including the free plan.<br>Developers will find ipinfo.io&#x2019;s APIs are simple to use and well documented. You&#x2019;ll find information on authentication, filtering response, response payloads, and everything else you need to interact with the API. Better still, ipinfo.io maintains many official client libraries in a wide array of languages and platforms and provides sample code for each of them.<br>Pricing is separated into five plans:</p><ul><li>Free &#x2014; has a rate limit of 40,000 requests per month and provides basic geolocation data</li><li>Basic &#x2014; $49 when billed monthly and allows 250,000 monthly lookups with expanded ASN data</li><li>Business &#x2014; $499/month for 1,000,000 lookups, and it allows access to more data sets and APIs</li><li>Premium &#x2014; $1499/month for 2,500,000 lookups and even more data sets/APIs.</li><li>Enterprise &#x2014; Flexible amount of lookups and all data sets/APIs. You&#x2019;ll need to contact them for pricing.</li></ul><h1 id="ip-api"><a href="https://ip-api.com">IP-API</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-54-46.png" class="kg-image" alt loading="lazy"></figure><p>Yes, you did read about ipapi a few moments ago. No, it is not the same as this provider. IP-API provides a simple-to-use interface for retrieving basic geolocation information about an IP address. Unlike many on this list, IP-API enforces a rate limit of 45 requests per minute. They do, however, provide a bulk endpoint to process multiple IP addresses in a single request.</p><p>It is hosted with <a href="https://totaluptime.com/">Total Uptime Technologies, LLC</a>, and while it is unknown how many servers the service uses to serve requests, <a href="https://ip-api.status.io/">IP-API&#x2019;s status page</a> provides information such as uptime (near 100%), latency (15ms average), and requests per minute (21,322 average).</p><p>For developers, IP-API provides thorough documentation regarding the endpoints, their expected and optional parameters, and the response. It supports multiple response formats, such as JSON, XML, CSV, and newline.</p><p>IP-API is a simple API with three simple pricing tiers. The free tier is available for non-commercial use, but it lacks SSL support. The Pro tier is &#x20AC;13.3 per month and offers SSL support and other features. The Enterprise tier comes with custom terms and agreements, but you have to contact the sales department for further information.</p><h1 id="abstract-ip-geolocation-api"><a href="https://www.abstractapi.com/ip-geolocation-api">Abstract IP Geolocation API</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-52-08.png" class="kg-image" alt loading="lazy"></figure><p>Abstract is less of an IP geolocation company and more of a general API company, offering APIs for looking up IP address, exchange rates, email validation, and so much more. But don&#x2019;t let that dissuade you; like many other entries in this list, Abstract&#x2019;s geolocation API is used by many large corporations.</p><p>Abstract&#x2019;s IP Geolocation API is hosted in AWS and built for scale and speed. However, Abstract does not let you filter your request results to save bandwidth. Therefore, every response contains the full JSON payload whether you want it or not.</p><p>The IP Geolocation API is one of the simplest on this list. Simply provide your API key and an optional IP address in order to perform a lookup. As a result, the documentation is very sparse. They provide detail regarding the response payload, but they only offer rudimentary code samples. There are no official libraries for any language or platform, but one could argue that such a simple API does not need them.</p><p>Pricing begins at $9/month for 200,000 requests and 50 requests per second. Abstract also offers a free tier of 20,000 requests per month and 1 request per second. Their other APIs are not included and have differing pricing structures.</p><h1 id="ipgeolocationapi-com"><a href="https://ipgeolocationapi.com">IPGeolocationAPI.</a>com</h1><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_2E4AD0CC1B3A207472FF1C80EA726B15787EAE86F1ED1AB94220ED4DB62F1C68_1611636056606_image.png" class="kg-image" alt loading="lazy"></figure><p>Not to be confused with Abstract&#x2019;s product of the same name, IP Geolocation API is a free real-time IP geolocation service. It requires no sign-up or API key. The service is open source, and you could easily host your own service using the freely available code.</p><p>IP Geolocation API is hosted behind Cloudflare to ensure speed and reliability, and the geolocation data returned by the API is provided by a monthly updated MaxMind Geolite2 database.</p><p>The documentation is very limited&#x2014;providing only a sample response payload and a few example URLs. There are no code samples or libraries.</p><h1 id="ipapi-co"><a href="https://ipapi.co">ipapi.co</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-53-29.png" class="kg-image" alt loading="lazy"></figure><p>ipapi is yet another IP geolocation focused API company providing fast and accurate results. Its service is hosted in AWS behind Cloudflare; so, speed and reliability should not be a factor. In testing, the service responded with satisfactory latency (74ms).</p><p>For developers, it provides a thorough documentation, detailing the various end points and response payloads. There are code samples for many languages (PHP, C#, JavaScript, Ruby, Python, and others), and they provide official libraries for Python and Node.js/NPM. Unlike other services, ipapi supports payloads in JSON, JSONP, XML, CSV, and YAML.</p><p>ipapi uses a monthly pricing structure starting at $12 for 60,000 requests per month with no daily limits. They do provide a no sign-up required free tier that is limited to 30,000 requests per month with a 1,000 request daily limit. Their free tier, however, is suitable only for testing and development; it is not recommended for production.</p><h1 id="ipstack"><a href="https://ipstack.com">ipstack</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-54-14.png" class="kg-image" alt loading="lazy"></figure><p>Like many other entries in this list, ipstack&#x2019;s IP geolocation service is used by thousands of developers and trusted by large and small companies around the world. They provide a wide range of geolocation information; however, it is separated into different modules that are only available within certain pricing tiers.</p><p>ipstack provides three endpoints for standard, bulk, and requester lookups, all of which are hosted behind Cloudflare. The documentation for these endpoints are well done, detailing the required pieces of information (such as the API key), as well as various options for specifying the output format (JSON or XML) and response fields. Unfortunately, the sample code is sorely lacking, and there are no official libraries.</p><p>Pricing starts at $9.99/month for the location, currency, time zone, and connection modules. Higher priced tiers are available that include the bulk endpoint and security module. Their paid tiers are protected via SSL; so, you don&#x2019;t have to worry about the request and response data being transmitted in the clear. The free tier, however, does not use SSL. Therefore, your data is at risk at being intercepted and read.</p><h1 id="ipgeolocation-io"><a href="https://ipgeolocation.io">ipgeolocation.io</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-55-38.png" class="kg-image" alt loading="lazy"></figure><p>The ipgeolocation.io API provides basic geolocation information about an IP address, but it also exposes three complementary APIs for retrieving an IP&#x2019;s <a href="https://ipgeolocation.io/timezone-api.html">time zone information</a>, <a href="https://ipgeolocation.io/astronomy-api.html">its astronomical data</a>, and a <a href="https://ipgeolocation.io/documentation/user-agent-api.html">user agent API</a>; the latter, of course, is only useful when called from the browser. Despite having different end points for each of these APIs, they are not charged separately; they are included with every pricing tier.</p><p>ipgeolocation.io is hosted behind Cloudflare; so, you can expect it to have good reliability and response times. According to their API&#x2019;s status page, it has near 100% uptime and a global response time average of 109ms.</p><p>Each endpoint is well documented, providing all of the information you need to make raw requests. They also provide code samples and official libraries for Java, C#, PHP, TypeScript, JavaScript, and jQuery.</p><p>There are several pricing tiers. They provide a free tier for developers with a 1,000 daily request limit and 30,000 requests per month. Their Bronze tier is $15 per month for 150,000 monthly requests. Their highest tier, Platinum, is $500 per month with 20,000,000 requests per month. They have so many pricing tiers that you are bound to find one that fits your needs.</p><h1 id="ip2location"><a href="https://www.ip2location.com/">IP2Location</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-56-22.png" class="kg-image" alt loading="lazy"></figure><p>IP2Location is one of the oldest IP geolocation services on the internet. They provide 24 IP geolocation databases and provide a number of proxy detection databases as well. These are separate products and not bundled with the API. IP2Location claims to use a &#x201C;non-intrusive IP location lookup technology that retrieves geolocation information with no explicit permission required from users&#x201D;, which of course is marketing-speak for typical IP geolocation look-ups.</p><p>IP2Location&#x2019;s geolocation API is hosted behind Cloudflare, and I experienced an average of 72ms response time.</p><p>The API itself is more complex than it needs to be. IP2Location has 24 predefined payloads that you can specify in your request, and you&#x2019;ll have to consult the documentation to determine which payload you need. There are also add-ons that provide additional information, but not all add-ons can be used with all packages. The documentation does spell it all out, however, and there are basic examples for PHP, Java, C#, Python, and Ruby. JSON and XML are supported payload formats.</p><p>Like the API, pricing is more complicated than it needs to be. Instead of monthly or yearly subscriptions, you purchase credits that are good for one year. Each request consumes credits, and the amount of credits is determined by the package and add-on for that request. The cheapest package is $49 for 100,000 credits.</p><h1 id="ipify"><a href="https://geo.ipify.org">ipify</a></h1><figure class="kg-card kg-image-card"><img src="https://ipdata.co/blog/blog/content/images/2021/01/Screenshot-from-2021-01-27-14-56-51.png" class="kg-image" alt loading="lazy"></figure><p>ipify&#x2019;s geolocation API is relatively new, but they claim to have more than 15 million IP blocks and their associated locations in its database. According to their <a href="https://geo.ipify.org/faq">FAQ page</a>, their database has the highest number of unique locations from the USA, France, and Germany. In addition to their geolocation API, ipify also has a <a href="https://geo-database.ipify.org/">downloadable geolocation database</a> and an <a href="https://vpn-proxy-detection.ipify.org/">IP proxy data feed</a> for detecting anonymous users, but they are separate products and not part of the geolocation API.</p><p>Like many on this list, ipify&#x2019;s geolocation API is hosted behind Cloudflare. In my tests, ipify&#x2019;s geolocation API had an response time average of 64ms.</p><p>ipify&#x2019;s geolocation is fairly simple, providing basic geolocation information; so, its documentation is thorough and concise. There are simple code samples for JavaScript, Java, C#, Perl, PHP, PowerShell, Python, and Ruby.</p><p>Pricing is broken down by the number of requests per month and the length of subscription. For example, 10,000 requests per month is $10 with a monthly subscription and $79 for a yearly subscription. More expensive tiers do not provide extra features&#x2014;only more requests.</p><h1 id="conclusion">Conclusion</h1><p>IP geolocation APIs are a must-have for any business that wants to know the specific locations of their customers. This information can be vital, especially for small and local businesses that want to maintain the home-town feel. They can also be useful for displaying relevant advertisements, as well as filtering risky or questionable users.</p><p>No matter what your goal is, the APIs in this list will serve your geolocation needs.</p>]]></content:encoded></item><item><title><![CDATA[ASN Data Lookup APIs and Tools]]></title><description><![CDATA[<h1 id="what-is-asn-data-and-why-is-it-useful">What is ASN data and why is it useful?</h1><p><a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">Autonomous Systems</a> (AS) are used in <a href="https://en.wikipedia.org/wiki/Border_Gateway_Protocol">BGP</a> to route traffic to the correct network. ASNs are the notation for identifying a given AS and ASN data is stored by Regional Internet Registries (RIRs), which are used to look up where to</p>]]></description><link>https://ipdata.co/blog/asn-data-lookup-apis-and-tools/</link><guid isPermaLink="false">624afb5a24d10526e99d8d6a</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Tue, 08 Dec 2020 17:58:49 GMT</pubDate><content:encoded><![CDATA[<h1 id="what-is-asn-data-and-why-is-it-useful">What is ASN data and why is it useful?</h1><p><a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">Autonomous Systems</a> (AS) are used in <a href="https://en.wikipedia.org/wiki/Border_Gateway_Protocol">BGP</a> to route traffic to the correct network. ASNs are the notation for identifying a given AS and ASN data is stored by Regional Internet Registries (RIRs), which are used to look up where to route traffic. Outside of BGP routing, they may sound useless, but they&#x2019;re actually great sources of data and they can be used to identify the organisation which controls a set of IP addresses. There are currently over 92,000 registered ASNs.</p><h1 id="which-organisations-use-asns">Which organisations use ASNs?</h1><h2 id="internet-service-providers">Internet Service Providers</h2><p>The most well-known users of ASNs are Internet Service Providers (ISPs), such as Comcast, Virgin Media, Vodafone or Telef&#xF3;nica. They are allocated blocks of IP addresses by a RIR, which they then allocate to individual users. Your home internet&#x2019;s IP address will be a part of a larger block of IPs, for example many Virgin Media users will have an IP within the ASN <code>AS5089</code>, which has a route, or CIDR block, of <code>81.106.0.0/15</code> , meaning the IPs range from <code>81.106.0.0</code> to <code>81.107.255.255</code>.</p><h2 id="hosting-providers">Hosting Providers</h2><p>Web Hosting Providers, such as AWS, Azure, and Oracle Cloud, need a lot of IP addresses so they can allocate them to their customer&#x2019;s servers. When you launch a server with a public IP, just like your home internet, it&#x2019;ll be given an IP address from a larger pool. AWS is such a large provider that, based on their <a href="https://ip-ranges.amazonaws.com/ip-ranges.json">public list of IP ranges</a>, they&#x2019;re allocated 109,845,950 IPv4 addresses, which is approximately 3% of the global total.</p><h2 id="anyone-else-">Anyone else!</h2><p>The list of organisations who use ASNs could go on for ages. Anyone who needs a large block of IP addresses could use them. Other notable organisations include Netflix, Apple, Microsoft, but they&#x2019;re also used by some smaller enterprises to keep all their office networks within the same IP range, which can help with security.</p><h1 id="why-are-asns-useful">Why are ASNs useful?</h1><p>ASNs are essential to the operation of the Internet, but most people don&#x2019;t need to understand the inner-workings of them. However, ASNs can still be incredibly useful as they associate a block of IP addresses with an organisation, which means we can look up organisation information by IP address.</p><h2 id="who-is-accessing-my-website">Who is accessing my website?</h2><p>One of the most common uses of ASN data is to find out where your visitors are coming from. Along with geolocation data, many popular analytics tools will show which ISPs/organisations are accessing your website. All this information is powered by ASN data.</p><h2 id="could-this-ip-address-be-malicious">Could this IP address be malicious?</h2><p>We can use ASN data to help us identify IP addresses as malicious. For example, if there&#x2019;s an abnormally high number of login attempts, we can look up the IP addresses which are making those requests. If the IPs are from ASNs associated with hosting providers, they&#x2019;re likely trying to gain access to your users&#x2019; accounts! Additional information, such as ipdata&#x2019;s <a href="https://docs.ipdata.co/overview/threat-data">threat API</a> and location data, can help assist this.</p><h2 id="who-is-serving-this-website">Who is serving this website?</h2><p>Using a DNS lookup, we can find the IP addresses which are serving a given website. This can help us to find the hosting provider, and might indicate if the site is malicious. The IP addresses can be found using an online tool, like <a href="https://dnslookup.org/ipdata.co/A/#delegation">dnslookup.org</a> or the <code>dig</code> Unix command, for example <code>dig +short ipdata.co</code>.</p><h1 id="how-can-i-look-up-asns">How can I look up ASNs?</h1><p>There are many different ways to look up ASN data by IP address. We&#x2019;ll explore 5 different options, each with different pros/cons. To give a consistent example, we&#x2019;ll be testing an IP address associated with serving apple.com - <code>17.253.144.10</code>.</p><h2 id="whois">Whois</h2><p>Whois is a utility tool which can look up the data associated with a domain or IP address. It can be used on the command line in Unix or Windows-based systems.</p><p><code>whois 17.253.144.10</code></p><pre><code>NetRange:       17.0.0.0 - 17.255.255.255
CIDR:           17.0.0.0/8
NetName:        APPLE-WWNET
NetHandle:      NET-17-0-0-0-1
Parent:          ()
NetType:        Direct Assignment
OriginAS:
Organization:   Apple Inc. (APPLEC-1-Z)
RegDate:        1990-04-16
Updated:        2017-07-08
Ref:            https://rdap.arin.net/registry/ip/17.0.0.0

OrgName:        Apple Inc.
OrgId:          APPLEC-1-Z
// ... More information - try it yourself!
</code></pre><p>By default, whois will query <a href="https://www.iana.org">IANA</a> to find which RIR to query for detailed information. In the case of Apple, it uses <code>[arin.net](https://www.arin.net)</code>. Based on the response, we can immediately see that Apple owns the IP, and has done since 1990.</p><p>The ASN associated with the IP should be after <code>OriginAS</code>, but strangely for this IP, there&#x2019;s no result! This could be an issue caused by the fact that Apple is assigned an entire <code>/8</code> address block, the largest possible size, which means that any IP which begins with <code>17.</code> will be from Apple! Regardless of the reason, we&#x2019;ll need to try another tool to find the ASN.</p><h2 id="cymru">Cymru</h2><p>Cymru provides an easy-to-use <code>whois</code> compatible API. This means we can switch the whois host to <code>whois.cymru.com</code> and use it to look up IP addresses.</p><p><code>whois -h whois.cymru.com 17.253.144.10</code></p><pre><code>AS      | IP               | AS Name
714     | 17.253.144.10    | APPLE-ENGINEERING, US
</code></pre><p>The output is short and easy to read - this IP address&#x2019; ASN is AS714, which is owned by Apple. However, it&#x2019;s very limited and missing a lot of useful information, so let&#x2019;s look at PeeringDB and CAIDA, which can enrich the data.</p><h2 id="peeringdb">PeeringDB</h2><p><a href="https://peeringdb.com">PeeringDB</a> is the go-to solution for ASN-owners to decide which ASNs to peer with. When an ASN is peered with another, it allows both networks to exchange traffic between each other. In Apple&#x2019;s case, they <a href="https://cache.edge.apple">encourage ISPs to peer</a> with them to deliver Apple content directly to customers. For our use-case, this means they have a very detailed database of the organisations which operate each ASN, along with their relationships with each other.</p><p>PeeringDB has no functionality to look up a ASN data by IP address, but as we already know the ASN for <code>17.253.144.10</code> is <code>714</code>, we can look up all the peering information about this IP - <a href="https://www.peeringdb.com/asn/714">https://www.peeringdb.com/asn/714</a>. PeeringDB also provides an API, so we can access this data programmatically, but frustratingly there is no dedicated ASN API. First, we&#x2019;ll need the Network ID, which can only be found by searching for the ASN&#x2026;</p><p><code>curl --write-out &quot;%{redirect_url}\n&quot; &quot;https://www.peeringdb.com/search?q=AS714&quot;</code></p><pre><code>https://www.peeringdb.com/net/3554
</code></pre><p>We can then add <code>/api</code> to the start of the URL path to get the JSON API endpoint:</p><p><code>curl https://www.peeringdb.com/api/net/3554</code></p><pre><code>{&quot;data&quot;: [{&quot;id&quot;: 3554, &quot;org_id&quot;: 8418, &quot;org&quot;: {&quot;id&quot;: 8418, &quot;name&quot;: &quot;Apple Inc&quot;,
// ...
</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_4FCED7143F6EFF418244293A328923D2402C621A12E8CF39D6AC6A931EBCF9EB_1607384510066_Screenshot+2020-12-07+at+23.41.19.png" class="kg-image" alt="Screenshot of PeeringDB&#x2019;s interface for AS714" loading="lazy"></figure><h2 id="caida">CAIDA</h2><p><a href="https://www.caida.org">CAIDA</a> provides a few tools and services associated with IP addresses, BGP and ASN data. <a href="https://asrank.caida.org">AS Rank</a> is the most relevant to us &#x2013; it&#x2019;s tool which allows searching by ASN and ranks them by the number of IPs that can be <a href="https://asrank.caida.org/about#cone">reached by following customer links</a>.</p><p>Like PeeringDB, we can&#x2019;t search by IP address, but we can search by ASN. The <a href="https://asrank.caida.org/asns?asn=714&amp;type=search">results for</a> <code>[AS714](https://asrank.caida.org/asns?asn=714&amp;type=search)</code> &#xA0;show it, at the time of writing, to be ranked 6,630th. The top ranked ASN is <a href="https://asrank.caida.org/asns/3356">3356</a>, owned by Level 3, which is connected to about 75% of all IPs.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_4FCED7143F6EFF418244293A328923D2402C621A12E8CF39D6AC6A931EBCF9EB_1607384610347_Screenshot+2020-12-07+at+23.43.11.png" class="kg-image" alt="Screenshot of AS Rank results for AS714" loading="lazy"></figure><p>AS Rank&#x2019;s data is available via <a href="https://api.asrank.caida.org/v2/docs">an API which supports GraphQL and REST</a>, allowing us to flexibly query their systems. The RESTful API returns all their data associated with an ASN with a GET request.<br><code>curl https://api.asrank.caida.org/v2/restful/asns/714</code></p><pre><code>{
  &quot;data&quot;: {
    &quot;asn&quot;: {
      &quot;rank&quot;: 6630,
      &quot;asn&quot;: &quot;714&quot;,
      &quot;asnName&quot;: &quot;APPLE-ENGINEERING&quot;,
      &quot;source&quot;: &quot;ARIN&quot;,
      &quot;cliqueMember&quot;: true,
      &quot;seen&quot;: true,
      &quot;ixp&quot;: null,
      &quot;longitude&quot;: -118.278526797574,
      &quot;latitude&quot;: 40.9090750557379,
      &quot;organization&quot;: {
        &quot;orgId&quot;: &quot;e7117ce3ab&quot;
      },
      &quot;cone&quot;: {
        &quot;numberAsns&quot;: 2,
        &quot;numberPrefixes&quot;: 1022,
        &quot;numberAddresses&quot;: 16816640
      },
      &quot;country&quot;: {
        &quot;iso&quot;: &quot;US&quot;
      },
      &quot;asnDegree&quot;: {
        &quot;total&quot;: 316,
        &quot;customer&quot;: 1,
        &quot;peer&quot;: 292,
        &quot;provider&quot;: 23
      }
    }
  }
}
</code></pre><h2 id="ipdata">ipdata</h2><p>The <a href="https://docs.ipdata.co/overview/asn-data">ipdata ASN API</a> is easy-to-use and works with a simple HTTP request. It can look up a huge amount of information associated with an IP address, and it even enriches the ASN data with a usage type, helping to determine whether the ASN is allocated to an organisation, ISP, hosting provider, <a href="https://docs.ipdata.co/overview/asn-data#usage-type">or something else</a>!</p><p>For our Apple IP address, a request would look like this:<br><code>curl &quot;https://api.ipdata.co/17.253.144.10/asn?api-key=test&quot;</code></p><pre><code>{
  asn: &quot;AS714&quot;,
  name: &quot;Apple Inc.&quot;,
  domain: &quot;apple.com&quot;,
  route: &quot;17.253.144.0/21&quot;,
  type: &quot;hosting&quot;
}
</code></pre><p>At the same time as providing ASN data, ipdata&#x2019;s API can look up location information, detect VPNs, and more, providing you with an affordable all-in-one solution.</p><p>We&#x2019;ve also written a handy guide &#x2013; <a href="https://ipdata.co/blog/blog/ip-to-company/">How to look up Company Details by IP address</a>, which follows a practical example of using ASN data to enrich a &#x201C;Contact Us&#x201D; form with company information.</p><h1 id="conclusions">Conclusions</h1><p>ASN data is critical for the Internet to function, but most people have no idea about BGP routing, AS&#x2019;, or RIRs. Despite their obscurity, ASN data is valuable &#x2013; it can identify the organisation associated with an IP address, showing us where website traffic is coming from and helping to identify malicious traffic. There are many tools and providers to look up ASN data, each with their own pros and cons, so we&#x2019;d recommend trying out the options and choosing for yourself &#x2013; we&#x2019;re confident you&#x2019;ll love <a href="https://ipdata.co">ipdata</a>!</p>]]></content:encoded></item><item><title><![CDATA[How to detect and block bot traffic on your website]]></title><description><![CDATA[<h2 id="detecting-and-optimising-your-site-for-bot-traffic">Detecting and optimising your site for bot traffic</h2><p>Bots come in all shapes and sizes. Many are legitimate, such as Search Engine bots (or spiders), which crawl your website to include pages on search engines like Google. There are plenty of illegitimate bots too &#x2013; one common issue is referral</p>]]></description><link>https://ipdata.co/blog/bot-detection/</link><guid isPermaLink="false">624afb5a24d10526e99d8d69</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Mon, 26 Oct 2020 13:46:08 GMT</pubDate><content:encoded><![CDATA[<h2 id="detecting-and-optimising-your-site-for-bot-traffic">Detecting and optimising your site for bot traffic</h2><p>Bots come in all shapes and sizes. Many are legitimate, such as Search Engine bots (or spiders), which crawl your website to include pages on search engines like Google. There are plenty of illegitimate bots too &#x2013; one common issue is referral spam, advertising sites by making them show up in your web analytics, consuming your resources and usage limits.</p><h1 id="detecting-bots">Detecting bots</h1><h2 id="legitimate-bots">Legitimate bots</h2><p>Legitimate bots will send a <code>User-Agent</code> which indicates that they&#x2019;re a bot. Most of these include <code>bot</code> in them, such as <code>Googlebot</code>, but there&#x2019;s a long list of rules and exceptions. One frustrating example is that <code>Cubot</code> is often detected as a bot, but it&#x2019;s not a bot at all &#x2013; it&#x2019;s a mobile phone manufacturer.</p><p>For those reasons, it&#x2019;s best to use a library to detect whether it&#x2019;s a bot. Most major programming languages will have a library available &#x2013; for Node.js, there&#x2019;s a library named <code>[isbot](https://github.com/omrilotan/isbot)</code> and for PHP there&#x2019;s one named <code>[device-detector](https://github.com/matomo-org/device-detector)</code>.</p><p>Usage of <code>isbot</code> is simple:</p><pre><code>const isbot = require(&apos;isbot&apos;)

isbot(&apos;Googlebot/2.1 (+http://www.google.com/bot.html)&apos;) // true
isbot(&apos;Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36&apos;) // false
</code></pre><h2 id="illegitimate-bots">Illegitimate bots</h2><p>Referral spam bots, and other illegitimate bots, will try to disguise themselves as normal visitors to your website. For that reason, they won&#x2019;t set a recognisable User-Agent, and it&#x2019;s impossible to reliably detect all of them.</p><p>Luckily, there&#x2019;s an <a href="https://github.com/matomo-org/referrer-spam-list">open-source list of known referral spammers</a>. We can compare the <code>Referer</code> header against this list and decide how to handle it&#x2026;</p><pre><code>const fs = require(&apos;fs&apos;)
// referral-spammers.txt downloaded from https://github.com/matomo-org/referrer-spam-list/blob/master/spammers.txt
const spammerList = fs.readFileSync(&apos;./referral-spammers.txt&apos;)
  .toString()
  .split(&apos;\n&apos;) // each spammer is on a new line
  .filter(Boolean) // filter out empty lines

const isSpammer = referer =&gt; {
  return referer &amp;&amp; spammerList.some(spammer =&gt; referer.includes(spammer))
}

isSpammer(&apos;https://casino-top3.ru&apos;) // true
isSpammer(&apos;https://ipdata.co&apos;) // false
</code></pre><p>Bots which aren&#x2019;t referral spammers will be very difficult to detect. It&#x2019;s best to rely on threat data to ensure you&#x2019;re able to block requests from IPs which have been flagged as malicious. <a href="https://docs.ipdata.co/overview/threat-data">ipdata&#x2019;s threat API</a> can help to do exactly this. Additionally, <a href="https://docs.ipdata.co/overview/asn-data">ipdata&#x2019;s ASN API</a> can detect IP addresses which are associated with hosting providers, such as AWS. Hosting IPs are often used for bots and hacking attempts, but they could also be legitimate proxies. Here&#x2019;s a small script, which we&#x2019;ll use later, to detect hosting providers and threats.</p><pre><code>const axios = require(&quot;axios&quot;);
// Get an ipdata API Key from here: https://ipdata.co/sign-up.html
const IPDATA_API_KEY = &quot;test&quot;;
const getIpData = async (ip) =&gt; {
  const response = await axios.get(
    `https://api.ipdata.co/${ip}?api-key=${IPDATA_API_KEY}`
  );
  return response.data;
};

// Let&apos;s test it out!
const microsoftIpData = await getIpData(&quot;13.107.6.152&quot;);
microsoftIpData.threat.is_threat; // false - this IP is not associated with threats
microsoftIpData.asn.type; // &quot;hosting&quot; - this IP is a Microsoft hosting IP
</code></pre><h1 id="responding-to-bot-traffic">Responding to bot traffic</h1><p>Now we&#x2019;ve detected a majority of our bot traffic, we need to decide what to do with it!</p><h2 id="excluding-bots-from-analytics">Excluding bots from analytics</h2><p>Most people want to exclude all bot traffic from their web analytics tools. Many tools have a built-in methods for excluding bots, but if you wish to control this yourself, you can exclude the tracking codes. This can help prevent bots from consuming your usage limits, and ensures consistency across different tools.</p><p>In Node.js with Express and EJS, we can do this with just a few lines of code.</p><p><code>**index.js**</code></p><pre><code>const isbot = require(&apos;isbot&apos;)
const isSpammer = require(&apos;./is-spammer&apos;) // isSpammer function defined above
const getIpData = require(&apos;./get-ip-data&apos;) // getIpData function defined above
const express = require(&quot;express&quot;)
const app = express()

app.set(&apos;view engine&apos;, &apos;ejs&apos;)

app.get(&quot;/&quot;, async (req, res) =&gt; {
  const ipdata = await getIpData(req.connection.remoteAddress)
  if (ipdata.threat.is_threat) {
    // Block threats altogether!
    res.status(403).send(&apos;You are not allowed to access this site&apos;)
    return
  }
  const includeAnalytics = 
    !isbot(req.get(&apos;user-agent&apos;)) &amp;&amp; 
    !isSpammer(req.get(&apos;referer&apos;)) &amp;&amp;
    ipdata.asn.type !== &apos;hosting&apos;

  res.render(&quot;./index.ejs&quot;, { includeAnalytics })
})

app.listen(8000)
</code></pre><p><code>**views/index.ejs**</code></p><pre><code>&lt;html&gt;
  &lt;head&gt;
    &lt;% if (includeAnalytics) { %&gt;
      &lt;script&gt;
        // Google Analytics here
        // Other analytics scripts too..
      &lt;/script&gt;
    &lt;% } %&gt;
  &lt;/head&gt;
  &lt;body&gt;
    Hello!
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Now, we can test our implementation using <code>curl</code>.</p><p><strong>Browser with no referrer</strong></p><pre><code>curl -H &quot;user-agent: A-Browser&quot; http://localhost:8000
&lt;html&gt;
  &lt;head&gt;
    &lt;script&gt;
      // Google Analytics here
      // Other analytics scripts too..
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    Hello!
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p><strong>Bot user-agent</strong></p><pre><code>curl -H &quot;user-agent: bot&quot; http://localhost:8000
&lt;html&gt;
  &lt;head&gt;
  &lt;/head&gt;
  &lt;body&gt;
    Hello!
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p><strong>Referral spammer</strong></p><pre><code>curl -H &quot;user-agent: A-Browser&quot; -H &quot;referer: https://casino-top3.ru&quot; http://localhost:8000
&lt;html&gt;
  &lt;head&gt;
  &lt;/head&gt;
  &lt;body&gt;
    Hello!
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>As you can see, when we request the website from a normal browser, they&#x2019;re served the analytics scripts, but bots and referral spammers are excluded.</p><p>This can easily be adapted to totally block traffic from bots or referral spammers from accessing your website, you can treat them like threats (see line 11 of <code>index.js</code>), but this should usually be unnecessary for most sites.</p><h2 id="serving-a-search-engine-optimised-version-of-your-site">Serving a Search Engine Optimised version of your site</h2><p>Serving a different version of your website to crawlers can cause a major improvement in SEO. Modern websites, especially single-page-applications can be hard for crawlers to read and index, so we can serve them a simplified version.</p><p>The approach is very similar to excluding bots from analytics.</p><p><code>**index.js**</code></p><pre><code>const isbot = require(&apos;isbot&apos;)
const express = require(&quot;express&quot;);
const app = express();

app.set(&apos;view engine&apos;, &apos;ejs&apos;)

app.get(&quot;/&quot;, (req, res) =&gt; {
  if (isbot(req.get(&apos;user-agent&apos;))) {
    res.render(&apos;./seo.ejs&apos;)
    return
  }
  res.render(&quot;./index.ejs&quot;)
});

app.listen(8000);
</code></pre><p><code>**views/seo.ejs**</code></p><pre><code>&lt;html&gt;
  &lt;body&gt;
    Hello search engine crawlers + bots!
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p><code>**views/index.ejs**</code></p><pre><code>&lt;html&gt;
  &lt;body&gt;
    Hello normal visitors
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Again, we can test our implementation with <code>curl</code>.</p><p><strong>Browser</strong></p><pre><code>curl -H &quot;user-agent: A-Browser&quot; http://localhost:8000
&lt;html&gt;
  &lt;body&gt;
    Hello normal visitors
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p><strong>Bot</strong></p><pre><code>curl -H &quot;user-agent: bot&quot; http://localhost:8000
&lt;html&gt;
  &lt;body&gt;
    Hello search engine crawlers + bots!
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>To ensure high performance, which can also improve your search rankings, you may want to use an Edge Computing solution, such as Lambda@Edge or Cloudflare Workers. For detailed guides on edge computing, check out our posts: <a href="https://ipdata.co/blog/blog/using-cloudflare-workers-to-redirect-users-based-on-country/"><strong>Using Cloudflare Workers to redirect users based on country</strong></a> ****and <a href="https://ipdata.co/blog/blog/redirect-users-based-on-country-with-lambda-edge/"><strong>Redirect users based on country with Lambda@Edge</strong></a>.</p><h1 id="conclusions">Conclusions</h1><p>Detecting and adapting your website for bot traffic can be incredibly valuable. Simply excluding them from your analytics can reduce your usage costs and keep your data clean &#x2013; bots can cause your conversion rates to drop, distracting you from focussing on real customers. Serving a specific site dedicated to SEO won&#x2019;t be required often, as search crawlers are able to index most websites, but for highly-complex single page applications, it can have a huge impact on your search rankings.</p><p>Let us know your bot-detection use-cases in the comments below!</p>]]></content:encoded></item><item><title><![CDATA[Showing Promotions Based on Users’ Location]]></title><description><![CDATA[<p>The web revolutionized the way business is conducted around the world. But as with all revolutions, there are casualties. Small and family-owned businesses had to face the hard reality that most consumers prefer convenience over community, and many of those businesses closed their doors. Many others, however, embraced the opportunity</p>]]></description><link>https://ipdata.co/blog/showing-promotions-based-on-users-location/</link><guid isPermaLink="false">624afb5a24d10526e99d8d68</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Sat, 26 Sep 2020 20:37:57 GMT</pubDate><content:encoded><![CDATA[<p>The web revolutionized the way business is conducted around the world. But as with all revolutions, there are casualties. Small and family-owned businesses had to face the hard reality that most consumers prefer convenience over community, and many of those businesses closed their doors. Many others, however, embraced the opportunity and potential the Internet promises and expanded their consumer base across city, state, and even country lines.</p><p>Today, it&#x2019;s easier than ever for local businesses to not only reach new customers through the Internet, but to also cater to their local customer-base by offering promotions. With tools like ipdata&#x2019;s location-oriented API, local shops can identify their hometown customers and offer customized incentives.</p><p>In this article, you&#x2019;ll learn how to use ipdata&#x2019;s API to show promotions for a visitor based upon their location. You&#x2019;ll need an API key, and if you don&#x2019;t already have one, <a href="https://dashboard.ipdata.co/sign-up.html">sign up for a free ipdata account</a>.</p><p>You&#x2019;ll build a service class for a Laravel application. So, if you want to follow along and actually run this code, you&#x2019;ll need to setup your environment with the following:</p><ul><li>PHP 7.3+</li><li>MySQL (or comparable database, such as MariaDB)</li><li>Laravel 6+ (it&#x2019;s easy to install; <a href="https://laravel.com/docs/8.x/installation">just follow the directions</a>)</li></ul><p>Note that even though this article uses PHP and the Laravel framework, the same ideas and concepts can be applied to any language and framework.</p><h1 id="setting-up-the-project">Setting up the Project</h1><p>The first thing you need to do is create a new Laravel project. The easiest way to do so is by running the following command in your terminal:</p><pre><code>laravel new &lt;project_name&gt;
</code></pre><p>Replace <code>&lt;project_name&gt;</code> with your name of choice. Laravel, like many other popular frameworks, provides many command-line tools for managing different aspects of the project. So, leave your terminal window open.</p><p>Next, change the directory in your terminal to the newly created project folder, like this:</p><pre><code>cd &lt;project_name&gt;
</code></pre><p>The next step is to install the official ipdata PHP library and supporting packages. Note that this is not a requirement&#x2014;you can consume the ipdata API with your own code. However, the library saves a lot of time and makes it very easy to interact with the API. In your terminal window, execute the following command:</p><pre><code>composer require ipdata/api-client
</code></pre><p>This command uses the composer package manager to download and install ipdata&#x2019;s PHP client library to your project. The API client is built upon PSR standards, and you need to install some supporting packages with the following command:</p><pre><code>composer require nyholm/psr7 symfony/http-client
</code></pre><p>Next, you need to configure the project to find your database. Open your project&#x2019;s <code>.env</code> file and modify the following options:</p><pre><code>DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_awesome_store
DB_USERNAME=root
DB_PASSWORD=
</code></pre><p>Naturally, these settings depend upon your database&#x2019;s settings. Be sure to input the correct information; otherwise, the database migrations (as well as the application itself) will fail.</p><h1 id="creating-the-promotions-table">Creating the Promotions Table</h1><p>Your project will track the various promotions it provides visitors with a single database table called <code>promotions</code>. While you can manually create your <code>promotions</code> table with any number of database tools, you&#x2019;ll use Laravel&#x2019;s command-line utility called Artisan. Execute the following command in your terminal window:</p><pre><code>php artisan make:model Promotion --migration
</code></pre><p>This command does two things. First, it creates a model class that you will use later to interact with the database table. Second, it creates a database migration file that you use to, among other things, define the <code>promotions</code> table schema.</p><p>Navigate to your project&#x2019;s <code>database\migrations</code>\ folder and open the file that ends with <code>create_promotions_table.php</code>. In it, you will find a class called <code>CreatePromotionsTable</code>, and it contains an <code>up()</code> method that looks like the following:</p><pre><code>public function up()
{
  Schema::create(&apos;promotions&apos;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;timestamps();
  });
}
</code></pre><p>This code uses the <code>Schema::create()</code> method to create the <code>promotions</code> table, and you&#x2019;ll use the <code>$table</code> object to define the table&#x2019;s columns. For this article, your author decided to implement a solution that allows for multiple promotions based upon the date, the visitor&#x2019;s region, and/or the visitor&#x2019;s city. Therefore, you can define promotions that are limited to visitors within a specific city, a specific region (eg: state, province, etc), or are not limited to anyone at all.</p><p>To account for these different variables, the <code>promotions</code> table needs the following columns:</p><ul><li>start date</li><li>end date</li><li>region</li><li>city</li><li>discount amount</li></ul><p>The following code defines those columns using Laravel&#x2019;s table builder syntax:</p><pre><code>public function up()
{
  Schema::create(&apos;promotions&apos;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;date(&apos;start_date&apos;);
    $table-&gt;date(&apos;end_date&apos;);
    $table-&gt;string(&apos;region_code&apos;);
    $table-&gt;string(&apos;city&apos;, 100)-&gt;nullable();
    $table-&gt;decimal(&apos;discount&apos;, 2, 2);
    $table-&gt;timestamps();
  });
}
</code></pre><p>Of course, you need some data to work with. You can manually insert records using your database management tool of choice, or you can use Laravel&#x2019;s seeding capabilities to quickly populate the <code>promotions</code> table.</p><p>In a large application, you would want to create a custom seeder class specifically for your <code>promotions</code> table. In this case, however, you can simply add code to the default database seeder. Open the <code>database\seeders\DatabaseSeeder.php</code> file and add the following <code>use</code> statements:</p><pre><code>use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
</code></pre><p>The first <code>use</code> statement imports the <code>DB</code> facade, which provides the methods you&#x2019;ll need to insert records into the <code>promotions</code> table. The second statement imports <code>Carbon</code>, a simple API extension for working with date and time.</p><p>Next, modify the <code>run()</code> method to insert records into the <code>promotions</code> table, like this:</p><pre><code>public function run()
{
    // User::factory(10)-&gt;create();
  DB::table(&apos;promotions&apos;)-&gt;insert([
    &apos;start_date&apos; =&gt; Carbon::create(&apos;2020&apos;, &apos;07&apos;, &apos;01&apos;),
    &apos;end_date&apos; =&gt; Carbon::create(&apos;2020&apos;, &apos;10&apos;, &apos;31&apos;),
    &apos;region_code&apos; =&gt; &apos;*&apos;,
    &apos;city&apos; =&gt; null,
    &apos;discount&apos; =&gt; .10
  ]);

  DB::table(&apos;promotions&apos;)-&gt;insert([
    &apos;start_date&apos; =&gt; Carbon::create(&apos;2020&apos;, &apos;08&apos;, &apos;01&apos;),
    &apos;end_date&apos; =&gt; Carbon::create(&apos;2020&apos;, &apos;9&apos;, &apos;30&apos;),
    &apos;region_code&apos; =&gt; &apos;TX&apos;,
    &apos;city&apos; =&gt; null,
    &apos;discount&apos; =&gt; .25
  ]);

  DB::table(&apos;promotions&apos;)-&gt;insert([
    &apos;start_date&apos; =&gt; Carbon::create(&apos;2020&apos;, &apos;08&apos;, &apos;01&apos;),
    &apos;end_date&apos; =&gt; Carbon::create(&apos;2020&apos;, &apos;9&apos;, &apos;30&apos;),
    &apos;region_code&apos; =&gt; &apos;TX&apos;,
    &apos;city&apos; =&gt; &apos;Houston&apos;,
    &apos;discount&apos; =&gt; .30
  ]);
}
</code></pre><p>The highlighted code in this example inserts three records into the <code>promotions</code> table. Naturally, make sure your start and end dates are suitable for purposes, and it is also important that any city you specify actually resides within the specified region.</p><p>Notice line 7 in the above code. The <code>region_code</code> column contains a value of &#x2018;*&#x2019;. This, in conjunction with a <code>null</code> city, is a &#x201C;catch all&#x201D; promotion. If a visitor is not in a specified city or region, then they can take advantage of the &#x201C;catch all&#x201D; promotion as long as they visit within the start and end date.</p><p>Your next step is to migrate and seed the database, and you can do so by executing the following commands in the terminal:</p><pre><code>php artisan migrate
php artisan db:seed
</code></pre><p>As you&#x2019;ve already guessed, the first command executes the migration and actually creates the <code>promotions</code> table. The second command executes the <code>run()</code> method in the <code>DatabaseSeeder</code> class, therefore inserting the specified records into the table.</p><p>Now that you have a some test data in an actual database table, you can focus on writing the service class that will retrieve the most appropriate promotion for a given visitor.</p><h1 id="writing-the-service-class">Writing the Service Class</h1><p>As its name implies, the <code>app</code> folder in a Laravel project typically contains the application code. With that in mind, create a subdirectory in <code>app</code> called <code>Services</code>. Then create a new file called <code>PromotionService.php</code> and start the file with the following lines of code:</p><pre><code>namespace App\Services;

use App\Models\Promotion;
use Ipdata\ApiClient\Ipdata;
use Symfony\Component\HttpClient\Psr18Client;
use Nyholm\Psr7\Factory\Psr17Factory;

class PromotionService {
    // more code to come
}
</code></pre><p>The first line in this code defines the <code>App\Services</code> namespace. While not entirely necessary, using namespaces is a fantastic way of organizing your application&#x2019;s code.</p><p>The next four lines of code are <code>use</code> &#xA0;statements that import the following classes:</p><ul><li><code>Promotion</code>&#x2014;the model class you will use to query the database.</li><li><code>Ipdata</code>&#x2014;the client class for interacting with the ipdata API.</li><li><code>Psr18Client</code> and <code>Psr17Factory</code>&#x2014;dependencies needed to create an <code>Ipdata</code> client object.</li></ul><p>Next, define a private method called <code>createClient()</code>, as shown in the following code:</p><pre><code>class PromotionService {
  private function createClient() {
    $httpClient = new Psr18Client();
    $psr17Factory = new Psr17Factory();
    return new Ipdata(&apos;&lt;your_ip_key&gt;&apos;, $httpClient, $psr17Factory);
  }
  // more code here
}
</code></pre><p>Creating an <code>Ipdata</code> client is very straight forward. Simply &#x201C;new up&#x201D; the class&#x2019; constructor and pass three arguments in the following order: your API key, a <code>Psr18Client</code> object, and an instance of <code>Psr17Factory</code>.</p><p>The next method is called <code>getPromotion()</code>, &#xA0;and its purpose is to query the <code>promotions</code> table , process the query results, and return the appropriate promotion for the provided region code and city. Start with the following code:</p><pre><code>class PromotionService {
  private function createClient() {
    $httpClient = new Psr18Client();
    $psr17Factory = new Psr17Factory();
    return new Ipdata(&apos;&lt;your_ip_key&gt;&apos;, $httpClient, $psr17Factory);
  }
  
  private function getPromotion($regionCode, $city) {
    $now = date(&apos;Y-m-d&apos;);
    // more code here
  }
}
</code></pre><p>This code defines the method and creates a variable to contain the current date. Remember, in addition to a visitor&#x2019;s location, promotions also depend upon the date; so, you&#x2019;ll use this <code>$now</code> variable to find a current promotion.</p><p>In plain SQL, the basic query looks like the following:</p><pre><code>SELECT * 
FROM   promotions 
WHERE  start_date &lt; $now
       AND end_date &gt; $now
</code></pre><p>This query simply selects the promotions that have a start date before, and an end date after, the current date (represented by <code>$now</code>). You also want to find promotions that match your visitor&#x2019;s region, but remember that a promotion may be globally available&#x2014;a &#x201C;catch all&#x201D; promotion. Therefore, you need to find any promotion where the <code>region_code</code> column is either an asterisk (<code>*</code>) or the supplied region code (represented by <code>$regionCode</code> below). That can be done with the following:</p><pre><code>SELECT * 
FROM   promotions 
WHERE  start_date &lt; $now
       AND end_date &gt; $now
       AND region_code IN ( &apos;*&apos;, $regionCode )
</code></pre><p>The city slightly complicates things. The <code>city</code> column can be <code>NULL</code>, indicating that the promotion applies to anyone in the specified region, but it can also contain an actual city name.</p><p>By themselves, city names are not unique. For example, what region/country comes to mind when you think of the city name of &#x201C;Paris&#x201D;? Like your author, you probably thought of France&#x2014;unless you happen to live in or near one of the 23+ cities in the USA that are also named Paris.</p><p>A city name is only unique when it is combined with a region. I point this out because unlike the <code>region_code</code> column, you cannot use an <code>IN</code> clause when comparing values for the city. Instead, you have to check if <code>city</code> is <code>null</code> or if <code>city</code> matches the provided <code>$city</code>, and you have to group those comparisons together. In case that&#x2019;s clear as mud, hopefully the following SQL will clarify:</p><pre><code>SELECT * 
FROM   promotions 
WHERE  start_date &lt; $now
       AND end_date &gt; $now
       AND region_code IN ( &apos;*&apos;, $regionCode )
       AND ( city IS NULL 
              OR city = $city )
</code></pre><p>This is the query you need to execute in order to find all of the <strong>possible</strong> promotions for a visitor. In Laravel applications, you can execute raw SQL statements with no problem. However, you can also use model classes, like your <code>Promotion</code> class, and the query builder syntax to execute statements. The Laravel equivalent to the above SQL is the following:</p><pre><code>Promotion::where(&apos;start_date&apos;, &apos;&lt;&apos;, $now)
  -&gt;where(&apos;end_date&apos;, &apos;&gt;&apos;, $now)
  -&gt;whereIn(&apos;region_code&apos;, [&apos;*&apos;, $regionCode])
  -&gt;where(function($query) use ($city) {
      $query-&gt;whereNull(&apos;city&apos;)
          -&gt;orWhere(&apos;city&apos;, $city);
  })
  -&gt;get();
</code></pre><p>Therefore, the first few lines of the <code>getPromotion()</code> method looks like this:</p><pre><code>private function getPromotion($regionCode, $city) {
  $now = date(&apos;Y-m-d&apos;);
  $promotions = Promotion::where(&apos;start_date&apos;, &apos;&lt;&apos;, $now)
    -&gt;where(&apos;end_date&apos;, &apos;&gt;&apos;, $now)
    -&gt;whereIn(&apos;region_code&apos;, [&apos;*&apos;, $regionCode])
    -&gt;where(function($query) use ($city) {
      $query-&gt;whereNull(&apos;city&apos;)
        -&gt;orWhere(&apos;city&apos;, $city);
      })
    -&gt;get();
  // more code here
}
</code></pre><p>But remember: this finds all of the <strong>possible</strong> promotions for the visitor. So, you&#x2019;ll need to process the results in order to find the promotion that best fits the visitor&#x2019;s location. In the code above, the <code>$promotions</code> variable contains a special enumerable object called a <code>Collection</code>. You could iterate over the collection with a normal <code>for</code> or <code>foreach</code> loop. However, a <code>Collection</code> exposes many useful methods that you can use to find a specific record.</p><p>In this case, you want to find a single record that matches the visitor&#x2019;s city or region code, and you can easily find that object by using the <code>first()</code> method, like this:</p><pre><code>private function getPromotion($regionCode, $city) {
  $now = date(&apos;Y-m-d&apos;);
  $promotions = Promotion::where(&apos;start_date&apos;, &apos;&lt;&apos;, $now)
    -&gt;where(&apos;end_date&apos;, &apos;&gt;&apos;, $now)
    -&gt;whereIn(&apos;region_code&apos;, [&apos;*&apos;, $regionCode])
    -&gt;where(function($query) use ($city) {
      $query-&gt;whereNull(&apos;city&apos;)
        -&gt;orWhere(&apos;city&apos;, $city);
      })
    -&gt;get();

  return $promotions-&gt;first(function($record) use ($city, $regionCode) {
    return 
      $record-&gt;city == $city || 
      $record-&gt;region_code == $regionCode || 
      $record-&gt;region_code == &apos;*&apos;;
  });
}
</code></pre><p>A collection&#x2019;s <code>first()</code> method accepts a closure that executes on every item within the collection. This closure first tries to match the record&#x2019;s city with the visitor&#x2019;s city. If there&#x2019;s a match, the city-matching record is returned. If a city-match is not found, the closure tries to match a record&#x2019;s region code with the visitor. Once again, if a match is found, the matching record is returned. If a record cannot be matched by city or region code, then the closure attempts to find a &#x201C;catch all&#x201D; record. If no match is found, it returns <code>null</code>.</p><p>The last method in the <code>PromotionService</code> class brings everything together. It&#x2019;s called <code>getPromotionFor()</code>, and it will create the ipdata client, lookup the provided IP address, and query the database using the resulting location data. The following code is the complete <code>PromotionService</code> class:</p><pre><code>class PromotionService {
  public function getPromotionFor($ipAddress) {
      $client = $this-&gt;createClient();
      $result = $client-&gt;lookup($ipAddress);
      return $this-&gt;getPromotion($result[&apos;city&apos;], $result[&apos;region_code&apos;]);
  }

  private function getPromotion($city, $regionCode) {
      $now = date(&apos;Y-m-d&apos;);
      $promotions = Promotion::where(&apos;start_date&apos;, &apos;&lt;&apos;, $now)
          -&gt;where(&apos;end_date&apos;, &apos;&gt;&apos;, $now)
          -&gt;whereIn(&apos;region_code&apos;, [&apos;*&apos;, $regionCode])
          -&gt;where(function($query) use ($city) {
              $query-&gt;whereNull(&apos;city&apos;)
                  -&gt;orWhere(&apos;city&apos;, $city);
          })
          -&gt;get();
      var_dump($promotions);
      $selectedPromo = null;
      return $promotions-&gt;first(function($item) use ($city, $regionCode) {
          return 
              $item-&gt;city == $city || 
              $item-&gt;region_code == $regionCode || 
              $item-&gt;region_code == &apos;*&apos;;
      });
  }

  private function createClient() {
      $httpClient = new Psr18Client();
      $psr17Factory = new Psr17Factory();
      return new Ipdata(&apos;test&apos;, $httpClient, $psr17Factory);
  }
}
</code></pre><h1 id="using-the-promotionservice-class">Using the <code>PromotionService</code> Class</h1><p>Now that you completed the <code>PromotionService</code> class, all you have to do is use it. Open the project&#x2019;s <code>routes\web.php</code> file and add the following <code>use</code> statement:</p><pre><code>use App\Services\PromotionService;
</code></pre><p>Next, modify the single route defined in the file. You want to create a service object, use its <code>getPromotionFor()</code> method, and pass the result to the view, like this:</p><pre><code>Route::get(&apos;/&apos;, function () {
  $service = new PromotionService();
  $promotion = $service-&gt;getPromotionFor(\request()-&gt;ip());
  return view(&apos;welcome&apos;, [&apos;promotion&apos; =&gt; $promotion]);
});
</code></pre><p>The highlighted code in this code are the additions you need to make. Save the file, and then open the <code>welcome.blade.php</code> file located in your project&#x2019;s <code>resources\views\</code> directory. Directly after the opening <code>&lt;body&gt;</code> tag, add the following code:</p><pre><code>@if (isset($promotion))
    &lt;h2&gt;Grats! You get a {{ $promotion-&gt;discount }} discount at checkout!&lt;/h2&gt;
@endif
</code></pre><p>This code checks to see if the visitor receives a promotion (remember that if no promotion is found, <code>$promotion</code> is <code>null</code>), and if so, it displays a message indicating the discount amount.</p><h1 id="conclusion">Conclusion</h1><p>The web revolutionized the way companies conduct business. It is the &#x201C;great equalizer&#x201D; because small and local businesses have the same potential customer base as large corporations. But that doesn&#x2019;t mean small businesses have to eschew their local base in favor gaining global customers. It&#x2019;s quite the opposite. By leveraging modern tools like ipdata&#x2019;s location API, local businesses can cater to their most loyal customers, strengthening the relationship within the community.</p>]]></content:encoded></item><item><title><![CDATA[What are proxies and how can we detect them?]]></title><description><![CDATA[<p>Proxies come in many different shapes and sizes, some of them are easy to detect, others much harder. Some are used for illegitimate purposes, others are used as standard practise by corporations. In this post, we&#x2019;ll explore the different types of proxies, what they can be used for,</p>]]></description><link>https://ipdata.co/blog/what-are-proxies-and-how-can-we-detect-them/</link><guid isPermaLink="false">624afb5a24d10526e99d8d67</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[Jonathan Kosgei]]></dc:creator><pubDate>Mon, 14 Sep 2020 14:28:54 GMT</pubDate><content:encoded><![CDATA[<p>Proxies come in many different shapes and sizes, some of them are easy to detect, others much harder. Some are used for illegitimate purposes, others are used as standard practise by corporations. In this post, we&#x2019;ll explore the different types of proxies, what they can be used for, and how to detect them.</p><h1 id="what-is-a-proxy">What is a proxy?</h1><p>A proxy is a simple server which forwards requests on your behalf. For example, if you&#x2019;re using a proxy to access google.com, your request will first be sent to the proxy server, which will then in turn make the actual request to google.com, and return the result back to you.</p><p>To help explain it, let&#x2019;s look at a simplified diagram of how your computer connects to a website.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_F170E8C09D53B7EFFC74192A87BBA0B3D9C9F9A5E8B015326F65044980CFAF57_1595955470481_TOR-Normal.png" class="kg-image" alt="Connecting to a website without a proxy or VPN" loading="lazy"></figure><p>And here&#x2019;s how a proxy fits in&#x2026;</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_4EF8B496120BE102F332131C62A717CCB2382C72865F22C08BF34B4EBEA646C9_1599233111289_Proxy.png" class="kg-image" alt="Proxies receive requests and pass them on to the website" loading="lazy"></figure><h2 id="how-is-a-proxy-different-from-a-vpn">How is a proxy different from a VPN?</h2><p>This all looks very similar to a VPN, as we explored in our <a href="https://ipdata.co/blog/blog/tor-detection/">Tor Detection</a> post, but there are some key differences.</p><p>When you connect to a VPN, it establishes a secure, <a href="https://cybernews.com/best-vpn/">encrypted connection between your computer and the Internet</a>. Your computer is connected to a remote and Virtual Private Network, so all Internet traffic will be sent over this connection, regardless of whether it&#x2019;s from your web browser, a native application, or even your operating system checking for updates. Because of this, the VPNs are much heavier and have the potential to add more latency to your traffic. However, they&#x2019;re more fully-functioning and generally more secure.</p><p>Traffic from VPNs can look identical to proxy traffic, so from a detection perspective there&#x2019;s no difference &#x2013; the IP address of the VPN or proxy can be looked up against a database or service, such as <a href="https://ipdata.co">ipdata</a>.</p><h1 id="underlying-technology">Underlying technology</h1><p>The term &#x201C;proxy&#x201D; covers any server that forwards data on behalf of another user or server. Due to the broad coverage, there are many different types and styles of proxies, but most of these will use either HTTP or SOCKS as an underlying technology.</p><h2 id="http-proxies">HTTP Proxies</h2><p>An HTTP proxy is one of the simplest types, and can be written in just a few lines of code. In Node.js, the module <code>node-http-proxy</code> makes the process very simple.</p><pre><code>const httpProxy = require(&apos;http-proxy&apos;);
httpProxy.createProxyServer({ target:&apos;https://dogtreats.com&apos; }).listen(8000);
</code></pre><p>Now, if we run this, any requests to port <code>8000</code> will be forwarded on to <a href="https://dogtreats.com">https://dogtreats.com</a> and look as if they originated from the server where this is running.</p><h2 id="socks-proxies">SOCKS Proxies</h2><p>A <a href="https://en.wikipedia.org/wiki/SOCKS">SOCKS</a> proxy runs at a lower-level than a HTTP proxy, making it far more versatile. SOCKS can forward TCP and UDP connections and is therefore more commonly used than HTTP proxies. A SOCKS proxy can also be run over SSH using OpenSSH, allowing users to securely connect to servers.</p><p><strong>Tor</strong> is a system which effectively links multiple SOCKS proxies together to provide strong anonymity, at the cost of speed and some convenience. <a href="https://ipdata.co/blog/blog/tor-detection/">Read more about Tor detection here</a>.</p><h1 id="proxy-categories">Proxy Categories</h1><p>Any of these proxy types can use any of the proxy technologies, such as SOCKS. A proxy can also fall into multiple categories!</p><p><strong>Transparent</strong> <strong>proxies</strong> simply pass on the request from the user to the destination, without making any modifications at all. <strong>Non-transparent</strong> proxies make some modification to the request, such as adding HTTP headers. Some proxies add an <code>[x-forwarded-for](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)</code> header which includes the original IP address of the user, this would be an example of a non-transparent proxy.</p><p><strong>Open proxies</strong> are simply proxies which are accessibly by anyone on the internet, sometimes by mistake. Due to their open nature, they&#x2019;re often used to provide anonymity and are frequently used for malicious activity. Using open proxies is extremely dangerous, unless you totally trust the proxy service you&#x2019;re connecting to, as they may be snooping on all your internet usage.</p><p><strong>Corporate proxies</strong> are proxies run by corporations, usually hosted on their own servers. Many companies use proxies to control access to the internet and their networks from their employees.</p><p><strong>Private proxies</strong> are very similar to open proxies, but they require some kind of registration and usually promise to be more secure than open proxies.</p><h1 id="detecting-proxies">Detecting proxies</h1><p>It&#x2019;d be impossible to detect all proxy servers, but some can be detected. Proxy providers continually change their IP addresses to try to avoid detection.</p><p>There are multiple lists of known proxy IP addresses available online, and <a href="https://ipdata.co">ipdata</a> combines many of them, along with proprietary lists to detect the larger proxies.</p><p>Additionally, proxies are reasonably likely to be hosted by a cloud hosting provider, such as AWS or OVH. Traffic from hosting providers can usually be detected using the IP address, but of course there are sometimes legitimate reasons for a hosting provider to be calling your service &#x2013; especially if you&#x2019;re running an API.</p><p>Here&#x2019;s an example ipdata response for a proxy:</p><pre><code>curl https://api.ipdata.co/54.39.133.108?api-key=test


{
    ip: &quot;54.39.133.108&quot;,
    is_eu: false,
    city: null,
    region: null,
    region_code: null,
    country_name: &quot;Canada&quot;,
    country_code: &quot;CA&quot;,
    continent_name: &quot;North America&quot;,
    continent_code: &quot;NA&quot;,
    latitude: 43.6319,
    longitude: -79.3716,
    postal: null,
    calling_code: &quot;1&quot;,
    flag: &quot;https://ipdata.co/flags/ca.png&quot;,
    emoji_flag: &quot;&#x1F1E8;&#x1F1E6;&quot;,
    emoji_unicode: &quot;U+1F1E8 U+1F1E6&quot;,
    asn: {
        asn: &quot;AS16276&quot;,
        name: &quot;OVH SAS&quot;,
        domain: &quot;ovh.com&quot;,
        route: &quot;54.39.0.0/16&quot;,
        type: &quot;hosting&quot;
    },
    languages: [
        {
            name: &quot;English&quot;,
            native: &quot;English&quot;
        },
        {
            name: &quot;French&quot;,
            native: &quot;Fran&#xE7;ais&quot;
        }
    ],
    currency: {
        name: &quot;Canadian Dollar&quot;,
        code: &quot;CAD&quot;,
        symbol: &quot;CA$&quot;,
        native: &quot;$&quot;,
        plural: &quot;Canadian dollars&quot;
    },
    time_zone: {
        name: &quot;America/Toronto&quot;,
        abbr: &quot;EDT&quot;,
        offset: &quot;-0400&quot;,
        is_dst: true,
        current_time: &quot;2020-09-04T12:42:43.132643-04:00&quot;
    },
    threat: {
        is_tor: false,
        is_proxy: false,
        is_anonymous: false,
        is_known_attacker: false,
        is_known_abuser: false,
        is_threat: false,
        is_bogon: false
    }
}
</code></pre><p>Here, we can see ipdata hasn&#x2019;t currently got this IP address on a known proxy list (<code>threat.is_proxy</code> ), but the request is coming from a hosting provider ( <code>asn.type</code>).</p><h2 id="blocking-proxies">Blocking proxies</h2><p>Due to the difficulty in reliably detecting proxies, it&#x2019;s usually not worth spending much time trying to block proxies. However, some proxies will be caught by <code>threat.is_proxy</code> and generally, ipdata will help protect your site from threats if you block all requests where <code>threat.is_threat == true</code>.</p><p>There are also many, totally legitimate, use-cases for proxies. Corporate proxies are common and blocking legitimate traffic from these companies could be costly!</p>]]></content:encoded></item><item><title><![CDATA[What is Tor and how can we detect Tor users?]]></title><description><![CDATA[<p>Tor is commonly used to anonymise internet traffic. The core ethos behind Tor is that internet users should have <strong>private</strong> access to an <strong>uncensored</strong> web. Due to this, it&#x2019;s commonly used for illegal activities, such as purchasing weapons or drugs. These illegal sites, known collectively as the &#x201C;</p>]]></description><link>https://ipdata.co/blog/tor-detection/</link><guid isPermaLink="false">624afb5a24d10526e99d8d66</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Mon, 03 Aug 2020 12:37:01 GMT</pubDate><content:encoded><![CDATA[<p>Tor is commonly used to anonymise internet traffic. The core ethos behind Tor is that internet users should have <strong>private</strong> access to an <strong>uncensored</strong> web. Due to this, it&#x2019;s commonly used for illegal activities, such as purchasing weapons or drugs. These illegal sites, known collectively as the &#x201C;dark web&#x201D;, are only accessible using Tor using special links &#x2013; for example, the original <a href="https://en.wikipedia.org/wiki/Silk_Road_(marketplace)">Silk Road</a> URL was <em>silkroad6ownowfk.onion</em>. &#xA0;On the public internet, Tor is often used by credit card fraudsters to hide their true identity when they&#x2019;re using stolen card information.</p><p>Due to a large amount of illegal use, some internet providers and websites block access from Tor. We&#x2019;ll explore how to detect Tor traffic, how to block it, and analyse whether blocking is a good approach.</p><h2 id="how-does-tor-work">How does Tor work?</h2><p>Tor uses a process called &#x201C;onion routing&#x201D;, and its name was originally an acronym for &#x201C;The Onion Router&#x201D;. Onion routing was developed by the U.S. Naval Research Lab and is named &#x201C;onion&#x201D; due to the layers of privacy and encryption.</p><p>To help explain it, let&#x2019;s look at a simplified diagram of how your computer connects to a website.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_F170E8C09D53B7EFFC74192A87BBA0B3D9C9F9A5E8B015326F65044980CFAF57_1595955470481_TOR-Normal.png" class="kg-image" alt="Connecting to a website without a VPN or Tor" loading="lazy"></figure><p>When you open a website, your real IP address is exposed, which can be used to determine your location, ISP and much more. Using HTTPS means your connection is encrypted, so your data is safe from interception, snooping, or tampering. However, your ISP can see which website you&#x2019;re connecting to, or even change it, which means they are often asked to block websites, such as The Pirate Bay.</p><p>To get around this, some users will use a VPN, which sits between the connection between your computer and the website, helping to hide your real IP and identity.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_F170E8C09D53B7EFFC74192A87BBA0B3D9C9F9A5E8B015326F65044980CFAF57_1595955713945_TOR-VPN.png" class="kg-image" alt="Connecting to a website with a VPN" loading="lazy"></figure><p>Tor is like chaining together multiple VPNs at the same time. Your laptop connects to a Tor entry node, which in turn connects to a Tor relay. Your connection will pass through multiple relays, with each step adding another layer of security and re-encrypting the data.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_F170E8C09D53B7EFFC74192A87BBA0B3D9C9F9A5E8B015326F65044980CFAF57_1595955729755_TOR-Tor.png" class="kg-image" alt="Connecting to a website with Tor" loading="lazy"></figure><p>A Tor exit node ends up connecting to the website you want to visit. Only the Tor entry node knows your real IP address.</p><h2 id="how-can-i-detect-and-block-tor-traffic">How can I detect and block Tor traffic?</h2><p>To detect Tor traffic, we need a list of all of the known IPs of Tor exit nodes. We don&#x2019;t need any information about the relays or the entry nodes, as they&#x2019;ll never connect to our website.</p><p>Tor publishes an <a href="https://check.torproject.org/torbulkexitlist">official list of exit node IP address</a>. There are fewer than 2000 IPs in that list, so it&#x2019;s not too difficult to check if the connecting IP is a known Tor exit node. If we had a simple Express application, we could detect and block this traffic using a middleware.</p><pre><code>const fs = require(&quot;fs&quot;);
const express = require(&quot;express&quot;);
const app = express();

// Download the official list to a txt file, split it into an array of IP addresses
const torIPs = fs.readFileSync(&quot;./tor-exit-nodes.txt&quot;).split(&quot;\n&quot;);

app.use((req, res, next) =&gt; {
  const ip = req.connection.remoteAddress;
  // Block any known Tor exit node IP addresses
  if (torIPs.contains(ip)) {
    res.status(403).send(&apos;Tor is not allowed&apos;);
    return;
  }
  next();
});

// Serve the home page
app.get(&quot;/&quot;, (req, res) =&gt;
  res.sendFile(&quot;./index.html&quot;, { root: __dirname })
);

app.listen(8000);
</code></pre><p>This approach will catch most Tor connections, but there are also some unofficial exit nodes which aren&#x2019;t on the published list. <a href="https://ipdata.co">ipdata</a> maintains an up-to-date list of the official exit nodes, and combines that with a proprietary list of unofficial Tor IPs. The data is aggregated every 15 minutes and updates every hour, meaning you&#x2019;ll get the most accurate Tor detection possible.</p><p>Let&#x2019;s update our application to use ipdata&#x2019;s API instead.</p><pre><code>const express = require(&quot;express&quot;);
const app = express();
const axios = require(&apos;axios&apos;);

// Get an ipdata API Key from here: https://ipdata.co/sign-up.html
const IPDATA_API_KEY = &quot;test&quot;;
const getIpData = async (ip) =&gt; {
  const response = await axios.get(
    `https://api.ipdata.co/${ip}?api-key=${IPDATA_API_KEY}`
  );
  return response.data;
};

app.use(async (req, res, next) =&gt; {
  const ip = req.connection.remoteAddress;
  const ipdata = await getIpData(ip);
  // Block any known Tor traffic
  if (ipdata.threat.is_tor) {
    res.status(403).send(&apos;Tor is not allowed&apos;);
    return;
  }
  next();
});

// Serve the home page
app.get(&quot;/&quot;, (req, res) =&gt;
  res.sendFile(&quot;./index.html&quot;, { root: __dirname })
);

app.listen(8000);
</code></pre><h2 id="should-i-block-tor-traffic">Should I block Tor traffic?</h2><p>Now that we&#x2019;ve shown how to block Tor traffic, it&#x2019;s important to understand that Tor isn&#x2019;t only used for illegal activities. Tor can be used to bypass the <a href="https://en.wikipedia.org/wiki/Great_Firewall">Great Firewall</a> and other regulating systems to allow the free movement of information &#x2013; currently all versions of Wikipedia are blocked in China. Users are becoming more privacy-conscious, leading to increased usage of VPNs and Tor. So the question should be, &#x201C;do I want to risk blocking legitimate traffic?&#x201D;</p><p>If you&#x2019;re running an e-commerce store, you&#x2019;ll need to be wary of fraud, so you may want to block Tor. It&#x2019;s unlikely that your store would be blocked by any firewalls, and unless you&#x2019;re selling sensitive products, blocking Tor should have minimal impact on your real customers.</p><p>If you&#x2019;re running a news site, blocking Tor could restrict people in certain areas from accessing the information they need. There&#x2019;s minimal risk of fraud, unless you&#x2019;re taking payments from your site, so it&#x2019;d usually be best to allow Tor traffic, but monitor it closely for fraudulent activity.</p><h2 id="conclusions">Conclusions</h2><p>Tor traffic can be blocked using a list of IP addresses, or by using the <a href="https://ipdata.co">ipdata</a> API, which also exposes other data such as the user&#x2019;s location and more detailed threat data. However, it&#x2019;s worth considering whether blocking all Tor traffic is the right option for you.</p><p>What&#x2019;s your experience with Tor? Have you had issues with visitors hitting your website via Tor? Let us know in the comments!</p>]]></content:encoded></item><item><title><![CDATA[Using Geolocation and Open Exchange Rate to Convert Currency]]></title><description><![CDATA[<p>The Internet is many things; one of them just happens to be a global e-commerce platform. Since the early 2000s, one of the Internet&apos;s many promises has been the immediate expansion of a company&apos;s customer base. Local companies are no longer bound to the tether of</p>]]></description><link>https://ipdata.co/blog/localize-currency/</link><guid isPermaLink="false">624afb5a24d10526e99d8d65</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Sat, 18 Jul 2020 23:37:53 GMT</pubDate><content:encoded><![CDATA[<p>The Internet is many things; one of them just happens to be a global e-commerce platform. Since the early 2000s, one of the Internet&apos;s many promises has been the immediate expansion of a company&apos;s customer base. Local companies are no longer bound to the tether of their physical location; they can serve customers across their country and even around the world.</p><p>But with growth comes complexity. Serving a global audience requires a company to adapt their systems for a global economy. That could be a daunting task even five years ago, but it is now relatively simple with services like <a href="https://ipdata.co/">ipdata</a> and <a href="https://openexchangerates.org/">Open Exchange Rates</a>.</p><p>In this article, you will build a simple page that converts product prices to the visitor&apos;s local currency in real time using the ipdata and Open Exchange Rates APIs.</p><h2 id="getting-started">Getting Started</h2><p>We need API keys before we can do anything. If you don&apos;t already have an ipdata account, <a href="https://dashboard.ipdata.co/sign-up.html">sign up for a free account</a>. Next, create an account at Open Exchange Rates. Their pricing page does not prominently display their free plan, but don&apos;t fear&#x2014;<a href="https://openexchangerates.org/signup/free">it&apos;s forever free</a>.</p><p>For this article, we&apos;ll build a simple web page that uses plain &#x2018;ol JavaScript to display a list of products and their respective prices in the visitor&apos;s currency. The only library you&apos;ll use in this project is the <a href="https://openexchangerates.github.io/money.js/">money.js library</a> for converting our product price to the visitor&apos;s local currency.</p><p>The overall process is simple:</p><ul><li>fetch product data (it&apos;s just a static .json file to emulate a local service)</li><li>fetch the visitor&apos;s location data from ipdata&apos;s API</li><li>fetch the currency exchange information from Open Exchange Rates</li><li>display the product data with the converted currency values</li></ul><p>Because the page issues HTTP requests, it must be served from an HTTP server. Any HTTP server will do. If you don&apos;t have easy access to one, the (lite-server NPM package)[<a href="https://github.com/johnpapa/lite-server">https://github.com/johnpapa/lite-server</a>] can get you up and running with a simple HTTP server with no configuration.</p><h2 id="building-the-product-page">Building the Product Page</h2><p>You&apos;ll emulate the products &#x201C;service&#x201D; with a simple JSON file. Create a new file called <code>data.json</code>, and type the following:</p><pre><code>[
    { &quot;name&quot; : &quot;Guitar&quot;, &quot;price&quot; : 1500 },
    { &quot;name&quot; : &quot;Piano&quot;, &quot;price&quot; : 5000 },
    { &quot;name&quot; : &quot;Cello&quot;, &quot;price&quot; : 15000 }
]
</code></pre><p>Next, create an HTML file with the following markup:</p><pre><code>&lt;html lang=&quot;en&quot;&gt;
&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Musical Instruments&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;


    &lt;script src=&quot;https://openexchangerates.github.io/money.js/money.min.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><p>As you can see, the only dependency in this page is the Open Exchange Rate&#x2019;s money.js library. Of course, you could use the respective JavaScript libraries for the ipdata and Open Exchange Rate services, but you will directly interact with those web APIs using <code>fetch()</code>.</p><p>Now you need someplace to display the product list. You could build everything using JavaScript, but doing so without a UI-focused library is an exercise in tedium. Instead, add the following table to the body of the page. Later, you will programmatically insert rows into the table.</p><pre><code>&lt;table id=&quot;product-table&quot;&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;Name&lt;/th&gt;
            &lt;th&gt;Price&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;&lt;/tbody&gt;
&lt;/table&gt;
</code></pre><h2 id="fetching-data">Fetching Data</h2><p>Now it&#x2019;s time to fetch your data. For the sake of simplicity, use <code>async/await</code> when calling <code>fetch()</code>. All modern browsers support <code>async/await</code>, and it saves you from the Promise callback hell that is associated with multiple asynchronous operations.</p><p>Begin by defining an immediately invoked asynchronous function, as shown in the following code:</p><pre><code>(async function() {
    // all of our JavaScript code here
})();
</code></pre><p>The <code>Fetch</code> API is a huge improvement over <code>XMLHttpRequest</code>, but it can still be tedious to use when working with HTTP resources that return JSON. However, a helper function that fetches data and returns the resulting parsed JavaScript object can be beneficial. The following <code>fetchJson()</code> function does just that:</p><pre><code>async function fetchJson(url) {
    let res = await fetch(url);
    return await res.json();
}
</code></pre><p>It simply fetches the provided URL and uses the response object&#x2019;s <code>json()</code> method to transform the JSON structure into a usable JavaScript object.</p><p>Next, fetch your data with the following three lines:</p><pre><code>let products = await fetchJson(&apos;data.json&apos;);
let location = await fetchJson(&apos;https://api.ipdata.co?api-key=&lt;YOUR_API_KEY&gt;&amp;fields=currency&apos;);
let exchange = await fetchJson(&apos;https://openexchangerates.org/api/latest.json?app_id=&lt;YOUR_API_KEY&gt;&apos;);
</code></pre><p>This code fetches the product data, the visitor&#x2019;s location information, and the latest currency exchange information. This code is straight-forward, but let&apos;s focus on the ipdata request.</p><p>First, ipdata&#x2019;s API returns location-based information about the device that issued the request. In this case, that is the user&#x2019;s browser, and that is exactly what we want. Remember, you are converting product prices to the user&#x2019;s currency based upon their current location, and you automatically get that with just issuing a &quot;blank&quot; request.</p><p>Second, you don&#x2019;t need to know everything about the user&#x2019;s location; the only thing you really care about is the currency information. Therefore, use the <code>fields</code> query parameter to tell the API that you only want the <code>currency</code> object in the returned JSON structure. This saves bandwidth and resources on the device.</p><h2 id="displaying-the-products">Displaying the Products</h2><p>Before using the money.js library, you need to initialize its base and rates information. It&#x2019;s just takes two lines of code using data from the Open Exchange Rates API:</p><pre><code>fx.base = exchange.base;
fx.rates = exchange.rates;
</code></pre><p>Next, get a reference to the <code>&lt;tbody/&gt;</code> in the table and start iterating over the products, like this:</p><pre><code>let tbody = document.getElementById(&apos;product-table&apos;).tBodies[0];

products.forEach(function(p) {
   // more code here    
});
</code></pre><p>The goal here is to use the <code>tbody</code> object to create a new row for each iteration through the <code>products</code> array. While that sounds like a lot of tedious DOM code, it&#x2019;s actually quite simple. The API for working with tables and their related objects are very intuitive, as shown in the following example:</p><pre><code>products.forEach(function(p) {
    let row = tbody.insertRow();

    let nameCell = row.insertCell();
    let priceCell = row.insertCell();

    nameCell.innerHTML = p.name;
    // price cell here    
});
</code></pre><p>In this code, you create a new row using <code>insertRow()</code>. This automatically creates the resulting <code>&lt;tr/&gt;</code> element and appends it to the <code>&lt;tbody/&gt;</code> element. Using the newly created <code>row</code> object, you can then create the cells for that row using the <code>insertCell()</code> method. This method automatically creates the necessary <code>&lt;td/&gt;</code> element and appends it to the row. Don&#x2019;t you wish the rest of the DOM API is as easy to use?</p><p>The final piece of this puzzle is the price. The money.js library provides multiple ways to convert values from one currency to another. For this project, use the fluent approach, shown below:</p><pre><code>fx(price).from(&#x2018;FROM_CURRENCY_CODE&#x2019;).to(&#x2018;TO_CURRENCY_CODE&#x2019;)
</code></pre><p>For this project, the user is whoever is viewing the page (you, in this case). So, if you&#x2019;re going to convert the price to your own currency, FROM_CURRENCY_CODE needs to be a currency that is not yours. This example uses the euro (code: EUR) as the starting currency, but feel free to use whichever currency you want. To plug in your values, the code will look like the following:</p><pre><code>fx(p.price).from(&apos;EUR&apos;).to(location.currency.code).toFixed(2);
</code></pre><p>Conversions rarely result in clean numbers; chances are very good that the resulting amount will have more than two decimal places. Therefore, call <code>toFixed()</code> to constrain the result to two decimal places.</p><p>But converting the price is only part of the solution; you also need to include the correct currency symbol. Thankfully, you already have that from the ipdata API. Just use the <code>currency.symbol</code> property and concatenate it with the conversion result, like this:</p><pre><code>priceCell.innerHTML = location.currency.symbol + &apos;&apos; + 
    fx(p.price).from(&apos;EUR&apos;).to(location.currency.code).toFixed(2);
</code></pre><p>So the JavaScript code in its entirety is the following:</p><pre><code>(async function() {
    async function fetchJson(url) {
        let res = await fetch(url);

        return await res.json();
    }

    let products = await fetchJson(&apos;data.json&apos;);
    let location = await fetchJson(&apos;https://api.ipdata.co?api-key=&lt;YOUR_API_KEY&gt;&amp;fields=currency&apos;);
    let exchange = await fetchJson(&apos;https://openexchangerates.org/api/latest.json?app_id=&lt;YOUR_API_KEY&gt;&apos;);

    fx.base = exchange.base;
    fx.rates = exchange.rates;

    let tbody = document.getElementById(&apos;product-table&apos;).tBodies[0];

    products.forEach(function(p) {
        let row = tbody.insertRow();

        let nameCell = row.insertCell();
        let priceCell = row.insertCell();

        nameCell.innerHTML = p.name;
        priceCell.innerHTML = location.currency.symbol + &apos;&apos; + 
            fx(p.price).from(&apos;EUR&apos;).to(location.currency.code).toFixed(2);
    });
})();
</code></pre><p>Save the file and request it from your HTTP server. You should see the products displayed in the page with the prices converted to your currency.</p><h2 id="personalizing-experiences">Personalizing Experiences</h2><p>The web is a global community, and it can certainly feel cold and inhuman, but using location data to personalize your user&#x2019;s experiences goes a long way toward developing a relationship with your users. Converting product and service prices in real-time is just one way that location data can help us build meaningful experiences. No matter what kind of site you&#x2019;re building, use location data to help your users feel at home. They&#x2019;ll more than likely come back.</p>]]></content:encoded></item><item><title><![CDATA[How to pre-select country calling codes using ipdata]]></title><description><![CDATA[<p>Knowing which country your customer is in can be valuable for many reasons. One of the most frustrating parts of any website is the form filling, and entering your country calling code is one of the worst.</p><p>Even with a well-designed form, scrolling through all the calling codes (around 243,</p>]]></description><link>https://ipdata.co/blog/country-calling-codes/</link><guid isPermaLink="false">624afb5a24d10526e99d8d64</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Fri, 19 Jun 2020 22:37:03 GMT</pubDate><content:encoded><![CDATA[<p>Knowing which country your customer is in can be valuable for many reasons. One of the most frustrating parts of any website is the form filling, and entering your country calling code is one of the worst.</p><p>Even with a well-designed form, scrolling through all the calling codes (around 243, depending on which source you use) is difficult and slow. If you&#x2019;re able to search the list, it&#x2019;s still frustrating, is it +44 for the Isle of Man, Guernsey, United Kingdom, or Jersey? Does it matter? Will selecting the wrong country break everything? All these questions can easily be answered without any user input and solved using some simple JavaScript.</p><h2 id="how-not-to-do-it">How <strong>NOT</strong> to do it</h2><p>First, let&#x2019;s take a look at how many websites implement their country code selection&#x2026;</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_A314B1C2CF49D7400CD98288F6F336D1B3AB07AA42DE975DA7A010FDA96460E0_1591905703497_Screenshot+2020-06-11+at+21.01.20.png" class="kg-image" alt="Don&#x2019;t subject your customers to this pain!" loading="lazy"></figure><p>Scrolling through hundreds of options is not good enough for your users. Let&#x2019;s give them something better.</p><h2 id="how-to-do-it">How to do it</h2><p>There&#x2019;s an excellent library called <code>[intl-tel-input](https://github.com/jackocnr/intl-tel-input)</code> which can provide us with an easy-to-use and pretty input for collecting phone numbers with their calling code. Let&#x2019;s add it to our page and see how much better it is.</p><pre><code>&lt;!-- Load the intl-tel-input styles --&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/css/intlTelInput.min.css&quot; /&gt;

&lt;!-- This is our phone number input --&gt;
&lt;input type=&quot;tel&quot; id=&quot;phone&quot; /&gt;

&lt;!-- Load the intl-tel-input script --&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/js/intlTelInput.min.js&quot;&gt;&lt;/script&gt;

&lt;!-- Initialise the library --&gt;
&lt;script&gt;
  var input = document.querySelector(&quot;#phone&quot;);
  window.intlTelInput(input, {
    // utilsScript is useful for providing validation and pretty formatting
    utilsScript: &quot;https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/js/utils.min.js&quot;,
  });
&lt;/script&gt;
</code></pre><p>And here are the results, much better!</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_A314B1C2CF49D7400CD98288F6F336D1B3AB07AA42DE975DA7A010FDA96460E0_1591907655720_decent-calling-code.gif" class="kg-image" alt loading="lazy"></figure><p>Unfortunately, due to the large number of countries, there&#x2019;s still a lot of scrolling involved to find the correct calling code. The library allows users to type in their own calling code, and it&#x2019;ll update the flag accordingly, but we can still do better.</p><h2 id="auto-select-the-correct-country">Auto-select the correct country</h2><p>Using <a href="https://ipdata.co">ipdata</a>, we can look up the user&#x2019;s country and use that to pre-select the correct calling code. <code>intl-tel-input</code> has an easy option to help make this even simpler.</p><pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/css/intlTelInput.min.css&quot; /&gt;

&lt;input type=&quot;tel&quot; id=&quot;phone&quot; /&gt;

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/js/intlTelInput.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
  var input = document.querySelector(&quot;#phone&quot;);
  window.intlTelInput(input, {
    initialCountry: &quot;auto&quot;,
    geoIpLookup: function(success) {
      // Get your api-key at https://ipdata.co/
      fetch(&quot;https://api.ipdata.co/?api-key=test&quot;)
        .then(function(response) {
          if (!response.ok) return success(&quot;&quot;);
          return response.json();
        })
        .then(function(ipdata) {
          success(ipdata.country_code);
        });
    },
    utilsScript: &quot;https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.3/js/utils.min.js&quot;,
  });
&lt;/script&gt;
</code></pre><p>Now, when the library loads, it&#x2019;ll call our <code>geoIpLookup</code> function and use the country code returned by ipdata. If it&#x2019;s unable to detect the country for any reason, it&#x2019;ll fall back to the default country.</p><p>Let&#x2019;s see what happens if we load the page from a location in the &#xC5;land Islands, which is located right at the bottom of the list.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_A314B1C2CF49D7400CD98288F6F336D1B3AB07AA42DE975DA7A010FDA96460E0_1591908982368_good-calling-code.gif" class="kg-image" alt loading="lazy"></figure><p>Now, we&#x2019;re greeted with the familiarity of our native flag and allowed to immediately begin entering our phone number without any scrolling. If, for example, the customer is on holiday in a different country, they can still select the correct calling code using the selector.</p><h2 id="conclusions">Conclusions</h2><p>Making your customers happy should be at the top of your priority list. Giving them slow, clunky, and difficult to use country calling code selector will do exactly the opposite. In just a few lines of code, we&#x2019;ve been able to totally transform the user experience into a quick and pleasant one.</p><p>Location information can be used for much more than collecting phone numbers with country calling codes. Watch this space for more examples of using <a href="https://ipdata.co">ipdata.co</a> to improve your users&#x2019; form-filling experience!</p>]]></content:encoded></item><item><title><![CDATA[How to Get the IP Address in JavaScript]]></title><description><![CDATA[<p>To most people, an IP address is just that&#x2014;the address of a device on the internet. But for application developers, it is an incredibly valuable piece of information. Over the years, countless bytes of information have been collected and cataloged about almost every device that has accessed the</p>]]></description><link>https://ipdata.co/blog/how-to-get-the-ip-address-in-javascript/</link><guid isPermaLink="false">624afb5a24d10526e99d8d62</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Thu, 28 May 2020 19:50:00 GMT</pubDate><content:encoded><![CDATA[<p>To most people, an IP address is just that&#x2014;the address of a device on the internet. But for application developers, it is an incredibly valuable piece of information. Over the years, countless bytes of information have been collected and cataloged about almost every device that has accessed the internet, and by using ipdata&#x2019;s API, application developers can access a tremendous amount of location-oriented data to enhance their users&#x2019; experience.</p><p>But of course, you need an IP address before you can use the wealth of information provided by ipdata. In this article, you will learn how to retrieve an IP addresses with JavaScript&#x2014;both from within the browser and in your server application.</p><h1 id="retrieving-your-user-s-ip-in-the-browser">Retrieving Your User&#x2019;s IP in the Browser</h1><p>The browser is the OS of the web, and with more processing being handled within the browser, it makes some sense to retrieve the user&#x2019;s IP address so that you can use it in your client-side code. The problem, however, is that the browser doesn&#x2019;t expose any network-oriented information about the device.</p><h2 id="1-using-ipdata">1. Using ipdata</h2><p>You must therefore rely upon a web service to provide that information for you. You could write your own service as part of your server-side application, or you can use a third-party solution to retrieve your user&#x2019;s IP address. ipdata&#x2019;s web service is a perfect solution for this problem. All you need is an API key (go ahead and <a href="https://dashboard.ipdata.co/sign-up.html">sign up for free</a>). For example:</p><pre><code>function json(url) {
  return fetch(url).then(res =&gt; res.json());
}

let apiKey = &apos;your_api_key&apos;;
json(`https://api.ipdata.co?api-key=${apiKey}`).then(data =&gt; {
  console.log(data.ip);
  console.log(data.city);
  console.log(data.country_code);
  // so many more properties
});
</code></pre><p>This code defines a helper function called <code>json()</code>. It simplifies the process of using <code>fetch()</code> with resources that return a JSON structure, as most APIs do. Next, the <code>json()</code> function is used to send a GET request to ipdata&#x2019;s default endpoint: <a href="http://api.ipdata.co">http://api.ipdata.co</a>. You can use this endpoint to query any IP address, but if you do not provide one with the request, the web service uses the address of the device that issued the request: the browser in this case.</p><p>The web service returns a JSON payload that contains a wealth of information&#x2014;not just the user&#x2019;s IP address, but all the locational data that ipdata has about that address. For example: the ISP, city, state, country, continent, etc are provided in the payload. Because of this, you do not need to issue any other HTTP requests because you already have the information you need.</p><p>Sometimes, however, you do not need all the extraneous locational information, and ipdata&#x2019;s API lets you filter the results by including the <code>fields</code> query parameter. For example:</p><pre><code>json(`https://api.ipdata.co?api-key=${apiKey}&amp;fields=ip`).then(data =&gt; {
  console.log(data.ip);
});
</code></pre><p>The JSON payload contains only the fields specified by the <code>fields</code> query parameter. So, in this code, the resulting JavaScript object (named <code>data</code>) has a single property called <code>ip</code>.</p><h2 id="2-using-cloudflare">2. Using Cloudflare</h2><p>ipdata&apos;s web service is very easy to use. However, another (albeit limited) option is CloudFlare&#x2019;s trace utility found at <a href="https://www.cloudflare.com/cdn-cgi/trace">https://www.cloudflare.com/cdn-cgi/trace</a>. It returns a plain-text set of key/value pairs, and the following shows a condensed example of the format:</p><pre><code>h=www.cloudflare.com
ip=178.25.55.75
ts=1605854122.426
visit_scheme=https
uag=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36
</code></pre><p>As you can see, the output contains an <code>ip</code> key with its associated value, and you can parse it out with a regular expression like this:</p><pre><code>function text(url) {
  return fetch(url).then(res =&gt; res.text());
}

text(&apos;https://www.cloudflare.com/cdn-cgi/trace&apos;).then(data =&gt; {
  let ipRegex = /[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/
  let ip = data.match(ipRegex)[0];
  console.log(ip);
});
</code></pre><p>This code defines a <code>text()</code> helper function that is similar to <code>json()</code>, but of course, it reads the response stream as text instead of a JSON structure.</p><p>Inside the callback function, you define a regular expression that matches an IPv4 address, and you use that regex to find a match within the response payload. The result gives you the user&#x2019;s IP address that you can then use in your client-side code.</p><h2 id="3-using-amazon">3. Using Amazon</h2><p>A similar utility provided by AWS provides another reliable option to query your user&#x2019;s IP address.</p><pre><code>function getIPFromAmazon() {
  fetch(&quot;https://checkip.amazonaws.com/&quot;).then(res =&gt; res.text()).then(data =&gt; console.log(data))
}

getIPFromAmazon()
</code></pre><p>This will give you</p><pre><code>178.25.55.75
</code></pre><h1 id="retrieving-the-client-s-ip-in-the-server-nodejs-">Retrieving the Client&#x2019;s IP in the Server (NodeJS)</h1><p>IP stands for Internet Protocol, and it implements a type of networking called <a href="https://en.wikipedia.org/wiki/Packet_switching">packet switching</a>. It has the concept of hosts (machines), and it defines how data, in the form of packets, are sent between the hosts. In the case of HTTP, there are two hosts: client and server.</p><p>When a browser issues an HTTP request to a server, the request is transmitted from the client to the server in the form of IP packets. These packets contain a variety of information, but there are two particularly important things that every packet has: the source host address and the destination host address. Because every packet contains the client address, your server application can retrieve the client&#x2019;s IP address from the packet.</p><p>Every framework, regardless of language and/or platform, has some mechanism for retrieving the client&#x2019;s IP address. For Node.js applications, there are two such mechanisms:</p><ul><li>the <code>x-forwarded-for</code> header.</li><li>the <code>request.connection.remoteAddress</code> property.</li></ul><p>There is a slight issue, however; the <code>x-forwarded-for</code> header can contain multiple, comma-separated IP addresses. Thankfully, the first IP address in the <code>x-forwarded-for</code> header is typically the correct IP address. With that in mind, consider the following code:</p><pre><code>const http = require(&apos;http&apos;);

function requestListener(req, res) {
  let forwarded = req.headers[&apos;x-forwarded-for&apos;]
  let ip = forwarded ? forwarded.split(/, /)[0] : req.connection.remoteAddress;
  res.writeHead(200);
  res.end(ip);
}

const server = http.createServer(requestListener);
server.listen(3000);

console.log(&apos;Server listening at http://localhost:3000&apos;);
</code></pre><p>A lot of this code is boilerplate, so let&#x2019;s focus on the <code>requestListener()</code> function. The first line of the function retrieves the value of the <code>x-forwarded-for</code> header. It is not always guaranteed to be present. So, in the second line, you check if there is a value, and if so, you split it on a comma (<code>,</code>) and use the first item in the resulting array. If the <code>x-forwarded-for</code> header doesn&#x2019;t exist, then you use <code>req.connection.remoteAddress</code> as a fallback.</p><p>Viola! You have the client&#x2019;s IP address. It may not be 100% accurate (the end-user could be using a VPN to mask their true IP), but it is more reliable than getting the client&#x2019;s address through the browser.</p><h1 id="customize-your-content-with-ips">Customize Your Content with IPs</h1><p>As you can see, using JavaScript to find an IP address is relatively simple, and it&#x2019;s likely that you&#x2019;ll want to do something with that address. Maybe you&#x2019;ll want to <a href="https://ipdata.co/blog/blog/showing-promotions-based-on-users-location/">show different promotions or ads to your users based upon their region</a>, or you may need to <a href="https://ipdata.co/blog/blog/localize-currency/">convert currency on the fly</a>. Regardless of your needs, ipdata&#x2019;s web service provides comprehensive locational information based upon your user&#x2019;s IP address. <a href="https://dashboard.ipdata.co/sign-up.html">Go ahead and setup an account for free</a>. It only takes a couple of minutes to get an API key, and you can use it experiment with the API to see what it can do for you.</p>]]></content:encoded></item><item><title><![CDATA[Guide to Internationalization in JavaScript]]></title><description><![CDATA[<h2 id="what-is-internationalization-in-javascript"><strong>What Is Internationalization in JavaScript?</strong></h2><p>Internationalization, also called i18n, involves creating a new website or changing your current site in such a way that your site can adapt to a certain local language. Localization, which we touched on in the introduction, is the next step that developers can take to</p>]]></description><link>https://ipdata.co/blog/guide-to-internationalization-in-javascript/</link><guid isPermaLink="false">624afb5a24d10526e99d8d63</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Thu, 21 May 2020 19:52:12 GMT</pubDate><content:encoded><![CDATA[<h2 id="what-is-internationalization-in-javascript"><strong>What Is Internationalization in JavaScript?</strong></h2><p>Internationalization, also called i18n, involves creating a new website or changing your current site in such a way that your site can adapt to a certain local language. Localization, which we touched on in the introduction, is the next step that developers can take to implement one or more languages.</p><h2 id="popular-libraries-for-internationalization"><strong>Popular Libraries for Internationalization</strong></h2><p>Internationalizing sites is easier these days with the many open-source JavaScript libraries available. I&#x2019;ve found the following libraries to be quite useful.</p><h3 id="formatjs"><strong>FormatJS</strong></h3><p><a href="https://formatjs.io/">FormatJS</a> is a collection of JavaScript libraries that helps with number, time, and date formatting. Not only that, but this library also helps you compare language-sensitive strings. The best part is that it includes React-Intl, thus making your React JavaScript app development easier.</p><h4 id="angulari18n"><strong>AngularI18N</strong></h4><p>If your app is in Angular, <a href="https://angular.io/guide/i18n">AngularI18N</a> will help you with templates and tools. You can use this library to build helpers that can internationalize and localize your app.</p><h3 id="moment-js"><strong>Moment.js</strong></h3><p><a href="https://momentjs.com/">Moment.js</a> helps with streamlining the date-time format. It can also match the format in a specific time zone.</p><h3 id="i18next"><strong>i18next</strong></h3><p>You can use <a href="https://www.i18next.com/">i18next</a> with Vue, Angular, React, or any framework of your choice. The library helps detect the user&#x2019;s language. Once done, i18next loads and inserts translations. In addition, it also supports standard features of i18n.</p><h3 id="globalize-js"><strong>Globalize JS</strong></h3><p>One of the most popular libraries, <a href="https://github.com/globalizejs/globalize">Globalize JS</a> uses the Common Locale Data Repository (CLDR), the largest available base of locale data. The library uses CLDR to get the formatting and paring pattern for date-time, number, messages, units, and currencies.</p><p>In the next section, I&#x2019;ll guide you on how to internationalize your JavaScript web app using Globalize. We&#x2019;ll use Globalize because it uses the largest standard database of locale data.</p><h2 id="a-guide-to-internationalization-using-globalize-js"><strong>A Guide to Internationalization Using Globalize JS</strong></h2><p>As I noted in the previous section, Globalize uses CLDR from the Unicode Consortium. Because it has such a huge base, you don&#x2019;t have to think about adding any additional modules. Besides, you don&#x2019;t need to use the entire library. The content is separated from the code. So, if you want to use the locale data for, say, two or three specific zones, you can pick the modules that you need.</p><p>Let&#x2019;s get started. We&#x2019;ll discuss the installation first and then move on to the features.</p><h3 id="how-to-install"><strong>How to Install</strong></h3><p>Your first step is to find out what modules you need. Check out this <a href="http://johnnyreilly.github.io/globalize-so-what-cha-want/#/?currency=true&amp;date=true&amp;message=true&amp;number=true&amp;plural=true&amp;relativeTime=true&amp;unit=true">link</a>, which lets you can check or uncheck the modules. The site will tell you what CLDR JavaScript files and JSON files you need and in what order you need to arrange them.</p><figure class="kg-card kg-image-card"><img src="https://lh6.googleusercontent.com/azGnwgOkvnEY5KgDTYzrnOqaNo55T8Jtp8fEFA-rBLDA8JmKjYDrHyrlmHTs4eRNRRDz0C6RjV3exv7MMQiYbduYpRtyCRW-ek-xezm8Y9A2UP9EelMmwiQ3-K3mm2007jHEhnnW" class="kg-image" alt loading="lazy"></figure><p>To install Globalize, you need to have Node.js installed in your system. If you already have Node.js, you can install the entire Globalize library along with its dependencies by running the following command in cmd using npm.</p><pre><code>npm install globalize cldr-data --save</code></pre><p>Now, let&#x2019;s find out how to format the date-time, language, and currency using Globalize.</p><h3 id="formatting-the-date"><strong>Formatting the Date</strong></h3><p>First, you need to include the library in your code. You can do that as follows:</p><p><code>var Globalize = require(&apos;globalize&apos;);</code></p><p>Next, include the CLDR data:</p><p><code>var cldrData = require(&apos;cldr-data&apos;);</code></p><p>And don&#x2019;t forget to load the supplemental data:</p><p><code>Globalize.load(cldrData.entireSupplemental());</code></p><p>Finally, load the specific locale data that you need. For this example, I&#x2019;ll show you how to format your date-time in the Italian format.</p><p><code>Globalize.load(cldrData.entireMainFor(&apos;it&apos;, &apos;en-US&apos;, &apos;en-GB&apos;));</code></p><p>Now, let&#x2019;s format the date in the U.S., UK, and Italian formats:</p><pre><code>// Here, we declare today&apos;s date
var date = new Date(2020, 2, 15);

// Print date in Italian format
console.log(Globalize(&apos;it&apos;).formatDate(date));

// Print date in US format
console.log(Globalize(&apos;en-US&apos;).formatDate(date));

// Print date in UK format
console.log(Globalize(&apos;en-GB&apos;).formatDate(date));</code></pre><p>In the console, you&#x2019;ll get the following outputs:</p><pre><code>&quot;15/2/2020&quot;
&quot;02/15/2020&quot;
&quot;15/02/2020&quot;</code></pre><p>Now let&#x2019;s try formatting a number.</p><h3 id="formatting-the-number"><strong>Formatting the Number</strong></h3><p>To format numbers, first you have to add the CLDR data and supplemental data. Then you have to load the locale data. The steps are the same as before. Now, let&#x2019;s see the code to format the number.</p><pre><code>var number = 1452.67;

// Format in IT standard
console.log(Globalize(&apos;it&apos;).formatNumber(number));

// Format in US standard
console.log(Globalize(&apos;en-US&apos;).formatNumber(number));</code></pre><p>You&#x2019;ll get the following output:</p><pre><code>&quot;1.452,67&quot;
&quot;1,452.67&quot;</code></pre><h3 id="formatting-the-currency"><strong>Formatting the Currency</strong></h3><p>Now let&#x2019;s try something interesting. How about we format a currency?</p><p>After adding the required CLDR and local data, let&#x2019;s declare a number and format it in U.S. dollars (USD) and euros (EUR).</p><pre><code>var value = 456.342;
//Currency formatter for USD

var usdollarFormat = enGlobalize.currencyFormatter(&apos;USD&apos;);

console.log(usdFormatter(value));

//Currency formatter for EURO

var euroFormat = enGlobalize.currencyFormatter(&apos;EUR&apos;, {
  style: &apos;code&apos;,
  round: &apos;ceil&apos;
});

console.log(eurFormatter(value));</code></pre><p>You&#x2019;ll get the following output:</p><pre><code>$456.34
456.34 EUR</code></pre><h3 id="formatting-the-language"><strong>Formatting the Language</strong></h3><p>We saved the interesting stuff for last. Date-time and number formatting are simple. But you may wonder how to change the text content of your website. Well, Globalize JS helps you with that as well. But for that, you need to manually declare the translated data in a JSON file.</p><p>For example, let&#x2019;s suppose you need to translate your site&#x2019;s homepage. We can start with the register and cancel buttons. In the it.json file, write</p><pre><code>{
  &quot;it&quot;:
      { 
        &quot;register&quot;: &quot;Registrati&quot;,
        &quot;cancel&quot;:&quot;Annulla&quot;
      }
}</code></pre><p>Write the other texts and their translated versions in the JSON.</p><p>Now it&#x2019;s time to fetch the data from the JSON and pass them in order to globalize:</p><pre><code>const fetchHeaders = new Headers({&quot;accept&quot;: &quot;application/json&quot;, &apos;X-Requested-With&apos;: &apos;XMLHttpRequest&apos;});
var locale = it;
fetch(&quot;messages/&quot; + locale + &quot;.json&quot;, { headers: fetchHeaders }).then(response =&gt; {
return response.json();
})
   .then(
         json =&gt; {
                  Globalize.loadMessages(json);
                  Globalize.locale(locale);
                  yourRender();
                  }
         );</code></pre><p>Here, the locale is the variable where you declare the JSON filename. If the region is Italy (IT), the code wants the file to be present at messages/it.json. Similarly, if you have JSON for other countries, such as France (fr.json) or Japan (jp.json), the code will fetch data from the respective JSONs and translate the texts accordingly. For translating, you need the following code:</p><pre><code>function translateButtons() {
	//To translate the Register button
    document.getElementById( &quot;buttonRegister&quot; ).value = Globalize.formatMessage( &quot;register&quot; );
	//To translate the Cancel button
    document.getElementById( &quot;buttonCancel&quot; ).value = Globalize.formatMessage( &quot;cancel&quot; );
}</code></pre><p>Globalize JS also helps to format pluralization, relative time, units, and many other kinds of stuff. Select the module you like and start internationalizing your site.</p><p>Now that the code is ready, you may wonder how you can find the geolocation of the users accessing your site. In the next section, I&#x2019;ll introduce you to ipdata, an easy-to-use geolocation API.</p><h2 id="how-ipdata-can-help-with-internationalization"><strong>How ipdata Can Help With Internationalization</strong></h2><p>ipdata provides you with a geolocation API. Using that, you can find the location of any user with their IP address, both IPv4 and IPv6. Let&#x2019;s see how you can do that. Run the following JavaScript code:</p><pre><code>var request = new XMLHttpRequest();

//The GET call returns the location of the IP address of the request&#x2019;s origin
request.open(&apos;GET&apos;, &apos;https://api.ipdata.co/?api-key=test&apos;);
request.setRequestHeader(&apos;Accept&apos;, &apos;application/json&apos;);
//Function executes when readystate changes
request.onreadystatechange = function () {

  if (this.readyState === 4) {
    console.log(this.responseText);
  }
};
request.send();</code></pre><p>You need to ensure one thing&#x2014;that all calls are made using https. ipdata doesn&#x2019;t serve requests on unsecured http. You can use the ipdata API not only in JavaScript but also in C#, Java, Python, PHP, and many other languages. <a href="https://ipdata.co/registration.html">Sign up</a> to get a free API key and start testing.</p><p>The best feature of ipdata, apart from location, is that you can also use the API to detect the <a href="https://docs.ipdata.co/overview/mobile-carrier-detection">mobile carrier</a>, <a href="https://docs.ipdata.co/overview/currency-detection">currency</a>, and <a href="https://docs.ipdata.co/overview/timezone-detection">time zone</a>, along with <a href="https://docs.ipdata.co/overview/threat-data">proxies and threats</a>.</p><h2 id="expand-your-limits-with-internationalization"><strong>Expand Your Limits With Internationalization</strong></h2><p>While developing a website, remember the &#x201C;world&#x201D; in WWW. Your site is meant to be accessed by people from around the world. No matter what kind of site you&#x2019;re developing, be it e-commerce or a travelogue, target your audience. Find out the locale where your site is meant to be a hit. Start using internationalization in JavaScript and make your site readable by people from different countries with different languages.</p><p><strong><em>This post was written by Arnab Roy Chowdhury.</em><a href="https://www.linkedin.com/in/arnab-roy-chowdhury-a730693b/"><em> Arnab</em></a><em> is a UI developer by profession and a blogging enthusiast. He has strong expertise in the latest UI/UX trends, project methodologies, testing, and scripting.</em></strong></p>]]></content:encoded></item><item><title><![CDATA[Adding Location Data to ELK With GeoIP]]></title><description><![CDATA[<p>You already know that Elasticsearch excels at searching. The way they managed to simplify queries and information is amazing. While databases are good at storing data, search engines are good at &#x2026; well &#x2026; searching, of course. In our particular case, we won&#x2019;t be talking about searching, phrase</p>]]></description><link>https://ipdata.co/blog/logstash-geoip/</link><guid isPermaLink="false">624afb5a24d10526e99d8d61</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Thu, 21 May 2020 19:40:33 GMT</pubDate><content:encoded><![CDATA[<p>You already know that Elasticsearch excels at searching. The way they managed to simplify queries and information is amazing. While databases are good at storing data, search engines are good at &#x2026; well &#x2026; searching, of course. In our particular case, we won&#x2019;t be talking about searching, phrase matching, filtering, or retrieving data. This post is about how ELK&#x2019;s <a href="https://www.elastic.co/logstash">Logstash </a><strong>geoip-filter</strong> can read and parse geographic data out of IP addresses (GeoIP). I know, magic!</p><p>Later in the post, we&#x2019;ll discuss what to do when you don&#x2019;t have ELK available or when a quicker solution is needed. I&#x2019;m going to assume you already installed, configured, and ran Elasticsearch (and the rest of the ELK stack). If you haven&#x2019;t, you might want to pause here and come back when everything&#x2019;s ready.</p><p>So, let&#x2019;s get started!</p><h2 id="geoip-and-elk"><strong>GeoIP and ELK</strong></h2><p>As I mentioned before, Elasticsearch is an amazing tool. And it only gets better when paired with Kibana and Logstash. And Logstash is precisely what we&#x2019;re here to discuss. You probably know that Logstash is what sits between your data and your database for final archiving and analyzing. It can handle an increasing number of sources and feeds&#x2014;not just Elasticsearch, but also regular databases or email services. It&#x2019;s a very powerful tool.</p><p>Moreover, Logstash can parse, transform, and filter the data. One of those filters happens to be the <strong>geoip-filter</strong>. So a common use case is to analyze the access logs of a server; you can very easily ingest them (letting Logstash monitor the logs) or feed them directly to the application. The recommended way to start is to download and install the <strong>geoip-filter</strong>, as recommended by the official documentation.</p><p>sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-geoip</p><p>However, if you run this command on more recent versions, you may find an error as you run that command.</p><figure class="kg-card kg-image-card"><img src="https://lh3.googleusercontent.com/jkBo9zplUmJYkDYA96m8FazAkNSeq9Ryq3R7XZ5nG5rwNfuKb3UXW22waLzvNpaVRtWMDG9yFnCtaDG51dzmMHiAuESak4o-B7kboZSIdFQf0H2TqjNXvdfNNQwthNJF2e1SAJfh" class="kg-image" alt="logstash geoip" loading="lazy"></figure><p>That means that this plugin is actually part of the Elasticsearch distribution, as noted in the <a href="https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest-geoip.html">docs.</a></p><p>The way the GeoIP filter works is very simple: Elasticsearch ships with a database of IP addresses and their geographic information. So, when the tool parses the IP, it automatically maps it to a geopoint (latitude and longitude), which can later be mapped in the Visualize tab as follows.</p><figure class="kg-card kg-image-card"><img src="https://lh4.googleusercontent.com/Fi_-VEHQGHIM4wUiyhkhYR9FFqJl2KqKrVdSxlynN5LmGZD_Lsf8qPPW38cB-wxZbFbmKu3EQQi2bYaCps-Bi2itk1TeQdoSKHoLgY-m0H6QgkN29L9s-B63c8N9xHtMJjaE4RqS" class="kg-image" alt="logstash geoip" loading="lazy"></figure><p>Here, I&#x2019;m using the sample data that ships with Elasticsearch to map the unique visitors to our sample website, sorted by country. If you&#x2019;re curious enough, you can also jump in the Discover tab and click around your sample data; you&#x2019;ll find the geolocation field, noted by the world icon right next to it.</p><figure class="kg-card kg-image-card"><img src="https://lh5.googleusercontent.com/tYLKSYXtR_wevJ88yyZ7cyMkk827G9UxG3pMRkayFRoiZa1qVK5iJKAcj5gwo6TisTxuqsSAVqJ0ggRBT19q2CQ8B9FV39Y-zIVpj1g__FWi3L5RBWU3cHptoo0BL_Jeoiygj0ZV" class="kg-image" alt="logstash geoip" loading="lazy"></figure><p>The filter basically groups the latitude and longitude together, useful for pinpointing the markers. Easy!</p><h2 id="what-if-i-don-t-have-elk"><strong>What If I Don&#x2019;t Have ELK?</strong></h2><p>The objective remains the same: How can I get visualization or data based on the IP address? This is a feature that&#x2019;s normally used in websites, but nowadays, being able to obtain geoinformation is more and more important in a growing number of situations. You may serve <a href="https://ipdata.co/blog/blog/how-to-use-html5-geolocation/">API endpoints globally</a>, or perhaps you&#x2019;d like to filter the content shown on your app based on the location of your users. Another use case might come from your marketing department. They&#x2019;ll probably ask you to count and perform operations and visualizations on the sales funnel for different locations. Either way, you need GeoIP data.</p><p>But sometimes using ELK might be a bit of an overkill. In the cases above, setting up an ELK stack might be even more complicated than creating the functionality itself. That can be due to a number of things. Simplicity is the first one that comes to mind. There are a number of tools and services out there that can also serve GeoIP data&#x2014;normally using a database. Some of them might even be free. As a matter of fact, the Logstash default database for GeoIP data is free and publicly available from the <a href="https://dev.maxmind.com/geoip/geoip2/geolite2/">MaxMind</a> website (though previous registration is required).</p><p>Now that would require you to find the right database, download it, compile it (the relationships between the files might not work for you), and distribute it across your codebase. Make sure it has all the information you need (the available data changes on a per-database basis).</p><p>Did I mention those databases are normally different files? CSV formatted, too. I know, very 1995. Last but not least, you&#x2019;ve got to check periodically for changes and updates to the data. Once you have all of that, you need to add it to the infrastructure, whether that&#x2019;s on a separate service in your codebase or you do decide to keep the files. And we haven&#x2019;t even talked about accessing the data across different languages and environments.</p><p>What do you do?</p><h2 id="the-alternative"><strong>The Alternative</strong></h2><p>Well, you&#x2019;re in luck. There&#x2019;s a service called <a href="https://ipdata.co/">ipdata</a> that will get you up and running in no time. Let me show you how that works. Performing a simple request to their service (I&#x2019;ll discuss how to do that in a minute) returns a JSON object with useful information relevant to the geographical location you&#x2019;re requesting.</p><pre><code>{
    &quot;ip&quot;: &quot;81.20.41.113&quot;,
    &quot;is_eu&quot;: true,
    &quot;city&quot;: &quot;St Albans&quot;,
    &quot;region&quot;: &quot;England&quot;,
    &quot;region_code&quot;: &quot;ENG&quot;,
    &quot;country_name&quot;: &quot;United Kingdom&quot;,
    &quot;country_code&quot;: &quot;GB&quot;,
    &quot;continent_name&quot;: &quot;Europe&quot;,
    &quot;continent_code&quot;: &quot;EU&quot;,
    &quot;latitude&quot;: 51.7768,
    &quot;longitude&quot;: -0.2843,
    &quot;postal&quot;: &quot;AL4&quot;,
    &quot;calling_code&quot;: &quot;44&quot;,
    &quot;flag&quot;: &quot;https://ipdata.co/flags/gb.png&quot;,
    &quot;emoji_flag&quot;: &quot;\ud83c\uddec\ud83c\udde7&quot;,
    &quot;emoji_unicode&quot;: &quot;U+1F1EC U+1F1E7&quot;,
    &quot;asn&quot;: {
        &quot;asn&quot;: &quot;AS29033&quot;,
        &quot;name&quot;: &quot;Eckoh UK Limited&quot;,
        &quot;domain&quot;: &quot;eckoh.com&quot;,
        &quot;route&quot;: &quot;81.20.32.0/20&quot;,
        &quot;type&quot;: &quot;isp&quot;
    },
    &quot;languages&quot;: [
        {
            &quot;name&quot;: &quot;English&quot;,
            &quot;native&quot;: &quot;English&quot;
        }
    ],
    &quot;currency&quot;: {
        &quot;name&quot;: &quot;British Pound Sterling&quot;,
        &quot;code&quot;: &quot;GBP&quot;,
        &quot;symbol&quot;: &quot;\u00a3&quot;,
        &quot;native&quot;: &quot;\u00a3&quot;,
        &quot;plural&quot;: &quot;British pounds sterling&quot;
    },
    &quot;time_zone&quot;: {
        &quot;name&quot;: &quot;Europe/London&quot;,
        &quot;abbr&quot;: &quot;BST&quot;,
        &quot;offset&quot;: &quot;+0100&quot;,
        &quot;is_dst&quot;: true,
        &quot;current_time&quot;: &quot;2020-05-21T18:07:37.049274+01:00&quot;
    },
    &quot;threat&quot;: {
        &quot;is_tor&quot;: false,
        &quot;is_proxy&quot;: false,
        &quot;is_anonymous&quot;: false,
        &quot;is_known_attacker&quot;: false,
        &quot;is_known_abuser&quot;: false,
        &quot;is_threat&quot;: false,
        &quot;is_bogon&quot;: false
    }
}</code></pre><p>Isn&#x2019;t that amazing? The response times are equally as amazing as the information presented. The service runs globally on AWS, which allows them to respond in an average of 60 ms. That&#x2019;s without the use of cache, too. Signing up for their service is very fast. I requested a<a href="https://ipdata.co/sign-up.html">Free Tier API Key</a> and received a response very quickly to activate my account.</p><p>So let&#x2019;s jump ahead. At this point, I&#x2019;m sure you already have a number of use cases in mind. However, if you need ideas, the welcome email they send you includes a number of test calls. How&#x2019;s that for inspiration?</p><p>Oh, by the way, did I mention ipdata also handles IPv6? That may not seem like much. It may not even ring a bell, but believe me. It&#x2019;s a thing. You want your service to be able to handle both versions of the protocol.</p><h2 id="so-how-does-it-work"><strong>So How Does It Work?</strong></h2><p>Pretty easily. Here&#x2019;s a sample of a simple curl request. Use the following command:</p><p>curl https://api.ipdata.co/8.8.8.8?api-key=API_KEY</p><p>And very quickly, the results show up.</p><figure class="kg-card kg-image-card"><img src="https://lh5.googleusercontent.com/e8zprYBlpJkGBZ4VwrpoVAW4Re-qh7hElhxkF5K34RDnaJP52u-NyblRXbGVtk6bBgsTEE4FFjLUQ0jBJ6MXAyPMmqOHI_o0_0VWMea0QBi1zpMEikev752g6-fJ50JRTrntTZei" class="kg-image" alt="logstash geoip" loading="lazy"></figure><h2 id="wrapping-up"><strong>Wrapping Up</strong></h2><p>At its most basic functionality, ELK is a search engine, capable of transforming and displaying data much faster than a regular database manager can. The ELK stack is definitely a rising star in the development industry and not everyone can actually use all of its functionality. So, if you&#x2019;re here, it means that you&#x2019;re beginning to use it or perhaps more of a seasoned developer. Either way, you should feel lucky. After all, everyone can write <a href="https://en.wikipedia.org/wiki/MySQL">MySQL</a> &#x2026; right?</p><p>However, the more tools you have available as a developer, the more prepared you&#x2019;ll be when the next challenge arrives. Acquiring GeoIP information will always be a staple resource in commercial and corporate codebases. Not to mention how quickly it can spread to lower-scale environments due to the increased value of customizing content for your users. The possibilities are truly endless. So, if you&#x2019;re using centralized logging while visualizing global threats in a real-time dashboard, ELK is your friend.</p><p>For anything else that requires quick turnarounds and a solution that works on all modern programming languages and platforms, you require something different. Then ipdata has your back.</p><p><strong><em>This post was written by Guillermo Salazar. </em><a href="https://www.linkedin.com/in/gmosalazar/"><em>Guillermo </em></a><em>is a solutions architect with over 10 years of experience across a number of different industries. While his experience is based mostly in the web environment, he&apos;s recently started to expand his horizons to data science and cybersecurity.</em></strong><br></p>]]></content:encoded></item><item><title><![CDATA[How to look up Company Details by IP Address]]></title><description><![CDATA[<p>Knowing which company a prospective user is from can be invaluable. Use it to prioritise your leads, tailor your responses, or just monitor usage of your website. Many web analytics services offer some kind of organisation tracking data, but how do they do it, and how can we use it</p>]]></description><link>https://ipdata.co/blog/ip-to-company/</link><guid isPermaLink="false">624afb5a24d10526e99d8d60</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Mon, 18 May 2020 13:46:37 GMT</pubDate><content:encoded><![CDATA[<p>Knowing which company a prospective user is from can be invaluable. Use it to prioritise your leads, tailor your responses, or just monitor usage of your website. Many web analytics services offer some kind of organisation tracking data, but how do they do it, and how can we use it ourselves?</p><h2 id="what-are-asns">What are ASNs?</h2><p><a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">Autonomous System Numbers</a> are used by all services which look up company details by IP address. They identify the organisation or Internet Service Provider which controls a set of IP addresses. They&#x2019;re used in <a href="https://en.wikipedia.org/wiki/Border_Gateway_Protocol">BGP</a> to route information to the correct network. Unless you&#x2019;re a network administrator, you don&#x2019;t need to understand the specifics, but they&#x2019;re essential for looking up company details.</p><p>By keeping an up-to-date list of ASNs, it&#x2019;s possible to look up a specific IP address and establish which organisation owns it. In most cases, we aren&#x2019;t interested in situations where an ISP, such as Comcast or Virgin Media, owns the IP address, but the information can still be valuable to us. Maintaining this list is a lot of work, so most people opt to use a provider, like <a href="https://ipdata.co/">ipdata</a>, to look up ASN details, along with other IP address metadata, such as location.</p><h2 id="looking-up-company-details">Looking up company details</h2><p>ipdata provides a <a href="https://docs.ipdata.co/overview/asn-data">simple API</a> to look up ASN details. Let&#x2019;s try it out with a few examples:</p><p><strong>Microsoft Cloud hosting IP</strong></p><pre><code>curl &quot;https://api.ipdata.co/13.107.6.152/asn?api-key=test&quot;
{
    &quot;asn&quot;: &quot;AS8068&quot;,
    &quot;name&quot;: &quot;Microsoft Corporation&quot;,
    &quot;domain&quot;: &quot;microsoft.com&quot;,
    &quot;route&quot;: &quot;13.107.6.0/24&quot;,
    &quot;type&quot;: &quot;hosting&quot;
}
</code></pre><p><strong>AT&amp;T IP</strong></p><pre><code>curl &quot;https://api.ipdata.co/32.100.100.100/asn?api-key=test&quot;
{
    &quot;asn&quot;: &quot;AS7018&quot;,
    &quot;name&quot;: &quot;AT&amp;T Services, Inc.&quot;,
    &quot;domain&quot;: &quot;att.com&quot;,
    &quot;route&quot;: &quot;32.96.0.0/13&quot;,
    &quot;type&quot;: &quot;isp&quot;
}
</code></pre><p><strong>UK Government (House of Commons) IP</strong></p><pre><code>curl &quot;https://api.ipdata.co/194.60.38.10/asn?api-key=test&quot;
{
    &quot;asn&quot;: &quot;AS29214&quot;,
    &quot;name&quot;: &quot;THE HOUSE OF COMMONS&quot;,
    &quot;domain&quot;: &quot;parliament.uk&quot;,
    &quot;route&quot;: &quot;194.60.38.0/24&quot;,
    &quot;type&quot;: &quot;isp&quot;
}</code></pre><p>As you can see, each IP returns the ASN associated with the IP, along with additional data about the owner. We&#x2019;re able to know when our website is being used by UK Parliament or many other organisations, businesses, or governments.</p><h2 id="asn-type">ASN Type</h2><p>A unique feature of ipdata is the ASN type. We attempt to match each ASN to the type of entity which owns it. The possible types are as follows:</p><p>TypeDescriptionhostingBelonging to a datacenter (we have mapped 200M+ hosting IP addresses)ispBelonging to ISP IP space (we have mapped 1B+ ISP IPs)eduEducational institutionsgovOne of ~25,000 government agencies worldwidemilMilitary organizationsbusinessEnd-user organizations</p><p>We&#x2019;re mostly interested in the <code>business</code> type for lead prioritisation. However, due to the large number of ASNs, there will always be small corrections or mistakes, meaning the type may be wrong for some IPs. In the examples above, the House of Commons IP should likely be mapped to <code>gov</code>, but is reported as an <code>isp</code>, as that&#x2019;s how it&#x2019;s represented in public records. For this reason, the type should be used as an indicator, rather than a fact.</p><p>The <code>hosting</code> IPs are accurate and include most cloud hosting providers. This can be useful if you want to block crawlers, scrapers and bots from hosting providers. The first example shows the results for a request from one of Microsoft&#x2019;s backend services.</p><h2 id="enriching-a-contact-us-form-with-company-details">Enriching a &#x201C;Contact Us&#x201D; form with company details</h2><p>Let&#x2019;s build a quick practical example. We have a &#x201C;Contact Us&#x201D; form on our website, allowing potential customers to get in touch. We want to include the company details of the user contacting us, without asking them to fill in additional fields.</p><pre><code>&lt;form action=&quot;/contact&quot; method=&quot;POST&quot;&gt;
  &lt;input name=&quot;email&quot; type=&quot;email&quot; placeholder=&quot;Email address&quot; /&gt;
  &lt;textarea name=&quot;message&quot;&gt;Your message here...&lt;/textarea&gt;
  &lt;input type=&quot;submit&quot; /&gt;
&lt;/form&gt;
</code></pre><p>Here&#x2019;s a Node.js/Express application to handle serving the contact form, and handle contact messages, emailing our <em><a>contact@dogtreats.com</a></em> email with the details.</p><pre><code>const express = require(&quot;express&quot;);
const app = express();
const axios = require(&quot;axios&quot;);
const nodemailer = require(&quot;nodemailer&quot;);
const bodyParser = require(&quot;body-parser&quot;);
const email = nodemailer.createTransport({
  // https://nodemailer.com
  sendmail: true,
});

// Get an ipdata API Key from here: https://ipdata.co/sign-up.html
const IPDATA_API_KEY = &quot;test&quot;;
const getIpData = async (ip) =&gt; {
  ip = &quot;81.107.235.77&quot;;
  const response = await axios.get(
    `https://api.ipdata.co/${ip}?api-key=${IPDATA_API_KEY}`
  );
  return response.data;
};

// Serve the contact page
app.get(&quot;/contact&quot;, (req, res) =&gt;
  res.sendFile(&quot;./contact.html&quot;, { root: __dirname })
);

// Handle a contact request
app.post(&quot;/contact&quot;, bodyParser.urlencoded(), async (req, res) =&gt; {
  const ip = req.connection.remoteAddress;
  const ipdata = await getIpData(ip);
  const message = `From: ${req.body.email}
Message:
${req.body.message}
Company details:
Name: ${ipdata.asn.name}
Domain: ${ipdata.asn.domain}
Type: ${ipdata.asn.type}
`;
  await email.sendMail({
    from: req.body.email,
    to: &quot;contact@dogtreats.com&quot;,
    subject: &quot;Contact Us Message&quot;,
    text: message,
  });
  res.status(200).send(&quot;Thanks for your message!&quot;);
});

app.listen(8000);
</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_BD5EB238627D214871110BEE86308A913CA0D9E2CC0378B9DA261EDC219E8AEE_1589476572479_contact-us.gif" class="kg-image" alt="Simple contact us functionality for the user" loading="lazy"></figure><p>The email sent to <em><a>contact@dogtreats.com</a></em> looks like this:</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_BD5EB238627D214871110BEE86308A913CA0D9E2CC0378B9DA261EDC219E8AEE_1589476827610_Screenshot+2020-05-14+at+18.19.31.png" class="kg-image" alt loading="lazy"></figure><p>Of course, this could all use some styling to make it more attractive! However, the functionality is all there and in this example, we&#x2019;re able to see when someone from within the House of Commons is asking for some dog treats.</p><h2 id="conclusions">Conclusions</h2><p>With a very small amount of code, we&#x2019;ve been able to enrich our Contact Us form with company details with just an IP address. Using the <a href="https://ipdata.co">ipdata</a> API, we can also include other useful information, such as their location, language and currency.</p><p>The uses of company details go beyond a simple contact us form, allowing advanced lead prioritisation and much more. Let us know how you&#x2019;re using it in the comments below!</p>]]></content:encoded></item><item><title><![CDATA[How to block website visitors from a specific country]]></title><description><![CDATA[<p>Let&#x2019;s imagine we&#x2019;ve just launched our new e-commerce store, dogtreats.com. We&#x2019;ve noticed that we&#x2019;re already getting a lot of requests from locations all around the world, including some that can&#x2019;t even buy the product. Even worse, there are a</p>]]></description><link>https://ipdata.co/blog/how-to-block-website-visitors-from-a-specific-country/</link><guid isPermaLink="false">624afb5a24d10526e99d8d5e</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Tue, 31 Mar 2020 16:03:27 GMT</pubDate><content:encoded><![CDATA[<p>Let&#x2019;s imagine we&#x2019;ve just launched our new e-commerce store, dogtreats.com. We&#x2019;ve noticed that we&#x2019;re already getting a lot of requests from locations all around the world, including some that can&#x2019;t even buy the product. Even worse, there are a lot of failed login requests, which could indicate an attacker trying to brute-force access to accounts.<br>In this scenario, dogtreats.com only ships products to the EU, so we&#x2019;ve made the decision that we can block requests from any other country, but how can we do that?<br></p><h2 id="use-a-cloud-managed-service">Use a cloud-managed service</h2><p>As we&#x2019;re concerned about security, we should offload as much of the blocking as possible onto a third party or managed service. This is important as unless you&#x2019;re an expert in responding to DDoS attacks, handling it yourself could result in very high costs and user-impact.<br>Ideally, we&#x2019;ll block requests using the first resource which requests will hit. If we allow potentially malicious requests to pass through multiple resources, or even hit our application servers, our service will be far more vulnerable to attacks, such as <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack" rel="noreferrer nofollow noopener">Denial-of-Service</a>.<br>The first resource is usually a CDN, so let&#x2019;s look at how we can block requests using 4 leading CDNs - CloudFront, CloudFlare, Fastly, and Akamai. All major CDN providers should have some kind of in-built geo blocking functionality &#x2013; if yours is missing it then it&#x2019;d be worth considering an alternative.</p><p><strong>AWS CloudFront</strong></p><p>With CloudFront, there&#x2019;s a <em>geo restriction </em>feature which can be used to whitelist or blacklist certain countries. It&#x2019;s quite basic, only allowing us to block or allow a list of country codes.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_E3410FF0FE5299229F809A8CF5ED48EAD716772C7EB1E8BD9E7D6633000FD46C_1585421681989_Mar-02-2020+18-45-00.gif" class="kg-image" alt loading="lazy"></figure><p><strong>CloudFlare</strong></p><p>CloudFlare has an <a href="https://www.cloudflare.com/waf/" rel="noreferrer nofollow noopener">advanced Firewall</a> feature built-in. It&#x2019;s able to block or allow requests based on multiple different parameters, such as request path, request method, IP address, IP location, and many more.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_E3410FF0FE5299229F809A8CF5ED48EAD716772C7EB1E8BD9E7D6633000FD46C_1585326089383_Mar-02-2020+18-56-16.gif" class="kg-image" alt loading="lazy"></figure><p><br><strong>Fastly </strong>allows us to <a href="https://www.fastly.com/blog/restricting-access-content" rel="noreferrer nofollow noopener">restrict access using Varnish configuration</a>. It provides a large number of <a href="https://docs.fastly.com/vcl/geolocation/" rel="noreferrer nofollow noopener">geolocation variables</a>, allowing more than just blocking based on country code. To block all non-EU requests, it&#x2019;s just a few lines of code:</p><pre><code>
if ( geoip.continent_code != &quot;EU&quot; ) {
  error 403 &quot;Forbidden&quot;;
}
</code></pre><p><br><strong>Akamai</strong> offers a <a href="https://learn.akamai.com/en-us/webhelp/adaptive-media-delivery/adaptive-media-delivery-implementation-guide/GUID-74487456-5D25-413B-ABD5-1C0DC510E4D1.html" rel="noreferrer nofollow noopener">similar solution</a> to CloudFront, named Geo Protection, which also supports regional blocking and US Designated Market Areas.<br></p><h2 id="web-application-firewalls">Web Application Firewalls</h2><p>A Web Application Firewall (WAF) monitors and blocks web traffic according to rules. CloudFlare&#x2019;s Firewall is actually a fully-featured Web Application Firewall (WAF), and most hosting providers offer WAF functionality as a core part of their security features. A common use of WAF is to automatically block many common web exploits, such as <a href="https://en.wikipedia.org/wiki/Cross-site_scripting" rel="noreferrer nofollow noopener">XSS</a> or <a href="https://en.wikipedia.org/wiki/SQL_injection" rel="noreferrer nofollow noopener">SQLi</a>, so it&#x2019;s often useful regardless of any geo restrictions. <br>Most WAF solutions can be used with more than just CDNs &#x2013; for example, <a href="https://aws.amazon.com/waf/" rel="noreferrer nofollow noopener">AWS&#x2019; WAF</a> can be attached to CloudFront distributions, Application Load Balancers and API Gateways &#x2013; making them far more versatile than the in-built CDN geo blocking. However, they usually cost more than the in-built solutions and they&#x2019;re more complex to set up.<br>To set up AWS WAF, first head to the &#x201C;WAF &amp; Shield&#x201D; console, then click &#x201C;Create web ACL&#x201D;. You&#x2019;ll then see a wizard which allows you to create a web ACL, associate it with your resources and configure rules.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_E3410FF0FE5299229F809A8CF5ED48EAD716772C7EB1E8BD9E7D6633000FD46C_1585421745888_Mar-27-2020+18-58-48.gif" class="kg-image" alt loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_E3410FF0FE5299229F809A8CF5ED48EAD716772C7EB1E8BD9E7D6633000FD46C_1585421763915_Mar-27-2020+18-45-03.gif" class="kg-image" alt loading="lazy"></figure><p><br>If you&#x2019;re using Google Cloud, Google Cloud Shield now supports <a href="https://cloud.google.com/blog/products/identity-security/understanding-google-cloud-armors-new-waf-capabilities" rel="noreferrer nofollow noopener">WAF with geo blocking support</a>. For Azure users, Azure WAF has <a href="https://docs.microsoft.com/en-us/azure/web-application-firewall/ag/geomatch-custom-rules" rel="noreferrer nofollow noopener">geomatch rules</a>, currently available in preview. But what can we do if we aren&#x2019;t using a major cloud provider?<br></p><h2 id="nginx">NGINX</h2><p>NGINX is a very popular load balancer with both a free and a premium (named Plus) offering. Unfortunately, only NGINX Plus supports the <a href="https://docs.nginx.com/nginx/admin-guide/dynamic-modules/geoip/" rel="noreferrer nofollow noopener">GeoIP module</a> to allow geolocation-based blocking. It&#x2019;s reasonably complex to set up, but the NGINX docs have <a href="https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-by-geoip/" rel="noreferrer nofollow noopener">a great guide</a>.<br></p><h2 id="modsecurity">ModSecurity</h2><p>ModSecurity is an open-source Web Application Firewall. It was originally a module for use with the Apache HTTP server, but it can now be integrated with NGINX, IIS, and many other solutions. It&#x2019;s often available when using cPanel&#x2019;s WebHost Manager (WHM) so, for some services, it&#x2019;s the only feasible method of blocking traffic. There are two major versions of ModSecurity. <br><strong>v2</strong> entered support-mode and hasn&#x2019;t been released since 2018. It only supports geolocation blocking using the MaxMind legacy databases. GeoLite Legacy has <a href="https://support.maxmind.com/geolite-legacy-discontinuation-notice/" rel="noreferrer nofollow noopener">been discontinued</a> and is not available for download, and GeoIP legacy is <a href="https://dev.maxmind.com/geoip/legacy/" rel="noreferrer nofollow noopener">no longer available</a> to new customers. Unfortunately, v2 is still commonly bundled with WHM, rendering it effectively useless for geo blocking.<br><strong>v3</strong> now supports the GeoIP2 and GeoLite2 databases, allowing us to write a simple rule to block all traffic which doesn&#x2019;t originate from the EU:</p><pre><code>
SecGeoLookupDb /path/to/geoip/geolite2-country.mmdb
SecRule REMOTE_ADDR &quot;@geoLookup&quot; &quot;phase:1,t:none,pass,nolog&quot;
SecRule GEO:COUNTRY_CONTINENT &quot;!@streq EU&quot; &quot;phase:1,t:none,log,deny,msg:&apos;Blocking non-EU IP address&apos;&quot;</code></pre><h2 id="application">Application</h2><p>If security isn&#x2019;t the main reason for blocking traffic to your site, or you just want to provide additional location-based functionality, you may wish to do a location lookup within your application code. This could be done using the GeoIP databases mentioned above or a managed service, like <a href="https://ipdata.co/" rel="noreferrer nofollow noopener">ipdata</a>. An Express middleware to only allow EU traffic would look as follows:</p><pre><code>
app.use(async (req, res, next) =&gt; {
  const response = await fetch(&apos;https://api.ipdata.co?api-key=test&apos;);
  const ipdata = await response.json();
  if (ipdata.continent_code !== &apos;EU&apos;) {
    res.status(403).send(&apos;Only users from the EU are allowed&apos;);
    return;
  }
  // set ipdata on the request object so we can automatically tailor
  // user preferences such as currency
  req.ipdata = ipdata;
  next()
})</code></pre><p><br></p><h2 id="conclusions">Conclusions</h2><p>If you&#x2019;re using a popular CDN or cloud provider, one of their in-built solutions will allow blocking website visitors from specific countries. Choosing one of these in-built solutions will be far safer than using a self-hosted or self-managed option, but we&#x2019;ve also covered geo blocking for NGINX and ModSecurity, as well as within our application code itself. Having geographic information within your application can help to provide advanced features, such as automatically selecting the correct location or for calculating a delivery estimate.</p>]]></content:encoded></item><item><title><![CDATA[How to use HTML5 Geolocation]]></title><description><![CDATA[<p>Many websites and applications depend on the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API" rel="noreferrer nofollow noopener">HTML5 Geolocation API</a>, which provides the location information of the user, in the form of latitude and longitude. It&#x2019;s often used alongside a map, providing features such as a local store finder. In this guide, we&#x2019;ll start with a</p>]]></description><link>https://ipdata.co/blog/how-to-use-html5-geolocation/</link><guid isPermaLink="false">624afb5a24d10526e99d8d5d</guid><category><![CDATA[Writing]]></category><dc:creator><![CDATA[The ipdata Team]]></dc:creator><pubDate>Thu, 30 Jan 2020 14:38:54 GMT</pubDate><media:content url="https://ipdata.co/blog/blog/content/images/2020/02/Redirecting-user--1-.svg" medium="image"/><content:encoded><![CDATA[<img src="https://ipdata.co/blog/blog/content/images/2020/02/Redirecting-user--1-.svg" alt="How to use HTML5 Geolocation"><p>Many websites and applications depend on the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API" rel="noreferrer nofollow noopener">HTML5 Geolocation API</a>, which provides the location information of the user, in the form of latitude and longitude. It&#x2019;s often used alongside a map, providing features such as a local store finder. In this guide, we&#x2019;ll start with a simple implementation, then extend it to support advanced functionality with a great user experience.</p><h1 id="finding-a-user-s-position-in-javascript">Finding a user&#x2019;s position in Javascript</h1><p><br>The HTML5 geolocation API has only one object &#x2013; the <strong>navigator</strong>.<strong>geolocation</strong> object and exposes 3 methods getCurrentPosition(), watchPosition(), and<strong> </strong>clearWatch().<br>Let&#x2019;s start with a simple implementation that will display the user&#x2019;s coordinates on a webpage.</p><pre><code>
&lt;p&gt;Locating you...&lt;/p&gt;

&lt;script&gt;
  const p = document.querySelector(&apos;p&apos;);

  function onError() {
    p.textContent = &apos;Unable to locate you&apos;
  }
  function onSuccess(position) {
    p.textContent = `Latitude: ${position.coords.latitude} &#xB0;, Longitude: ${position.coords.longitude} &#xB0;`;
  }

  if (!navigator.geolocation) {
    // This browser doesn&apos;t support Geolocation, show an error
    onError();
  } else {
    // Get the current position of the user!
    navigator.geolocation.getCurrentPosition(onSuccess, onError);
  }
&lt;/script&gt;</code></pre><figure class="kg-card kg-image-card kg-width-full"><img src="https://paper-attachments.dropbox.com/s_60BF5DA5A068F1660621A4047D6E7A44031DA67CCE9F05E88BDA28315BE6D923_1579883979576_Jan-24-2020+16-39-16.gif" class="kg-image" alt="How to use HTML5 Geolocation" loading="lazy"></figure><p>To make this more visually appealing, let&#x2019;s update our script to plot the location on a map, using Google Maps.</p><pre><code>
&lt;div id=&quot;map&quot; style=&quot;height: 400px; width: 700px&quot;&gt;&lt;/div&gt;

&lt;script&gt;
  const map = document.querySelector(&quot;#map&quot;);

  function initMap() {
    const googleMap = new google.maps.Map(map, {
      center: { lat: 0, lng: 0 },
      zoom: 1
    });

    function onError() {
      map.textContent = &quot;Unable to locate you&quot;;
    }
    function onSuccess(geo) {
      const position = {
        lat: geo.coords.latitude,
        lng: geo.coords.longitude
      };
      // Reposition the map to the detected location
      googleMap.setCenter(position);
      googleMap.setZoom(12);
      // Add a marker
      new google.maps.Marker({ position, map: googleMap });
    }

    if (!navigator.geolocation) {
      onError();
    } else {
      navigator.geolocation.getCurrentPosition(onSuccess, onError);
    }
  }
&lt;/script&gt;
&lt;script
  async
  defer
  src=&quot;https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&amp;callback=initMap&quot;
&gt;&lt;/script&gt;</code></pre><p><br>Now, we have a map which shows the user where they&#x2019;re located.</p><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_60BF5DA5A068F1660621A4047D6E7A44031DA67CCE9F05E88BDA28315BE6D923_1579887294988_Jan-24-2020+17-34-08.gif" class="kg-image" alt="How to use HTML5 Geolocation" loading="lazy"></figure><h1 id="watching-for-updates">Watching for updates</h1><p>A users location can easily change whilst they&#x2019;re on the page, for example they could be travelling. With our current implementation, the user would need to refresh to update, but the Geolocation API provides a watchPosition function for exactly this situation. Simply change the function to </p><pre><code>navigator.geolocation.watchPosition(onSuccess, onError);</code></pre><h1 id="checking-browser-support">Checking Browser Support</h1><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_695C537CFBD20E00E6583675FC8F334E1B7ED548A9949B0FB0118A39E38125BF_1580390720185_Screenshot+from+2020-01-30+16-24-08.png" class="kg-image" alt="How to use HTML5 Geolocation" loading="lazy"></figure><p>Check whether HTML5 Geolocation is supported;</p><pre><code>if (navigator.geolocation) {
  console.log(&apos;Geolocation is supported!&apos;);
}
else {
  console.log(&apos;Geolocation is not supported for this Browser/OS.&apos;);
}</code></pre><h1 id="reverse-geocoding-more-than-just-coordinates">Reverse geocoding &#x2013; more than just coordinates</h1><p>There are many use-cases when it&#x2019;s useful to have the user&#x2019;s address, rather than just their geoposition. There are a few options, such as the <a href="https://developers.google.com/maps/documentation/geocoding/start" rel="noreferrer nofollow noopener">Google Geocoding API</a> or <a href="https://geocode.xyz/" rel="noreferrer nofollow noopener">Geocode.xyz</a>. For this example, we&#x2019;ve chosen Geocode.xyz. We&#x2019;re going to move away from the map example and use reverse geocoding to auto-fill some address details. This is commonly used by navigation services, such as Citymapper.</p><pre><code>
&lt;pre&gt;Locating you...&lt;/pre&gt;

&lt;script&gt;
  const address = document.querySelector(&quot;pre&quot;);

  function onError() {
    address.textContent = &quot;Unable to locate you&quot;;
  }
  async function onSuccess(position) {
    const geocode = await fetch(`https://geocode.xyz/${position.coords.latitude},${position.coords.longitude}?json=1`);
    const geoResponse = await geocode.json();

    address.textContent = `${geoResponse.stnumber} ${geoResponse.staddress}
${geoResponse.city}
${geoResponse.postal}
${geoResponse.country}`;
  }

  if (!navigator.geolocation) {
    onError();
  } else {
    navigator.geolocation.getCurrentPosition(onSuccess, onError);
  }
&lt;/script&gt;</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_60BF5DA5A068F1660621A4047D6E7A44031DA67CCE9F05E88BDA28315BE6D923_1579975490223_Jan-25-2020+18-03-01.gif" class="kg-image" alt="How to use HTML5 Geolocation" loading="lazy"></figure><p>Reverse geocoding can be exceptionally accurate &#x2013; it&#x2019;s often able to get the exact street address. Due to this, it&#x2019;d be very useful for ecommerce stores or any website which requires the location of the user for shipping.</p><h1 id="failure-cases">Failure cases</h1><p>Many users will not allow websites to access their current location on page load &#x2013; they&#x2019;ve been given no indication as to <em>why</em> you&#x2019;re requesting it. Therefore, it&#x2019;s best to delay requesting geolocation until it&#x2019;s required, or when a user explicitly asks for that functionality by clicking a &#x201C;Use my current location&#x201D; button.<br>Even with these improvements, some users will always reject usage of their location. Additionally, some older browsers <a href="https://caniuse.com/#feat=geolocation" rel="noreferrer nofollow noopener">don&#x2019;t support the Geolocation API</a> at all, so what can we do to improve their experience? <a href="https://ipdata.co/" rel="noreferrer nofollow noopener">ipdata.co</a> provides an API to lookup a user by their IP address, so we can fall back to a slightly less accurate location for these users.</p><pre><code>
    &lt;p&gt;Locating you...&lt;/p&gt;
    
    &lt;script&gt;
      const p = document.querySelector(&quot;p&quot;);
    
      async function onError() {
        // Fall back to ipdata.co response
        const ipdataResponse = await fetch(&quot;https://api.ipdata.co?api-key=test&quot;);
        const ipdata = await ipdataResponse.json();
    
        // Call our onSuccess handler with the location
        onSuccess({
          coords: {
            latitude: ipdata.latitude,
            longitude: ipdata.longitude
          }
        });
      }
      function onSuccess(position) {
        p.textContent = `Latitude: ${position.coords.latitude}, Longitude: ${position.coords.longitude}`;
      }
    
      if (!navigator.geolocation) {
        // This browser doesn&apos;t support Geolocation, show an error
        onError();
      } else {
        // Get the current position of the user!
        navigator.geolocation.getCurrentPosition(onSuccess, onError);
      }
    &lt;/script&gt;
</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_60BF5DA5A068F1660621A4047D6E7A44031DA67CCE9F05E88BDA28315BE6D923_1579977063174_Jan-25-2020+18-30-41.gif" class="kg-image" alt="How to use HTML5 Geolocation" loading="lazy"></figure><p><br>This solution works very well, and gives all of our users get a good experience, regardless of whether they allow location.<br>The HTML5 Geolocation API returns 3 error codes according to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError/code" rel="noreferrer nofollow noopener">Documentation</a>;<strong>ValueAssociated constantDescription</strong>1PERMISSION_DENIEDThe acquisition of the geolocation information failed because the page didn&apos;t have the permission to do it.2POSITION_UNAVAILABLEThe acquisition of the geolocation failed because one or several internal sources of position returned an internal error.3TIMEOUTThe time allowed to acquire the geolocation, defined by <a href="https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/timeout" rel="noreferrer nofollow noopener">PositionOptions.timeout</a> information that was reached before the information was obtained.</p><h2 id="progressive-enhancement">Progressive Enhancement</h2><p>We can take this solution one step further. Instead of waiting for the user to accept or deny location access, we can start with their ipdata location and enhance it with their exact geoposition afterwards. With this, we can build a map which shows a rough location and allow users the option to improve it.</p><pre><code>
&lt;div id=&quot;map&quot; style=&quot;height: 400px; width: 700px&quot;&gt;&lt;/div&gt;
&lt;button id=&quot;enhance&quot;&gt;Improve location accuracy&lt;/button&gt;

&lt;script&gt;
  const map = document.querySelector(&quot;#map&quot;);

  async function initMap() {
    const googleMap = new google.maps.Map(map, {
      center: { lat: 0, lng: 0 },
      zoom: 1
    });

    function onSuccess(geo) {
      const position = {
        lat: geo.coords.latitude,
        lng: geo.coords.longitude
      };
      // Reposition the map to the detected location
      googleMap.setCenter(position);
      googleMap.setZoom(12);
      // Add a marker
      new google.maps.Marker({ position, map: googleMap });
    }

    // Fetch ipdata location and update the map
    const ipdataResponse = await fetch(&quot;https://api.ipdata.co?api-key=test&quot;);
    const ipdata = await ipdataResponse.json();
    onSuccess({
      coords: {
        latitude: ipdata.latitude,
        longitude: ipdata.longitude
      }
    });

    // Listen for clicks on the &quot;Enhance&quot; button and use Geolocation API when clicked
    document.querySelector(&quot;#enhance&quot;).addEventListener(&quot;click&quot;, () =&gt; {
      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(onSuccess);
      }
    });
  }
&lt;/script&gt;
&lt;script
  async
  defer
  src=&quot;https://maps.googleapis.com/maps/api/js?key=AIzaSyCKGRx32IU_tJgtGea2En9kwLsAOtCmE2I&amp;callback=initMap&quot;
&gt;&lt;/script&gt;</code></pre><figure class="kg-card kg-image-card"><img src="https://paper-attachments.dropbox.com/s_60BF5DA5A068F1660621A4047D6E7A44031DA67CCE9F05E88BDA28315BE6D923_1580382913208_Jan-25-2020+18-52-05.gif" class="kg-image" alt="How to use HTML5 Geolocation" loading="lazy"></figure><h1 id="conclusions">Conclusions</h1><p>The Geolocation API is extremely powerful and the possibilities for exciting features are limitless, but it&#x2019;s also very intrusive to users as they need to explicitly allow access. The first question, whenever thinking about using geolocation, should be &#x201C;Do I really need to?&#x201D;. If you&#x2019;re able to use a non-intrusive method, such as using ipdata, then users will often have a better experience. If you need to use a more exact location, then giving users more control of their privacy is also preferable &#x2013; communicate why you need access and let them opt-in to using the Geolocation API.</p><h2 id="resources">Resources</h2><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://developers.google.com/web/fundamentals/native-hardware/user-location/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">User Location | Web Fundamentals | Google Developers</div><div class="kg-bookmark-description">Most browsers and devices have access to the user&amp;#39;s geographic location. Learn how to work with the user&amp;#39;s location in your site and apps.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.gstatic.com/devrel-devsite/prod/vcd1bbe5dda31d2b800805cc4c730b0229f847f2d108be33386b6e78644e79178/developers/images/touchicon-180.png" alt="How to use HTML5 Geolocation"><span class="kg-bookmark-author">Google Developers</span><span class="kg-bookmark-publisher">Paul Kinlan</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://developers.google.com/web/images/social-webfu-16x9.png" alt="How to use HTML5 Geolocation"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Geolocation API</div><div class="kg-bookmark-description">The Geolocation API allows the user to provide their location to web applications if they so desire. For privacy reasons, the user is asked for permission to report location information.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://developer.mozilla.org/static/img/favicon144.e7e21ca263ca.png" alt="How to use HTML5 Geolocation"><span class="kg-bookmark-author">MDN Web Docs</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://developer.mozilla.org/static/img/opengraph-logo.72382e605ce3.png" alt="How to use HTML5 Geolocation"></div></a></figure>]]></content:encoded></item></channel></rss>