<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tan Yong He</title>
    <description>The latest articles on Forem by Tan Yong He (@tanyonghe).</description>
    <link>https://forem.com/tanyonghe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F243440%2F446ec57f-4a50-4e2a-bdcd-3453f93cd951.jpeg</url>
      <title>Forem: Tan Yong He</title>
      <link>https://forem.com/tanyonghe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tanyonghe"/>
    <language>en</language>
    <item>
      <title>TrackLah!</title>
      <dc:creator>Tan Yong He</dc:creator>
      <pubDate>Sun, 08 Jun 2025 14:12:41 +0000</pubDate>
      <link>https://forem.com/tanyonghe/tracklah-9e9</link>
      <guid>https://forem.com/tanyonghe/tracklah-9e9</guid>
      <description>&lt;p&gt;This is a submission for the &lt;a href="https://dev.to/challenges/postmark"&gt;Postmark Challenge: Inbox Innovators&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧾 TrackLah!
&lt;/h1&gt;

&lt;p&gt;Freelancers and small businesses in Singapore often receive payments through peer-to-peer apps like PayLah! or PayNow, and clients typically confirm these transactions by emailing or messaging a screenshot. Manually checking each payment notification, extracting details, and updating a financial record or spreadsheet can be time-consuming and error-prone — especially with different formats, currencies, and inconsistent communication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TrackLah!&lt;/strong&gt; solves the problem by automatically parsing incoming emails (using Postmark's inbound email parsing feature), extracting payment information using OCR, and updating a Google Sheet to track each transaction. It ensures freelancers get real-time updates, avoid missed payments, and maintain a centralized record of all incoming funds — without lifting a finger.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Extracts text from payment screenshots (e.g., PayLah!, PayNow)&lt;/li&gt;
&lt;li&gt;🔤 Uses OCR.space API to parse text from image attachments&lt;/li&gt;
&lt;li&gt;💸 Identifies amount and currency from text&lt;/li&gt;
&lt;li&gt;📋 Appends or updates rows in a Google Sheet&lt;/li&gt;
&lt;li&gt;⚙️ Health check endpoint&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Upcoming Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📋 Generate invoices to clients&lt;/li&gt;
&lt;li&gt;🛡️ Ignores duplicate MessageIDs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React 18&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js, Express&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; Postmark (Transactional API)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Fly.io&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets:&lt;/strong&gt; .env / environment variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control:&lt;/strong&gt; Git &amp;amp; GitHub&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📊 Sequence Diagram
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03gznhx33rygyms3mq4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03gznhx33rygyms3mq4a.png" alt="Sequence Diagram" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Live Demo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Send an Email&lt;/strong&gt;&lt;br&gt;
Compose an email to &lt;a href="mailto:b5fec6c3607a2f29c77a448e01fedcf6@inbound.postmarkapp.com"&gt;b5fec6c3607a2f29c77a448e01fedcf6@inbound.postmarkapp.com&lt;/a&gt; using any email client.&lt;br&gt;
See &lt;code&gt;/examples/&lt;/code&gt; for payment screenshots you can attach to the email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Trigger the Automation&lt;/strong&gt;&lt;br&gt;
The system receives and processes the email via Postmark inbound webhook.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Observe the Result&lt;/strong&gt;&lt;br&gt;
Updates will appear automatically in the connected Google Spreadsheet (you'll see a new row added).&lt;br&gt;
&lt;a href="https://docs.google.com/spreadsheets/d/19cZqjmo9-ZA1uyiAFQNiCbnNKZoQ0O-hrkYDSPclysI" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/19cZqjmo9-ZA1uyiAFQNiCbnNKZoQ0O-hrkYDSPclysI&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only the first attachment will be processed per email.&lt;/li&gt;
&lt;li&gt;The Google Spreadsheet is read/write protected — edits are only made through this integration.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Code Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/tanyonghe/track-lah" rel="noopener noreferrer"&gt;https://github.com/tanyonghe/track-lah&lt;/a&gt;&lt;/p&gt;




</description>
      <category>devchallenge</category>
      <category>postmarkchallenge</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>🌍 Redis Geohashing: Storing and Querying Location Data with Ease</title>
      <dc:creator>Tan Yong He</dc:creator>
      <pubDate>Sat, 31 May 2025 06:56:40 +0000</pubDate>
      <link>https://forem.com/tanyonghe/redis-geohashing-storing-and-querying-location-data-with-ease-1gf4</link>
      <guid>https://forem.com/tanyonghe/redis-geohashing-storing-and-querying-location-data-with-ease-1gf4</guid>
      <description>&lt;p&gt;Redis isn't just a blazing-fast in-memory data store — it also comes packed with geospatial capabilities that are incredibly handy when working with location-based applications. At the core of this is &lt;strong&gt;geohashing&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 What Is Geohashing?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Geohashing&lt;/strong&gt; is a method of encoding geographic coordinates (latitude and longitude) into a single string or number. Redis uses a 52-bit representation of this concept to store locations efficiently.&lt;/p&gt;

&lt;p&gt;Think of it as compressing a lat/lon point into a compact format that allows for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast insertion&lt;/li&gt;
&lt;li&gt;Efficient querying of nearby locations&lt;/li&gt;
&lt;li&gt;Sorting by distance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🗺️ How Redis Uses Geohashing
&lt;/h2&gt;

&lt;p&gt;Redis provides the &lt;code&gt;GEOADD&lt;/code&gt;, &lt;code&gt;GEOPOS&lt;/code&gt;, &lt;code&gt;GEODIST&lt;/code&gt;, and &lt;code&gt;GEORADIUS&lt;/code&gt; (deprecated in favor of &lt;code&gt;GEOSEARCH&lt;/code&gt;) commands to handle geospatial data.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GEOADD places 13.361389 38.115556 &lt;span class="s2"&gt;"Palermo"&lt;/span&gt;
GEOADD places 15.087269 37.502669 &lt;span class="s2"&gt;"Catania"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what's happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis converts each lat/lon pair into a geohash.&lt;/li&gt;
&lt;li&gt;It stores the geohash under a sorted set (zset), using it as the score.&lt;/li&gt;
&lt;li&gt;Location names (like "Palermo") are the values.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           +--------------------------+
           |     Sorted Set (ZSET)    |
           +--------------------------+
           | Score (Geohash) | Member |
           +--------------------------+
           | 34790932423489 | Rome    |
           | 34923498230912 | Milan   |
           | 34982342344933 | Naples  |
           +--------------------------+

Each location's latitude &amp;amp; longitude is converted into a geohash score.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now query nearby locations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GEOSEARCH places FROMLONLAT 15 37 BYRADIUS 200 km
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return all locations within 200km of the specified point — fast and sorted by proximity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                   +-----------------------------+
                   |                             |
                   |        [Center Point]       |
                   |             🧭             |
                   |            / | \            |
                   |           /  |  \           |
                   |         /    |   \          |
                   |      [Nearby locations]     |
                   |         📍     📍            |
                   |                             |
                   +-----------------------------+

Using GEOSEARCH BYRADIUS, Redis returns all points within the specified radius, sorted by proximity from the center point.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧠 Why Use It?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ideal for "what's near me?" features&lt;/strong&gt; – Great for location-based apps like food delivery, rideshare, social meetups, and store locators.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In-memory performance&lt;/strong&gt; – Queries are lightning-fast compared to traditional spatial databases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple, elegant API&lt;/strong&gt; – Easy-to-use commands like GEOADD, GEOSEARCH, and GEODIST.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sorted results by distance&lt;/strong&gt; – Redis returns nearby places ordered by how close they are — no need for post-processing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compact storage&lt;/strong&gt; – Uses 52-bit geohashes internally, making it memory-efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atomic operations&lt;/strong&gt; – Redis commands are atomic, which avoids race conditions in high-concurrency systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-purpose ZSET integration&lt;/strong&gt; – Since geohashes are stored in sorted sets, you can combine geospatial queries with scoring, ranking, or expiring keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cluster-friendly&lt;/strong&gt; – Works well in distributed Redis setups for scalability and high availability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Built-in distance calculations&lt;/strong&gt; – No need for custom Haversine functions — Redis does it out-of-the-box in meters, kilometers, miles, or feet.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧾 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Redis geohashing&lt;/strong&gt; is a powerful yet underused feature that provides a fast and efficient way to store and query geospatial data — perfect for building features like "find nearby places" with minimal setup. With simple commands and in-memory speed, it's a great tool to have in your backend toolbox for location-aware applications.&lt;/p&gt;

&lt;p&gt;Give it a try and see how easy geospatial can be with Redis! 🌐✨&lt;/p&gt;




&lt;p&gt;🚀 Pro tip: Try it out with &lt;code&gt;redis-cli&lt;/code&gt; or libraries like &lt;code&gt;ioredis&lt;/code&gt; for Node.js or &lt;code&gt;redis-py&lt;/code&gt; for Python!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>redis</category>
      <category>database</category>
    </item>
    <item>
      <title>Beware the Evil Twin: Exploring Wi-Fi Impersonation Attacks</title>
      <dc:creator>Tan Yong He</dc:creator>
      <pubDate>Tue, 20 May 2025 05:17:21 +0000</pubDate>
      <link>https://forem.com/tanyonghe/beware-the-evil-twin-exploring-wi-fi-impersonation-attacks-4chd</link>
      <guid>https://forem.com/tanyonghe/beware-the-evil-twin-exploring-wi-fi-impersonation-attacks-4chd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"Is this really Starbucks Wi-Fi... or a trap?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Public Wi-Fi is convenient — but it could also be dangerous. Imagine you're sitting at a café, sipping coffee, and scrolling on your phone. You connect to a network named &lt;code&gt;Starbucks_WiFi&lt;/code&gt; without thinking twice. But what if that network wasn't real? What if it was set up by a hacker just a few feet away?&lt;/p&gt;

&lt;p&gt;Welcome to the world of Evil Twin Attacks — a stealthy and surprisingly common form of cyberattack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdyp09griutf2ioey37d6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdyp09griutf2ioey37d6.png" alt="Demon Slayer Twins" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  😈 What is an Evil Twin Attack?
&lt;/h2&gt;

&lt;p&gt;An Evil Twin Attack is a type of Man-in-the-Middle (MITM) attack where a malicious actor creates a fake Wi-Fi access point (AP) that mimics a legitimate Wi-Fi network's name (known as Service Set Identifier or SSID for short) and appearance.&lt;/p&gt;

&lt;p&gt;When you connect to this rogue AP, the attacker can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intercept your traffic&lt;/li&gt;
&lt;li&gt;Steal login credentials&lt;/li&gt;
&lt;li&gt;Redirect you to phishing websites&lt;/li&gt;
&lt;li&gt;Inject malware into downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, they're silently eavesdropping between you and the internet, watching and manipulating your connections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbt0upnjdyljbb9vxcdac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbt0upnjdyljbb9vxcdac.png" alt="Attack on Titan" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 How Does It Work?
&lt;/h2&gt;

&lt;p&gt;Here's a simplified breakdown of the attack process:&lt;br&gt;
&lt;strong&gt;1. The Setup&lt;/strong&gt;&lt;br&gt;
The attacker configures a Wi-Fi access point using the same SSID as a legitimate one — often something like &lt;code&gt;Airport_Free_WiFi&lt;/code&gt; or &lt;code&gt;Hotel_Guest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Bait&lt;/strong&gt;&lt;br&gt;
Devices that have previously connected to that SSID may auto-connect to the stronger signal — potentially the attacker's AP if it's closer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Trap&lt;/strong&gt;&lt;br&gt;
The attacker may serve a fake login page asking for credentials or silently proxy your traffic — essentially having access to any confidential data you send out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The Exploit&lt;/strong&gt;&lt;br&gt;
Any unencrypted data sent through the fake AP can be logged or modified.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔎 How to Spot an Evil Twin?
&lt;/h2&gt;

&lt;p&gt;Malicious actors would often target popular public hotspots in cafés and airports - areas with high density of devices likely seeking for free Wi-Fi - where it is easy to blend in and set up a rough AP in seconds.&lt;/p&gt;

&lt;p&gt;It's not always obvious — but here are red flags to look for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚩 Duplicate SSIDs with varying signal strengths&lt;/strong&gt;&lt;br&gt;
An attacker will often clone the SSID of a legitimate public Wi-Fi (e.g., &lt;code&gt;Starbucks_WiFi&lt;/code&gt;) to make their rogue network look authentic. But because they're physically located in a different spot than the real router, the signal strength will be noticeably stronger or weaker, depending on your proximity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚩 No encryption (open networks with no 🔒 icon)&lt;/strong&gt;&lt;br&gt;
Legitimate networks, even in public, increasingly use WPA2/WPA3 encryption. If you see an open network (i.e. no password required) that mimics the name of a secured one, it could be an Evil Twin trying to sniff, intercept, and/or modify unencrypted traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚩 Unexpected captive portals asking for logins&lt;/strong&gt;&lt;br&gt;
Some rogue APs use fake captive portals — web pages that pop up when you connect, prompting you to "log in" to access the internet. These are commonly used for phishing credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚩 Frequent disconnections and reconnects&lt;/strong&gt;&lt;br&gt;
Attackers may force your device to disconnect from legitimate Wi-Fi in order to get it to connect to their rogue AP. This is done using deauthentication attacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚩 SSL/TLS warnings in the browser (⚠️)&lt;/strong&gt;&lt;br&gt;
If you get a warning like &lt;code&gt;Your connection is not private&lt;/code&gt;, that's a big red flag because browsers show warnings when certificates don't match. If you see these while on public Wi-Fi, your connection may be susceptible to malicious acts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n879a7z24ro3fs5x2i8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n879a7z24ro3fs5x2i8.png" alt="Pigeon Meme" width="495" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ How to Protect Yourself?
&lt;/h2&gt;

&lt;p&gt;You don't need to be a hacker to defend yourself. Here are practical tips:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Use a Virtual Private Network (VPN)&lt;/strong&gt;&lt;br&gt;
Encrypts your traffic from your device to the VPN server, shielding it from local snooping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Avoid public Wi-Fi&lt;/strong&gt;&lt;br&gt;
Prefer mobile data when accessing sensitive services (banking, email). If you must connect to one, treat the network as untrusted — avoid logging into sensitive accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Disable auto-connect&lt;/strong&gt;&lt;br&gt;
Most operating systems allow you to turn off auto-join for public networks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Use HTTPS Everywhere&lt;/strong&gt;&lt;br&gt;
Ensure sites you visit are encrypted (look for the 🔒 icon).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 Use strong Wi-Fi security at home/work&lt;/strong&gt;&lt;br&gt;
Enterprises should adopt WPA3 + RADIUS and monitor for rogue APs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3jea86yzyw32qfwp6eb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3jea86yzyw32qfwp6eb.png" alt="Two Buttons" width="438" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👨‍💻 For Developers and Cybersecurity Enthusiasts
&lt;/h2&gt;

&lt;p&gt;If you're interested in going beyond theory, here are hands-on exercises to deepen your understanding of Evil Twin Attacks and network security. These are intended for ethical learning in controlled environments — never use these techniques on networks or devices you don't own or have explicit permission to test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Set Up an Evil Twin Access Point&lt;/strong&gt;&lt;br&gt;
Follow a guided tutorial like &lt;a href="https://www.cybrary.it/blog/setting-up-evil-twin-tutorial" rel="noopener noreferrer"&gt;&lt;code&gt;Cybrary's Evil Twin Attack Tutorial&lt;/code&gt;&lt;/a&gt; or similar walkthroughs using tools like &lt;a href="https://www.kali.org/tools/aircrack-ng/" rel="noopener noreferrer"&gt;&lt;code&gt;airbase-ng&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/FluxionNetwork/fluxion" rel="noopener noreferrer"&gt;&lt;code&gt;Fluxion&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;📌 Goal: Understand how easy it is to spoof public networks and trick devices into connecting to spoofed networks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Capture and Analyze Traffic Using Wireshark&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;a href="https://www.wireshark.org/" rel="noopener noreferrer"&gt;&lt;code&gt;Wireshark&lt;/code&gt;&lt;/a&gt; to observe unencrypted HTTP traffic, DNS requests, as well as visible credentials or session cookies (especially in insecure apps).&lt;/p&gt;

&lt;p&gt;📌 Goal: See firsthand how unencrypted data travels across open Wi-Fi and why HTTPS and VPNs are critical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Explore the MITRE ATT&amp;amp;CK Framework&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Read about &lt;a href="https://attack.mitre.org/techniques/T1557/002/" rel="noopener noreferrer"&gt;&lt;code&gt;Technique T1557.002: Rogue Wireless Access Points&lt;/code&gt;&lt;/a&gt; and map the Evil Twin attack flow to MITRE's stages (Reconnaissance → Initial Access → Credential Access).&lt;/p&gt;

&lt;p&gt;📌 Goal: Gain a structured understanding of how attackers use wireless access as part of a larger kill chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧾 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Evil Twin Attacks are deceptively simple, yet dangerously effective. And as we grow increasingly reliant on wireless connectivity, cybersecurity awareness is no longer optional — it's essential.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpigjfh2o18wq6zqiar6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpigjfh2o18wq6zqiar6z.png" alt="Drakeposting" width="496" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;✍️ Have you ever suspected a rogue network? Share your experience or thoughts in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
      <category>networking</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Ditching Postman: Why and How I Transitioned to Bruno</title>
      <dc:creator>Tan Yong He</dc:creator>
      <pubDate>Mon, 12 May 2025 18:00:39 +0000</pubDate>
      <link>https://forem.com/tanyonghe/ditching-postman-why-and-how-i-transitioned-to-bruno-535e</link>
      <guid>https://forem.com/tanyonghe/ditching-postman-why-and-how-i-transitioned-to-bruno-535e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6cbxh8fu8u0sxg6qtmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6cbxh8fu8u0sxg6qtmm.png" alt="Ditching Postman" width="670" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pains of API Testing
&lt;/h2&gt;

&lt;p&gt;As a developer who regularly works with APIs, Postman has been the go-to choice for testing, documenting, and sharing across teams. However, over time, I began to feel the bloat. Postman became increasingly heavy, slow to start, and frustrating to use in version-controlled environments. That's when I discovered Bruno: a lightweight, open-source API client aiming to take on Postman's heavyweight legacy.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk you through why I made the switch from Postman to Bruno, what the transition looked like, and how my workflow has improved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flii1lt0vnxeog1th808b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flii1lt0vnxeog1th808b.png" alt="Bruno vs Postman" width="677" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Postman: The King of the Court
&lt;/h2&gt;

&lt;p&gt;Needing no introductions, Postman is undoubtedly a powerful tool. But with power often comes weight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Heavy:&lt;/strong&gt; Postman's Electron-based architecture makes it noticeably slow and memory-hungry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git-Unfriendly:&lt;/strong&gt; Collections are stored in a proprietary format, making version control awkward and merge conflicts inevitable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-Unfriendly:&lt;/strong&gt; Requires internet connection for full functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing Concerns:&lt;/strong&gt; Collaboration and advanced testing features are locked behind paid plans.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I realized I needed something simpler — something more developer-centric and user-friendly without all the gimmicky features I'd rarely/never use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Bruno: The New Kid on the Block
&lt;/h2&gt;

&lt;p&gt;Built by a team of developers who understand the pain points of using Postman, Bruno was designed to address them head-on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight:&lt;/strong&gt; Built as a native application with performance in mind, Bruno is snappy and resource-efficient, even on modest hardware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git-Friendly:&lt;/strong&gt; Its file-based request structure means you can version control your API work just like your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-Friendly:&lt;/strong&gt; Works entirely offline, so you're never blocked by internet issues or cloud service downtimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Source:&lt;/strong&gt; No paywalls, no subscriptions, just code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Transitioning from Postman to Bruno
&lt;/h2&gt;

&lt;p&gt;Two words — Export. Import.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Export from Postman:&lt;/strong&gt; Export Postman collections in JSON format.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ml5cwvy3y2lmxlh9g9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ml5cwvy3y2lmxlh9g9m.png" alt="Export from Postman" width="524" height="516"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Import into Bruno:&lt;/strong&gt; Bruno natively supports various imports from other platforms like Postman and transforms them into .bru files.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famips5ci52pi3c8drd5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famips5ci52pi3c8drd5t.png" alt="Import into Bruno" width="513" height="271"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Easy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Bruno
&lt;/h2&gt;

&lt;p&gt;Bruno may be minimal, but it's got the essentials covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creating Requests:&lt;/strong&gt; Simple interface for GET, POST, PUT, etc., with headers and body configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variables:&lt;/strong&gt; Supports multiple environments via env files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Viewer:&lt;/strong&gt; Neatly displays response headers, body, and time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripting:&lt;/strong&gt; Though not as robust as Postman's scripting, Bruno supports basic request chaining and JavaScript snippets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control:&lt;/strong&gt; Every change is tracked, readable, and merge-friendly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most use cases, Bruno is more than enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bruno Ain't Perfect
&lt;/h2&gt;

&lt;p&gt;Like many tools out there, Bruno — despite the many benefits — may face drawbacks as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smaller Community:&lt;/strong&gt; Being a relatively new tool, Bruno doesn't yet have the same size or activity level as Postman's community. That means fewer community-made tutorials, integrations, or Stack Overflow answers when you hit a snag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited Advanced Features:&lt;/strong&gt; Lacks some of Postman's advanced capabilities like built-in test assertions, workflow automation (like Newman CLI runners), monitors, and pre/post-request scripting with detailed logic. For complex API validation flows or automated test suites, you might find Bruno underpowered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Cloud Sync / Collaboration:&lt;/strong&gt; Lacks real-time collaboration, shared workspaces, and automated syncing features that are helpful for larger teams or non-technical collaborators.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It may not be the ultimate Swiss Army knife for API testing, but it gets the job done exceptionally well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Living with Bruno
&lt;/h2&gt;

&lt;p&gt;Since switching to Bruno, my workflow feels cleaner and more developer-centric.&lt;/p&gt;

&lt;p&gt;I keep all API collection files in my Git repositories alongside the codebase where branches and pull requests will include both code and collection updates, which makes reviewing changes much easier. Bruno's simplicity lets me focus on what matters — building and testing APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If Postman feels like an overkill for your day-to-day API work — or if you're frustrated by its limitations — Bruno might be the breath of fresh air you need. It is not trying to replace every feature Postman offers, but it does what it needs to, and does it well. For solo developers or small teams who value simplicity, performance, and Git-friendly workflows, Bruno is a compelling alternative worth checking out.&lt;/p&gt;

&lt;p&gt;In fact, having both tools at our disposal can enhance our workflow — we just need to know when to use each one effectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0z5n7h7tzjawhaizajw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0z5n7h7tzjawhaizajw.png" alt="Bruno and Postman" width="686" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading! If you've made the switch too or are considering it, I'd love to hear your experience.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>writing</category>
    </item>
  </channel>
</rss>
