<?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"><channel><title><![CDATA[Furqan Ashraf]]></title><description><![CDATA[Hands-on guides on APIs, DNS, and web security by Furqan Ashraf. Complex topics broken into clear, testable steps you can try yourself.]]></description><link>https://furqanashraf.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/68a30da5ed6e911eb30f473b/f690861e-3b58-4e7e-b634-f4e4044a393e.png</url><title>Furqan Ashraf</title><link>https://furqanashraf.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 01 Nov 2026 07:20:52 GMT</lastBuildDate><atom:link href="https://furqanashraf.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Your Browser Won't Catch Every Fake Domain. Here's How to Check One Yourself]]></title><description><![CDATA[Chrome 154 can miss lookalike domains. Check any suspicious domain with Punycode, RDAP and DNS, using free commands and a script you can run in 5 min.]]></description><link>https://furqanashraf.hashnode.dev/how-to-check-fake-lookalike-domain</link><guid isPermaLink="true">https://furqanashraf.hashnode.dev/how-to-check-fake-lookalike-domain</guid><category><![CDATA[Security]]></category><category><![CDATA[dns]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[phishing]]></category><category><![CDATA[cybersecurity]]></category><dc:creator><![CDATA[Furqan Ashraf]]></dc:creator><pubDate>Wed, 28 Oct 2026 07:31:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68a30da5ed6e911eb30f473b/41ee494d-1f8f-4130-8290-f52254d69298.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Earlier this month, two researchers showed that a single character can get a fake domain past Chrome's lookalike defenses.</p>
<p>Ian Muscat and Leanne Briffa of Have I Been Squatted registered 20 test domains, including <code>аррӏө.com</code> and <code>oƙta.com</code>. In Chrome 154 they display in the address bar as Unicode, not as the <code>xn--</code> form that usually gives a fake away. Gmail's web app showed all 20 as normal-looking text too (<a href="https://www.theregister.com/a/5302383">The Register</a>, Oct 10, 2026).</p>
<p>Those domains were built for research, not attacks. But phishing isn't slowing down: the APWG counted 971,181 phishing attacks in Q1 2026, up 13.8% from the quarter before (<a href="https://docs.apwg.org/reports/apwg_trends_report_q1_2026.pdf">APWG</a>).</p>
<p>So instead of trusting what the address bar shows, here's how to check a suspicious domain yourself in a few minutes. Every command below was run while writing this post, and you can run them too.</p>
<h2>Why the browser misses it</h2>
<p>Browsers show Unicode domain names as text only when they look safe. Chromium decides this with a list of characters it trusts and a check against popular site names.</p>
<p>The researchers found gaps in both. The Cyrillic <code>ө</code> isn't on the list, so one of them breaks the whole-script check. A letter like <code>ƙ</code> (k with a hook) survives the comparison, so <code>oƙta</code> doesn't match <code>okta</code>. Chrome's Safety Tips warning also skips names under five characters or with two or more changes.</p>
<p>A valid HTTPS padlock doesn't help either. It proves the connection is encrypted to that domain, not that the domain belongs to the brand.</p>
<img src="https://cdn.hashnode.com/uploads/covers/68a30da5ed6e911eb30f473b/3294d638-7bfe-4748-9ef5-c3a0f34b802d.png" alt="Comparison of what the address bar shows versus the real Punycode name: &quot;аррӏө.com&quot; is &quot;xn--80a6aa68c8d.com&quot; (all Cyrillic letters) and &quot;oƙta.com&quot; is &quot;xn--ota-f6a.com&quot; (Latin k with a hook). A padlock means encrypted, not genuine." style="display:block;margin:0 auto" />

<h2>Step 1: Reveal the real name</h2>
<p>Every domain with non-ASCII characters has an ASCII form called Punycode. It always starts with <code>xn--</code>. Converting the name is the fastest test.</p>
<p>In a browser console or Node.js:</p>
<pre><code class="language-javascript">new URL('https://oƙta.com').hostname
// 'xn--ota-f6a.com'
</code></pre>
<p>In Python (after <code>pip install idna</code>):</p>
<pre><code class="language-python">import idna
print(idna.encode('аррӏө.com').decode())
# xn--80a6aa68c8d.com
</code></pre>
<p>If the real <code>okta.com</code> comes back unchanged and the link you got comes back as <code>xn--...</code>, they are different domains. <code>аррӏө</code> is made entirely of Cyrillic letters: a, er, er, palochka and barred o.</p>
<h2>Step 2: Check the domain's age with RDAP</h2>
<p>Phishing domains are often freshly registered. In Q1 2026 alone, the APWG tracked more than 500 new domains tied to one "tariff refund" scam. RDAP is the replacement for WHOIS, and it returns clean JSON. ICANN completed the move to RDAP for generic domains in January 2025, and some registrars have already switched off the old port 43 WHOIS service.</p>
<p><code>rdap.org</code> forwards your request to the right registry, so one URL works for most extensions:</p>
<pre><code class="language-bash">curl -sL https://rdap.org/domain/xn--ota-f6a.com \
  | jq '.events[] | select(.eventAction=="registration") | .eventDate'
# "2026-09-27T12:48:44Z"
</code></pre>
<p>The real <code>okta.com</code> was registered on June 11, 2004. A login page for a company that old shouldn't live on a domain that is one month old.</p>
<p>To see the registrar too:</p>
<pre><code class="language-bash">curl -sL https://rdap.org/domain/xn--ota-f6a.com \
  | jq -r '.entities[] | select(.roles | index("registrar")) | .vcardArray[1][] | select(.[0]=="fn") | .[3]'
# Dynadot Inc
</code></pre>
<p>Many large brands use brand-protection registrars. <code>okta.com</code> uses MarkMonitor.</p>
<h2>Step 3: Check DNS</h2>
<p>Next, see whether the domain is live and whether it can receive email. DNS over HTTPS works everywhere <code>curl</code> does:</p>
<pre><code class="language-bash">curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=xn--ota-f6a.com&amp;type=A' | jq -r '.Answer[].data'
# 104.21.8.140
# 172.67.139.133
</code></pre>
<p>Change <code>type=A</code> to <code>type=MX</code> to check mail. <code>xn--ota-f6a.com</code> has none; <code>okta.com</code> has two. If you have <code>dig</code>, <code>dig +short A xn--ota-f6a.com</code> gives the same answer.</p>
<p>A domain that resolves isn't proof of anything. But a month-old lookalike that points at a live server deserves a closer look.</p>
<h2>All three steps in one script</h2>
<p>Save this as <code>checkdomain.sh</code>. It needs <code>python3</code>, <code>curl</code> and <code>jq</code>:</p>
<pre><code class="language-bash">#!/usr/bin/env bash
# Usage: ./checkdomain.sh &lt;domain&gt;
set -euo pipefail

input="$1"

# Step 1: the real (ASCII/Punycode) name
ascii=$(python3 -c 'import sys; print(sys.argv[1].encode("idna").decode())' "$input")
echo "Domain:     $input"
echo "Real name:  $ascii"
[[ "$ascii" == *xn--* ]] &amp;&amp; echo "            Warning: contains non-ASCII characters (IDN)"

# Step 2: registration date and registrar from RDAP
rdap=$(curl -sfL "https://rdap.org/domain/$ascii" || echo "{}")
created=$(echo "$rdap" | jq -r '.events[]? | select(.eventAction=="registration") | .eventDate' | head -1)
registrar=$(echo "$rdap" | jq -r '[.entities[]? | select(.roles | index("registrar")) | .vcardArray[1][]? | select(.[0]=="fn") | .[3]][0] // "unknown"')
echo "Registered: ${created:-not found in RDAP}  (registrar: $registrar)"
if [[ -n "${created:-}" ]]; then
  age=$(python3 -c 'import sys,datetime as d; c=d.datetime.fromisoformat(sys.argv[1].replace("Z","+00:00")); print((d.datetime.now(d.timezone.utc)-c).days)' "$created")
  echo "Age:        $age days"
  (( age &lt; 90 )) &amp;&amp; echo "            Warning: less than 90 days old"
fi

# Step 3: does it resolve, and can it receive email?
for type in A MX; do
  ans=$(curl -s -H 'accept: application/dns-json' \
    "https://cloudflare-dns.com/dns-query?name=$ascii&amp;type=$type" \
    | jq -r '[.Answer[]?.data] | join(", ")')
  printf '%-11s %s\n' "$type:" "${ans:-none}"
done
</code></pre>
<p>Running it on one of the research domains:</p>
<pre><code class="language-text">$ ./checkdomain.sh oƙta.com
Domain:     oƙta.com
Real name:  xn--ota-f6a.com
            Warning: contains non-ASCII characters (IDN)
Registered: 2026-09-27T12:48:44Z  (registrar: Dynadot Inc)
Age:        30 days
            Warning: less than 90 days old
A:          104.21.8.140, 172.67.139.133
MX:         none
</code></pre>
<h2>Step 4: Run the deeper checks in one call</h2>
<p>The script covers name, age and DNS. It doesn't check threat feeds, the SSL certificate or what the page actually does. For that, I use the WhoisFreaks <a href="https://whoisfreaks.com/documentation/domain-reputation-api">Domain Reputation API</a>. (Disclosure: I work at JFreaks, the company behind WhoisFreaks.)</p>
<pre><code class="language-bash">curl -s "https://api.whoisfreaks.com/v1/domain/security?domainName=xn--ota-f6a.com&amp;apiKey=$WHOISFREAKS_KEY"
</code></pre>
<p>One request runs six checks in parallel: threat-feed matches, live WHOIS (it flags domains under 30 days old), DNS, SSL, the landing page and its redirects, and a score for machine-generated names. Look at three fields in the JSON:</p>
<ul>
<li><code>risk_category.verdict</code>: safe, low risk, suspicious or malicious</li>
<li><code>trust_signals.trust_score</code>: 0 to 100</li>
<li><code>evidence_summary.why_flagged</code>: the reasons, in plain words</li>
</ul>
<p>Each lookup costs 5 credits, and new accounts get 500 free credits without a card. If you only check the odd link, the free script above is enough.</p>
<h2>Step 5: If you own a brand, look for lookalikes first</h2>
<p>Checking one link helps you. Finding lookalikes of your own name before customers do helps everyone who trusts you.</p>
<p>The <a href="https://whoisfreaks.com/documentation/domain-typosquats-api">Typosquatting API</a> searches registered domains for typo variants of a brand name:</p>
<pre><code class="language-bash">curl -s "https://api.whoisfreaks.com/v3.0/domain/typos?keyword=yourbrand&amp;apiKey=$WHOISFREAKS_KEY" \
  | jq -r '.domains[] | "\(.createDate)  \(.domainName)"'
</code></pre>
<p>Each page of up to 100 results costs 5 credits. The free version on the WhoisFreaks site found 2,178 registered domains close to "okta". Sort by <code>createDate</code> and start with the newest.</p>
<p>One honest limit: the docs describe ASCII keywords only and don't say whether Unicode lookalikes like the ones above are included. Pair this with the researchers' own advice and watch for new <code>xn--</code> registrations that resemble your name.</p>
<p>The pool of places to register lookalikes is also about to grow. ICANN revealed 1,615 applications for new top-level domains on October 7, 2026, and the final list of strings is due on November 17 (<a href="https://www.icann.org/resources/press-material/release-2026-10-07-en">ICANN</a>).</p>
<h2>What actually protects people</h2>
<img src="https://cdn.hashnode.com/uploads/covers/68a30da5ed6e911eb30f473b/12800931-faa7-4578-a34e-01bdb7854c06.png" alt="Four protections that work even against perfect lookalike domains: passkeys tied to the real domain, password managers that won't autofill on a mismatched domain, bookmarks for important sites, and mail filtering with impersonation protection." style="display:block;margin:0 auto" />

<p>Spotting fakes is a backup, not the plan. These work even when the domain looks perfect:</p>
<ul>
<li><strong>Passkeys.</strong> They are tied to the real domain, so they won't work on a lookalike.</li>
<li><strong>Password managers.</strong> They won't autofill on a domain that doesn't match the saved one. Treat a missing autofill as a warning.</li>
<li><strong>Bookmarks.</strong> Open important sites from a bookmark, not from a link in a message.</li>
<li><strong>Mail filtering.</strong> Turn on impersonation protection in your mail gateway if you run one.</li>
</ul>
<h2>FAQ</h2>
<p><strong>Is every</strong> <code>xn--</code> <strong>domain malicious?</strong> No. About 733,000 of the roughly 167 million .com domains are internationalized. The researchers found around 162,000 that resemble ASCII names, and many of those are defensive or multilingual registrations.</p>
<p><strong>Do I need an API to check a domain?</strong> No. Steps 1–3 use free, public tools. An API helps when you check many domains or need threat-feed and page-content checks.</p>
<p><strong>Is RDAP the same as WHOIS?</strong> It returns the same kind of registration data, as structured JSON over HTTPS. For generic top-level domains it is now the official source.</p>
<p>Have you seen a lookalike of a site you use? Run it through the script and share what it found in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[CNAME vs A Record: What's the Difference (and Which One Should You Use?)]]></title><description><![CDATA[CNAME vs A record explained simply: what each DNS record does, when to use which, and 3 common mistakes that break websites and email.]]></description><link>https://furqanashraf.hashnode.dev/cname-vs-a-record-what-s-the-difference-and-which-one-should-you-use</link><guid isPermaLink="true">https://furqanashraf.hashnode.dev/cname-vs-a-record-what-s-the-difference-and-which-one-should-you-use</guid><category><![CDATA[dns]]></category><category><![CDATA[networking]]></category><category><![CDATA[beginner developers]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Furqan Ashraf]]></dc:creator><pubDate>Mon, 26 Oct 2026 10:34:16 GMT</pubDate><content:encoded><![CDATA[<p>You're connecting a domain to a new website. Your DNS panel asks you to pick a record type, and the two options that keep showing up are <strong>A</strong> and <strong>CNAME</strong>. Pick the wrong one and your site just... doesn't load.</p>
<p>Here's the short version: <strong>an A record points a name to an IP address. A CNAME record points a name to another name.</strong> Everything else follows from that.</p>
<h2>What is an A record?</h2>
<p>An A (Address) record connects a domain name directly to an IPv4 address.</p>
<pre><code class="language-plaintext">example.com.    3600    IN    A    203.0.113.10
</code></pre>
<p>When someone visits <code>example.com</code>, DNS returns <code>203.0.113.10</code> and the browser connects to that server. One lookup, done.</p>
<p>Need IPv6? That's an <strong>AAAA record</strong>: same idea, longer address.</p>
<p>Curious where that IP actually lives? Paste it into an <a href="https://ipgeolocation.io/tools">IP lookup tool from ipgeolocation.io</a> to see its country, ISP, and ASN. It's a quick way to confirm your A record points to the hosting provider you expect.</p>
<h2>What is a CNAME record?</h2>
<p>A CNAME (Canonical Name) record makes one name an alias of another name.</p>
<pre><code class="language-plaintext">blog.example.com.    3600    IN    CNAME    yourname.github.io.
</code></pre>
<p>Here, DNS sees <code>blog.example.com</code>, follows the alias to <code>yourname.github.io</code>, and then looks up <em>that</em> name's A record to get the IP.</p>
<img src="https://cdn.hashnode.com/uploads/covers/68a30da5ed6e911eb30f473b/e7acd107-c0a7-43d1-beed-c9d25c7bbf7d.png" alt="Diagram showing how a CNAME record resolves: blog.example.com is an alias for yourname.github.io, whose A record returns the IP address 203.0.113.20" style="display:block;margin:0 auto" />

<p>The big win: if the platform changes its server IPs, you don't touch anything. Your CNAME still points to the right name, and the platform handles the rest.</p>
<h2>CNAME vs A record at a glance</h2>
<table>
<thead>
<tr>
<th></th>
<th>A record</th>
<th>CNAME record</th>
</tr>
</thead>
<tbody><tr>
<td>Points to</td>
<td>An IP address</td>
<td>Another domain name</td>
</tr>
<tr>
<td>Works on the root domain (<code>example.com</code>)</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Can share a name with other records</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Follows IP changes automatically</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>DNS lookups needed</td>
<td>One</td>
<td>Two or more</td>
</tr>
</tbody></table>
<h2>When to use each</h2>
<p>Use an <strong>A record</strong> when:</p>
<ul>
<li>You're setting up your root domain (<code>example.com</code>)</li>
<li>You have a server with a fixed IP address that you control</li>
</ul>
<p>Use a <strong>CNAME record</strong> when:</p>
<ul>
<li>You're pointing a subdomain (<code>www</code>, <code>blog</code>, <code>shop</code>) to a hosted service like GitHub Pages, Shopify, or a blogging platform</li>
<li>Your provider tells you to, because they want to control the IPs behind it</li>
</ul>
<p>The simple rule: <strong>root domain → A record. Subdomain pointing to a service → CNAME.</strong></p>
<h2>3 mistakes that break things</h2>
<p><strong>1. Putting a CNAME on your root domain.</strong> Your root domain already has SOA and NS records, and a CNAME can't share a name with any other record. Most DNS providers block this, and where they don't, it can quietly break things like email. If you need alias-style behavior at the root, look for an <strong>ALIAS</strong>, <strong>ANAME</strong>, or <strong>CNAME flattening</strong> option in your DNS provider.</p>
<p><strong>2. Adding a CNAME next to other records.</strong> Can you have a CNAME and an A record with the same name? No. If <code>www.example.com</code> has a CNAME, it can't also have an A, MX, or TXT record. Remove the conflicting record first.</p>
<p><strong>3. Pointing MX records at a CNAME.</strong> Mail (MX) records should point to a hostname that has its own A or AAAA record, not to an alias.</p>
<h2>How to check what you've set</h2>
<img src="https://cdn.hashnode.com/uploads/covers/68a30da5ed6e911eb30f473b/72aaafd1-19f1-4b14-b5b7-10425a53991b.png" alt="Terminal showing dig commands that check a domain's A record and a subdomain's CNAME record, plus the Windows nslookup equivalent" style="display:block;margin:0 auto" />

<p>On macOS or Linux, run:</p>
<pre><code class="language-plaintext">dig example.com A +short
dig www.example.com CNAME +short
</code></pre>
<p>On Windows:</p>
<pre><code class="language-plaintext">nslookup -type=CNAME www.example.com
</code></pre>
<p>Prefer a browser? The <a href="https://whoisfreaks.com/tools/dns/lookup">WhoisFreaks DNS lookup tool</a> shows a domain's A, CNAME, MX, and TXT records in one place.</p>
<p>Need to check records from code, say in a deploy script or uptime monitor? <a href="https://apifreaks.com/api/dns">APIFreaks' DNS APIs</a> return the same records as JSON.</p>
<p>Not seeing your change yet? Give it time. DNS resolvers cache records based on their TTL, so updates can take minutes or hours to show up everywhere.</p>
<h2>Quick recap</h2>
<ul>
<li><strong>A record:</strong> name → IP address</li>
<li><strong>CNAME:</strong> name → another name</li>
<li><strong>Root domain?</strong> Use an A record (or ALIAS if your provider supports it).</li>
<li><strong>Subdomain on a hosted platform?</strong> Use a CNAME.</li>
<li><strong>Never</strong> mix a CNAME with other records on the same name.</li>
</ul>
<p>Got a DNS setup that isn't working? Drop it in the comments and I'll help you figure out which record you need.</p>
]]></content:encoded></item></channel></rss>