<?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>DEV Community: Spicy</title>
    <description>The latest articles on DEV Community by Spicy (@spicykim).</description>
    <link>https://dev.to/spicykim</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3930300%2Ff80e2e97-ebe7-4ee3-b2cb-d70ff6eac7bc.png</url>
      <title>DEV Community: Spicy</title>
      <link>https://dev.to/spicykim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spicykim"/>
    <language>en</language>
    <item>
      <title>Why HIPAA Doesn't Cover the Health Data Your API Just Pulled</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Sat, 04 Jul 2026 12:10:45 +0000</pubDate>
      <link>https://dev.to/spicykim/why-hipaa-doesnt-cover-the-health-data-your-api-just-pulled-3bjk</link>
      <guid>https://dev.to/spicykim/why-hipaa-doesnt-cover-the-health-data-your-api-just-pulled-3bjk</guid>
      <description>&lt;p&gt;If you're building anything on top of HealthKit, Google Fit, or a direct wearable API integration, there's a compliance assumption worth checking before you ship: HIPAA almost certainly doesn't apply to the data you're pulling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Covered Entity Problem
&lt;/h2&gt;

&lt;p&gt;HIPAA only regulates "covered entities" — hospitals, doctors, health insurers, and their direct business associates. A consumer wearable API (Apple HealthKit, Google Fit, Fitbit Web API, Oura API, Whoop API) doesn't make your app a covered entity just because it's handling heart rate or sleep data. Unless you're building specifically for a healthcare provider under a signed business associate agreement, you're outside HIPAA's scope entirely, according to a &lt;a href="https://www.halllawgroup.com/love-your-wearable-technology-know-what-youre-sharing/" rel="noopener noreferrer"&gt;legal breakdown from the Law Office of Jeffrey Hall&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That means the usual HIPAA checklist — encryption at rest, audit logs, minimum necessary access — is good practice, but it's not a legal requirement for most consumer-facing health apps. Nobody is going to fine you under HIPAA for over-collecting sleep data. That's the trap, not the reassurance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Regulates This Data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Regulation&lt;/th&gt;
&lt;th&gt;Applies to consumer wearable apps?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HIPAA&lt;/td&gt;
&lt;td&gt;No (unless working with a covered entity)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GDPR (EU users)&lt;/td&gt;
&lt;td&gt;Yes, as sensitive personal data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CCPA (California users)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Washington My Health My Data Act&lt;/td&gt;
&lt;td&gt;Yes, if any WA users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App store health data policies&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The most interesting one for engineers is Washington's My Health My Data Act (RCW 19.373), passed in 2023. It's the first US state law written specifically to close the HIPAA gap for consumer health data, and it applies to any business with Washington users, not just Washington-based companies. If your app has any US user base, you likely have Washington users. The Act requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A separate, prominently linked Consumer Health Data Privacy Policy (not folded into your general privacy policy)&lt;/li&gt;
&lt;li&gt;Explicit opt-in consent before collection or sharing, not just a terms-of-service checkbox&lt;/li&gt;
&lt;li&gt;A functioning deletion request path across all systems, including backups&lt;/li&gt;
&lt;li&gt;Written authorization retained for six years if you ever sell health data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full text is at &lt;a href="https://www.atg.wa.gov/protecting-washingtonians-personal-health-data-and-privacy" rel="noopener noreferrer"&gt;RCW 19.373&lt;/a&gt; if you're scoping compliance work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Implementation Notes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Data retention.&lt;/strong&gt; If your backend keeps synced health metrics after a user deletes their account, you're not just accumulating liability, you're likely violating deletion-right requirements in at least one jurisdiction your users touch. Build a real deletion pipeline that hits backups, not just the primary table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party SDKs.&lt;/strong&gt; If you're piping HealthKit data into an analytics SDK, an ad network, or a crash reporter, check that SDK's data handling terms specifically. "Anonymized" health data has been shown to be re-identifiable at high accuracy when cross-referenced with other datasets, which is a real risk if you're forwarding granular biometric streams anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consent UX.&lt;/strong&gt; A single "Allow Health Access" iOS permission prompt is not equivalent to the affirmative, specific consent required under Washington's law or GDPR. If you're monetizing health data in any way (even aggregated), build a separate consent flow for that specific use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Employer/insurer integrations.&lt;/strong&gt; If you're building a wellness-program integration that feeds data to an employer or insurer, treat that data flow as high-risk by default. There's no HIPAA wall preventing that data from eventually informing risk decisions, and the legal exposure sits with whoever built the pipe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Found
&lt;/h2&gt;

&lt;p&gt;Most developer documentation for these APIs focuses entirely on the technical integration and barely mentions the regulatory landscape. Apple's HealthKit guidelines are the exception — they're genuinely strict about app review for health data access. Google Fit and most third-party wearable APIs leave compliance almost entirely up to you.&lt;/p&gt;

&lt;p&gt;If I were architecting a new wearable-data feature today, I'd default to treating every user as if they were covered by Washington's My Health My Data Act, since it's currently the strictest applicable standard and building to it covers most other jurisdictions by default.&lt;/p&gt;

&lt;p&gt;Full write-up with the consumer-facing angle and more on the legal background: &lt;a href="https://lucas8.com/wearable-health-data-hipaa-gap" rel="noopener noreferrer"&gt;https://lucas8.com/wearable-health-data-hipaa-gap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>The Solar Tax Credit Died in 2026. Here's the Decision Tree.</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Sat, 04 Jul 2026 12:09:41 +0000</pubDate>
      <link>https://dev.to/spicykim/the-solar-tax-credit-died-in-2026-heres-the-decision-tree-3pd9</link>
      <guid>https://dev.to/spicykim/the-solar-tax-credit-died-in-2026-heres-the-decision-tree-3pd9</guid>
      <description>&lt;p&gt;If you priced out solar before 2026 and priced it out again this year, the numbers probably look worse. That's not a sales tactic or a market shift. The 30% federal residential solar tax credit (Section 25D) ended on December 31, 2025, with zero phase-down period. No grandfathering, no partial credit for contracts signed in 2025 but installed in 2026.&lt;/p&gt;

&lt;p&gt;That said, the credit's disappearance created a fork in the decision tree rather than a dead end. Here's the breakdown, without the sales pitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Ended
&lt;/h2&gt;

&lt;p&gt;Section 25D let homeowners deduct 30% of system cost directly from federal taxes, with no cap and no income limit. It was legislated to run through 2034. The One Big Beautiful Bill Act, signed July 2025, moved the termination date to the end of 2025 instead — a nearly decade-early sunset. If your system was placed in service on or before December 31, 2025, you still claim the credit on that year's return. If it goes live in 2026 or later, the credit doesn't apply, full stop.&lt;/p&gt;

&lt;p&gt;The mechanism that survived is ownership structure. Section 25D covered direct ownership only. Section 48E, the commercial/business investment credit, is still active for entities that own solar equipment and lease or sell power from it. That single distinction is why "no tax credit" isn't accurate for every path to solar in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Remaining Paths
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Third-party ownership (lease/PPA).&lt;/strong&gt; A company owns the panels on your roof; you buy the electricity. That company can still claim the Section 48E credit and typically passes savings through as a lower monthly rate instead of a lump-sum deduction on your return. You lose equity-building and any ownership-dependent state incentives, but if your tax liability was too small to use 30% anyway, this closes most of the gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. State-level programs, independent of federal policy.&lt;/strong&gt; These never depended on 25D and mostly survived untouched:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State income tax credits (a minority of states)&lt;/li&gt;
&lt;li&gt;Sales tax exemptions on solar equipment (CA, FL, AZ, CO, NY, and others)&lt;/li&gt;
&lt;li&gt;Property tax exemptions so added home value doesn't raise your tax bill (30+ states)&lt;/li&gt;
&lt;li&gt;Production-based payments like New Jersey's SuSI program, which pays per kWh for 15 years&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check your specific state through the &lt;a href="https://dsireusa.org/" rel="noopener noreferrer"&gt;Database of State Incentives for Renewables &amp;amp; Efficiency&lt;/a&gt;, the DOE-funded clearinghouse — coverage is wildly inconsistent state to state, so a generic "solar incentives still exist" claim is close to useless without checking your own address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Net metering and community solar.&lt;/strong&gt; Net metering — getting credited for excess power sent back to the grid — is a utility mechanism, not a tax credit, so it wasn't touched by the OBBBA at all. Community solar (buying a share of an off-site array instead of installing your own panels) extends this to renters and shaded-roof homes with zero upfront installation cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Who claims the credit&lt;/th&gt;
&lt;th&gt;You own the system&lt;/th&gt;
&lt;th&gt;Works if you have low tax liability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cash/loan purchase&lt;/td&gt;
&lt;td&gt;No one (credit gone)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No benefit either way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lease / PPA&lt;/td&gt;
&lt;td&gt;Third-party owner&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes — savings via lower rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State credit + exemptions&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Depends on state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Net metering / community solar&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Yes — utility-side, not tax-side&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What I Actually Found
&lt;/h2&gt;

&lt;p&gt;Most coverage treats this as one national story: "credit's gone, solar's still worth it long-term." That framing skips the part that actually matters — the size of the gap left behind depends entirely on your state's net metering rules and exemption stack, not on a national average payback period.&lt;/p&gt;

&lt;p&gt;If I were running these numbers for myself, I'd ask an installer for two quotes side by side: total cost with zero incentives applied, and total cost after every program that applies specifically to my address. If an installer can't produce both numbers without a follow-up call, they're probably still quoting off a pre-2026 script.&lt;/p&gt;

&lt;p&gt;The federal credit for direct ownership isn't coming back this cycle. But state programs, net metering, and lease structures are still on the table for most of the country, and stacking even two of them gets you closer to 2025's numbers than most headlines suggest.&lt;/p&gt;

&lt;p&gt;Full breakdown with a state-by-state incentive walkthrough: &lt;a href="https://lucas8.com/solar-tax-credit-alternatives-2026" rel="noopener noreferrer"&gt;https://lucas8.com/solar-tax-credit-alternatives-2026&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Nutrition Label That Software Always Lacked</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Fri, 03 Jul 2026 16:45:31 +0000</pubDate>
      <link>https://dev.to/spicykim/the-nutrition-label-that-software-always-lacked-213</link>
      <guid>https://dev.to/spicykim/the-nutrition-label-that-software-always-lacked-213</guid>
      <description>&lt;p&gt;On December 9, 2021, a security researcher posted a proof-of-concept exploit for a vulnerability in a Java library called Log4j. Within 72 hours, hundreds of millions of systems were at risk. The chaos that followed wasn't primarily about the vulnerability's severity. It was about something more fundamental: most organizations had no way to quickly search their own software to find out whether Log4j was buried somewhere inside it.&lt;/p&gt;

&lt;p&gt;That moment made the software bill of materials — SBOM — go from a niche compliance topic to a board-level conversation almost overnight.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an SBOM?
&lt;/h2&gt;

&lt;p&gt;A software bill of materials is a structured, machine-readable inventory of every component inside a piece of software. Think of it as the nutrition label on a food package, applied to code. It tells you which open-source libraries, third-party packages, and internal components make up a given application — along with their versions, licenses, and any known vulnerabilities.&lt;/p&gt;

&lt;p&gt;Modern software is rarely written entirely from scratch. A typical commercial application might contain 500 to 1,500 third-party dependencies by the time all transitive relationships are counted. Without an SBOM, tracking all of those relationships manually is effectively impossible.&lt;/p&gt;

&lt;p&gt;Two open standards dominate today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPDX (Software Package Data Exchange)
  Origin: Linux Foundation
  Primary focus: License compliance
  Best for: Teams where legal is the main concern

CycloneDX
  Origin: OWASP
  Primary focus: Security and vulnerability tracking
  Best for: DevSecOps and security-focused teams
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Log4Shell Made This Non-Negotiable
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2021-44228" rel="noopener noreferrer"&gt;The Log4Shell vulnerability — CVE-2021-44228&lt;/a&gt; affected an estimated 3 billion devices. But the more instructive part of the story was the response time gap.&lt;/p&gt;

&lt;p&gt;Companies that had SBOMs — even rough, incomplete ones — could run an automated search and know within hours whether they were affected. Companies without them spent weeks manually auditing every application, tracing every dependency tree, and waiting on vendor responses.&lt;/p&gt;

&lt;p&gt;The 2024 XZ Utils backdoor reinforced the same point. An attacker spent nearly two years contributing to an open-source project before inserting a hidden backdoor into a widely used compression library. Most organizations still had no automated way to know whether that library was in their stack. Supply chain attacks exploit exactly the visibility gap that an SBOM closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Requires SBOM Now
&lt;/h2&gt;

&lt;p&gt;Compliance pressure has moved faster than most timelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;US Executive Order 14028 (May 2021)
  → Federal software vendors must provide SBOMs
  → Status: Active

FDA Medical Device Guidance (Oct 2023)
  → SBOM required with every premarket submission
  → Status: Active

EU Cyber Resilience Act (Dec 2027)
  → Covers all software and connected devices sold in the EU
  → SBOM documentation explicitly required
  → Status: Full enforcement December 2027
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to ENISA's 2026 figures, 78% of enterprises have begun SBOM adoption. A DigiCert survey found that 43% of companies expect to face explicit SBOM requirements within 24 months. The gap between "starting adoption" and "production-ready compliance" remains substantial for most of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Actually Get Started
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cisa.gov/sbom" rel="noopener noreferrer"&gt;CISA maintains a comprehensive SBOM resource library&lt;/a&gt; covering formats, tooling, and implementation guidance. For most teams, the practical starting point looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: Pick a standard
  → Security-first team: CycloneDX
  → License compliance priority: SPDX

Step 2: Integrate automated generation
  → Open source: Syft (Anchore) — generates from container images and filesystems
  → Open source: Grype — vulnerability scanning against generated SBOMs
  → Commercial: FOSSA, Snyk, Black Duck — CI/CD pipeline integration

Step 3: Connect to a vulnerability database
  → NIST NVD
  → OSV (Open Source Vulnerabilities)
  → Commercial feeds from vendors above

Step 4: Establish update cadence
  → Tie SBOM generation to every build, not a quarterly audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start with a single application. Attempting to retrofit SBOM generation across an entire portfolio at once is where most programs stall.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Found
&lt;/h2&gt;

&lt;p&gt;The framing that dominates most SBOM coverage — "generate an SBOM, know what's in your software, fix vulnerabilities faster" — is accurate but incomplete in a way that matters practically.&lt;/p&gt;

&lt;p&gt;Most organizations that invest in SBOM tooling discover the same thing in the first few months: the SBOM reveals a dependency tree that's far messier than anyone expected, and the organizational process for responding to what it reveals doesn't exist yet.&lt;/p&gt;

&lt;p&gt;An SBOM is a diagnostic tool. It tells you what's there. But knowing your application has 847 dependencies, 23 of which have known CVEs, doesn't automatically generate a prioritization framework, a patch ownership model, or a vendor communication process.&lt;/p&gt;

&lt;p&gt;The companies that responded fastest to Log4Shell weren't the ones with the most sophisticated tooling. They were the ones that had already answered "who owns this dependency when something goes wrong?" before the crisis hit. The SBOM tells you what you have. The harder question it surfaces is whether your organization is structured to act on it.&lt;/p&gt;




&lt;p&gt;Full piece with more detail and external sources: &lt;a href="https://lucas8.com/what-is-sbom-software-bill-of-materials/" rel="noopener noreferrer"&gt;lucas8.com/what-is-sbom-software-bill-of-materials&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>Is AI to Blame for Your Rising Electric Bill?</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Wed, 01 Jul 2026 15:06:30 +0000</pubDate>
      <link>https://dev.to/spicykim/is-ai-to-blame-for-your-rising-electric-bill-1kd</link>
      <guid>https://dev.to/spicykim/is-ai-to-blame-for-your-rising-electric-bill-1kd</guid>
      <description>&lt;p&gt;In January 2026, a Virginia man received an electric bill for $281 — nearly triple the $100 he had paid the previous month. He had lived in the same house for 40 years. Nothing in his home had changed. What had changed was what surrounded it: Northern Virginia's "Data Center Alley," the largest concentration of AI data centers on the planet.&lt;/p&gt;

&lt;p&gt;The question he started asking has become one of the defining consumer issues of 2026: are AI data centers genuinely responsible for rising electric bills? The honest answer is more nuanced than most headlines suggest. But the short version is: yes, partly, and the share is growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Much Electricity Do AI Data Centers Actually Use?
&lt;/h2&gt;

&lt;p&gt;The numbers are genuinely staggering. According to the IEA, data centers accounted for roughly half of all new electricity demand growth in the US last year — a share the agency expects to hold through 2030.&lt;/p&gt;

&lt;p&gt;Total US data center energy demand is projected to nearly double between 2025 and 2028, jumping from 80 to 150 gigawatts. That 70-gigawatt addition is roughly equivalent to the entire annual electricity consumption of Spain added to the US grid in just three years.&lt;/p&gt;

&lt;p&gt;Virginia alone now hosts nearly 600 data centers, with facilities accounting for close to 40 percent of all electricity used in the state in 2024. The race to find enough power for this demand has pushed the energy industry toward nuclear and other alternative energy sources, turning what was once a tech industry concern into a kitchen-table issue for millions of American households.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Connection to Your Bill Is Real
&lt;/h2&gt;

&lt;p&gt;The mechanism isn't a direct surcharge. But the path from data center construction to higher household costs is real, and it runs through the utility infrastructure system.&lt;/p&gt;

&lt;p&gt;When a data center connects to the grid, utilities must upgrade infrastructure to handle the added load — new transmission lines, transformers, generation capacity. Those investments get approved by state regulators and recovered through rate increases spread across all customers in the service territory, including residential ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cnbc.com/2026/02/12/electricity-price-data-center-ai-inflation-goldman.html" rel="noopener noreferrer"&gt;Goldman Sachs reported&lt;/a&gt; that US electricity prices jumped 6.9% in 2025 — more than double the headline inflation rate — and forecasts an additional 6% increase through 2027. In areas with heavy data center concentrations, Bloomberg found electricity costs rose 267% over the past five years.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://advocacy.consumerreports.org/press_release/consumer-reports-new-survey-finds-americans-are-skeptical-of-big-techs-pledge-to-pay-for-all-ai-data-center-energy-costs" rel="noopener noreferrer"&gt;Consumer Reports survey from May 2026&lt;/a&gt; found that 78% of Americans are concerned that data centers will continue raising their household energy bills. And when told about the "Ratepayer Protection Pledge" — a White House-sponsored agreement signed by Amazon, Google, Meta, Microsoft, OpenAI, Oracle, and xAI pledging to cover their full energy costs — 75% said they were not confident companies would follow through.&lt;/p&gt;

&lt;h2&gt;
  
  
  But Is AI the Only Culprit?
&lt;/h2&gt;

&lt;p&gt;Here's where the honest answer gets complicated.&lt;/p&gt;

&lt;p&gt;US residential electricity prices rose more than 30% between 2021 and early 2026 — a trend that began well before ChatGPT launched. Aging grid infrastructure, climate change, coal and natural gas plant closures, and structural issues in regional electricity markets were already pushing bills upward before hyperscalers started adding hundreds of gigawatts of demand.&lt;/p&gt;

&lt;p&gt;SemiAnalysis published a detailed analysis arguing that an obscure capacity auction mechanism in the PJM market — covering 13 mid-Atlantic and Midwest states — accounts for much of the electricity price surge, with data center load playing an amplifying but not singular role.&lt;/p&gt;

&lt;p&gt;The most accurate framing: AI data centers are a real and growing contributor to higher bills, but they're landing on top of a system that was already struggling with affordability. Your bills would be lower without the data center boom. They wouldn't be cheap regardless.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Found
&lt;/h2&gt;

&lt;p&gt;What surprised me wasn't the scale of the numbers — those are well documented. It was how clearly the politics had already shifted by mid-2026.&lt;/p&gt;

&lt;p&gt;In 2023 and 2024, criticism of data center energy use came mostly from environmental advocates and local communities. By 2026, it had become a mainstream voter concern: bipartisan calls in Congress, gubernatorial campaigns in Virginia and New Jersey fought partly on utility affordability, a Maine moratorium on new data center construction, and a White House pledge that would not have existed if this weren't a live political issue at the presidential level.&lt;/p&gt;

&lt;p&gt;The practical implication for a household is limited — there's no individual action that fully insulates you from regional rate increases driven by infrastructure decisions made at the utility and regulatory level. The most effective lever is engagement with state public utility commission proceedings, where rate increase requests are actually approved or rejected.&lt;/p&gt;

&lt;p&gt;The Virginia man with the $281 bill was right to ask the question. The full answer is complicated, but it starts with the same honest admission: the data centers that run the AI tools we use every day are not free, and someone is paying for them.&lt;/p&gt;




&lt;p&gt;Full piece with more detail and sources: &lt;a href="https://lucas8.com/ai-data-center-electric-bill/" rel="noopener noreferrer"&gt;lucas8.com/ai-data-center-electric-bill&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>That QR Code on the Parking Meter Might Be a Scam</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Tue, 30 Jun 2026 15:35:19 +0000</pubDate>
      <link>https://dev.to/spicykim/that-qr-code-on-the-parking-meter-might-be-a-scam-54hp</link>
      <guid>https://dev.to/spicykim/that-qr-code-on-the-parking-meter-might-be-a-scam-54hp</guid>
      <description>&lt;p&gt;You scan QR codes constantly without thinking about it — the restaurant menu, the parking meter, the flyer taped to a lamppost. A QR code scam usually doesn't look like a scam at all, which is exactly the problem. It looks like a sticker. It looks like part of the wallpaper.&lt;/p&gt;

&lt;p&gt;That gap between how QR codes look and what they can actually do is where quishing — QR code phishing — has quietly become one of the fastest-growing scam categories of 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  How QR Code Scams Work
&lt;/h2&gt;

&lt;p&gt;A QR code scam works because the malicious part is invisible until the moment you scan it. Unlike a phishing email, where a suspicious link sits in plain text, a QR code hides its destination inside a pattern of black and white squares that no human can read without a camera.&lt;/p&gt;

&lt;p&gt;Attackers exploit that blind spot by printing their own code on a sticker and placing it directly over a legitimate one — on a parking meter, a restaurant table tent, a flyer, or a delivery package. The fake code usually leads to a cloned login page, a fraudulent payment screen, or a malware download.&lt;/p&gt;

&lt;p&gt;This isn't a small trend. Quishing incidents jumped 146% in the first quarter of 2026 alone, with nearly 18.7 million cases recorded in March, according to threat intelligence data reported by &lt;a href="https://www.wataugademocrat.com/ap/state/quishing-surges-146-in-q1-2026-as-attackers-hide-behind-the-code/article_121840ac-c8d4-5e08-8c97-7cadd00ed686.html" rel="noopener noreferrer"&gt;Watauga Democrat&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Shows Up Most
&lt;/h2&gt;

&lt;p&gt;Public parking meters are one of the most common targets, since the sticker format is easy to replicate. Police departments in Denver and Austin have documented fake QR stickers placed over legitimate parking codes, redirecting drivers to payment pages that steal card details.&lt;/p&gt;

&lt;p&gt;Restaurant table tents, parcel delivery notices, fake court summons, and event posters round out the most common categories — all relying on the same trick: a context where scanning feels expected, not suspicious.&lt;/p&gt;

&lt;p&gt;Email-based quishing has also grown. QR codes embedded in PDF attachments or images slip past traditional phishing filters that only scan visible text. According to the &lt;a href="https://www.isthisqrsafe.com/learn/quishing-complete-guide" rel="noopener noreferrer"&gt;complete quishing guide from Is This QR Safe&lt;/a&gt;, this is exactly why security teams describe it as a blind spot in standard email defenses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Even Careful People Fall For It
&lt;/h2&gt;

&lt;p&gt;Most people scan QR codes without checking the destination first, and surveys have found that the majority of consumers can't reliably tell a malicious code from a legitimate one just by looking at it. High trust plus low verification is exactly what gives quishing room to grow even as awareness of regular email phishing improves.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Spot a Fake Code Before You Scan
&lt;/h2&gt;

&lt;p&gt;Look at the sticker itself before you look at your phone. A code that's crooked, layered on top of a different sticker, or peeling at one corner is a strong sign someone placed it there after the fact.&lt;/p&gt;

&lt;p&gt;Use your phone's built-in camera preview instead of a dedicated scanning app whenever possible. Most modern phones show you the destination URL before opening it — check whether the domain matches who you'd expect.&lt;/p&gt;

&lt;p&gt;Dynamic QR codes route through a shortened link before reaching their final destination, which makes the preview less useful on its own. In those cases, look at whether the page that finally loads matches the branding, fonts, and layout you'd expect.&lt;/p&gt;

&lt;p&gt;Pause on anything that asks you to log in or enter payment details immediately after scanning. Legitimate parking apps and ordering systems rarely require fresh credentials every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Found
&lt;/h2&gt;

&lt;p&gt;What surprised me digging into this wasn't how sophisticated these scams are — it's how little sophistication they need. Most of the fake QR codes documented by police departments weren't elaborate forgeries. They were printer-paper stickers, sometimes a visibly different shade of white than the surface underneath, placed by someone who knew nobody really inspects a parking meter before paying it.&lt;/p&gt;

&lt;p&gt;Most security advice focuses on apps and scanner tools that preview links before opening them. Those help, but they miss the simpler habit that actually prevents most of these scams: physically looking at the sticker for two seconds before you scan, not just checking the link after you've already decided to trust it.&lt;/p&gt;

&lt;p&gt;If I had to pick one habit to actually keep, it would be treating any QR code on a payment-related surface — meters, parking lots, toll booths — as default-suspicious until proven otherwise. Those are the highest-value, lowest-effort targets for this exact scam.&lt;/p&gt;




&lt;p&gt;Full piece with more detail and visuals: &lt;a href="https://lucas8.com/qr-code-scam-parking-meter/" rel="noopener noreferrer"&gt;lucas8.com/qr-code-scam-parking-meter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>Samsung vs LG vs Roku vs Fire TV vs Apple TV: Which One Tracks You the Least?</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Sun, 28 Jun 2026 15:33:23 +0000</pubDate>
      <link>https://dev.to/spicykim/samsung-vs-lg-vs-roku-vs-fire-tv-vs-apple-tv-which-one-tracks-you-the-least-14em</link>
      <guid>https://dev.to/spicykim/samsung-vs-lg-vs-roku-vs-fire-tv-vs-apple-tv-which-one-tracks-you-the-least-14em</guid>
      <description>&lt;p&gt;Your smart TV is running a feature you almost certainly never turned on.&lt;/p&gt;

&lt;p&gt;It's called ACR — Automatic Content Recognition. It works like Shazam, but instead of identifying songs on request, it runs continuously in the background: capturing screenshots of whatever is on your screen, matching those frames against a content database, and sending that viewing history to advertisers and data brokers. It tracks everything — streaming apps, live TV, and devices plugged into HDMI like your game console or cable box.&lt;/p&gt;

&lt;p&gt;In December 2025, Texas Attorney General Ken Paxton sued Samsung, LG, Sony, Hisense, and TCL over ACR, calling it "an uninvited, invisible digital invader." His office obtained temporary restraining orders against Samsung and Hisense while the cases proceed.&lt;/p&gt;

&lt;p&gt;Here's how the six major platforms actually compare.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comparison at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;ACR Used&lt;/th&gt;
&lt;th&gt;HDMI Tracking&lt;/th&gt;
&lt;th&gt;On by Default&lt;/th&gt;
&lt;th&gt;Data Sold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Samsung Tizen&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LG webOS&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roku&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google TV&lt;/td&gt;
&lt;td&gt;⚠️ Platform: No / Brand: Varies&lt;/td&gt;
&lt;td&gt;⚠️ Varies&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Fire TV&lt;/td&gt;
&lt;td&gt;❌ No (HDMI)&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;⚠️ Partial&lt;/td&gt;
&lt;td&gt;⚠️ Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple TV 4K&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Samsung Tizen
&lt;/h2&gt;

&lt;p&gt;ACR is called "Viewing Information Services" — not ACR — which is why most users never find it. It's buried under &lt;strong&gt;Settings → Support → Terms &amp;amp; Policy&lt;/strong&gt;, not in the Privacy menu.&lt;/p&gt;

&lt;p&gt;Texas obtained a temporary restraining order against Samsung specifically. The court agreed there was sufficient reason to pause data collection while the lawsuit proceeds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disable it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Support → Terms &amp;amp; Policy → Viewing Information Services → Off
Also disable: Interest-Based Advertisement Services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LG webOS
&lt;/h2&gt;

&lt;p&gt;LG's ACR is called "Live Plus." It defaults to on and tracks content across HDMI inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disable it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → All Settings → General → Live Plus → Off
Settings → All Settings → General → About This TV → User Agreements → Viewing Information → Off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two separate settings, not one. Missing the second one still leaves partial tracking enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roku
&lt;/h2&gt;

&lt;p&gt;Roku is the most transparent about ACR — its published documentation explicitly states that collected data is shared with third parties and that previously collected data is retained even if you disable the feature later.&lt;/p&gt;

&lt;p&gt;Disabling ACR on Roku stops HDMI-based tracking but doesn't affect data collected from Roku's own streaming channels. That requires separate steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disable it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Privacy → Smart TV Experience → Use Info from TV Inputs → uncheck
Settings → Privacy → Advertising → Limit Ad Tracking → on
Settings → Privacy → Privacy Choices → disable data sharing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Google TV (Sony, TCL, Philips, Hisense)
&lt;/h2&gt;

&lt;p&gt;Google's platform doesn't use ACR directly. But the individual TV brand (Sony, TCL, etc.) may add its own ACR layer separately. And Google's own data collection from the platform — searches, YouTube viewing, app usage — feeds into the same ad profile used across all your Google-connected devices. There's no opting out of Google's core policies if you want smart TV functionality.&lt;/p&gt;

&lt;p&gt;Check for brand-specific ACR under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Device Preferences → Samba Interactive TV → Disable
or
Settings → Display &amp;amp; Sound → Intelligent Settings → all off
Settings → Privacy → Usage &amp;amp; Diagnostics → off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Amazon Fire TV
&lt;/h2&gt;

&lt;p&gt;Amazon has publicly confirmed it doesn't use ACR to track content from HDMI-connected devices. That's the key differentiator from every other platform in this list.&lt;/p&gt;

&lt;p&gt;It still collects data on what you watch through antenna inputs and Fire TV streaming apps. But the absence of HDMI ACR tracking is a meaningful privacy win.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tighten it further:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Preferences → Privacy Settings → Collect App and Over-the-Air Usage Data → off
Settings → Preferences → Privacy Settings → Interest-Based Ads → off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Apple TV 4K
&lt;/h2&gt;

&lt;p&gt;No ACR. Full stop. Apple has confirmed this, and independent research supports it.&lt;/p&gt;

&lt;p&gt;Apple does collect some usage data within its own apps, but processes it through differential privacy — anonymized in aggregate before use. Third-party apps must explicitly request tracking permission under tvOS, which you can block globally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Privacy → Tracking → Allow Apps to Request to Track → off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One caveat: Apple TV is a streaming box, not a TV. If the physical TV it's plugged into has ACR enabled, that TV's OS is still screenshotting whatever Apple TV displays. The correct setup is Apple TV as your streaming device &lt;em&gt;plus&lt;/em&gt; ACR disabled on the TV itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part Most Guides Miss
&lt;/h2&gt;

&lt;p&gt;Disabling ACR at the OS level doesn't stop individual streaming apps from tracking you — Netflix, YouTube, and every other app collect data separately under their own policies. You need to treat the TV's OS and each app as separate tracking systems.&lt;/p&gt;

&lt;p&gt;Also: firmware updates on Samsung, LG, and Roku have been documented to reset privacy settings to their defaults. Set a reminder to recheck after every major update.&lt;/p&gt;




&lt;p&gt;The practical hierarchy if privacy matters to you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Apple TV 4K&lt;/strong&gt; — no ACR, anonymized data, explicit tracking consent model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Fire TV&lt;/strong&gt; — no HDMI ACR, still collects streaming data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google TV&lt;/strong&gt; — platform ACR-free, but brand ACR and Google's data model apply&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roku&lt;/strong&gt; — ACR on by default, unusually transparent documentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LG webOS&lt;/strong&gt; — ACR on by default, requires two separate settings to disable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Samsung Tizen&lt;/strong&gt; — ACR on by default, buried deepest in legal menus&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Full breakdown with per-brand step-by-step settings: &lt;a href="https://lucas8.com/smart-tv-acr-privacy-comparison" rel="noopener noreferrer"&gt;https://lucas8.com/smart-tv-acr-privacy-comparison&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>Your Baby Monitor's Biggest Security Flaw Isn't Hackers. It's the Company That Built It.</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Thu, 25 Jun 2026 16:50:56 +0000</pubDate>
      <link>https://dev.to/spicykim/your-baby-monitors-biggest-security-flaw-isnt-hackers-its-the-company-that-built-it-5hbj</link>
      <guid>https://dev.to/spicykim/your-baby-monitors-biggest-security-flaw-isnt-hackers-its-the-company-that-built-it-5hbj</guid>
      <description>&lt;p&gt;In May 2026, a French ethical hacker named Sammy Azdoufal bought a baby monitor off Amazon and spent a few hours looking at its network traffic. What he found: 1.1 million cameras across 300+ brand names, all running on the same shared platform, accessible to anyone with a free account. No password cracking. No exploit chain. He clicked a URL and got the image.&lt;/p&gt;

&lt;p&gt;The vulnerability wasn't a clever attack. It was negligence — hardcoded credentials, an MQTT broker with no per-device access controls, and motion-alert images sitting on an Alibaba OSS bucket with no authentication required.&lt;/p&gt;

&lt;p&gt;This is the actual baby monitor security problem. Not a stranger breaking in through your Wi-Fi. The architecture itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The White-Label Problem
&lt;/h2&gt;

&lt;p&gt;Most budget smart cameras on Amazon are the same product under different names.&lt;/p&gt;

&lt;p&gt;Meari Technology (Hangzhou, China) supplies hardware, software, and cloud infrastructure to 300+ brands. When you buy a monitor you've never heard of — or even some you have — there's a real chance it shares a backend platform with hundreds of other products. The box doesn't disclose which cloud it connects to. The app is often interchangeable across brands.&lt;/p&gt;

&lt;p&gt;A flaw at the platform level means millions of devices are exposed simultaneously, regardless of brand name.&lt;/p&gt;

&lt;p&gt;Rapid7 tested nine popular baby monitors and gave eight of them an "F" for security. Higher prices didn't correlate with better security — more features meant more attack surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Encrypted" Actually Means Here
&lt;/h2&gt;

&lt;p&gt;Many monitors market end-to-end encryption. Most don't implement it correctly.&lt;/p&gt;

&lt;p&gt;Real E2E encryption: only your device and your app can decrypt the stream — not the manufacturer's servers.&lt;/p&gt;

&lt;p&gt;What most Wi-Fi monitors actually do: encrypt the leg from your camera to their servers, and the leg from their servers to your app. The manufacturer sees plaintext at the server level.&lt;/p&gt;

&lt;p&gt;The Meari MQTT broker (CVE-2026-33356) let any authenticated platform account subscribe to camera activity across all devices on a regional broker. Azdoufal observed 2,000+ device messages within minutes from a single broker endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Reduces Risk
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Network segmentation&lt;/strong&gt; — most underused, highest impact. Put your baby monitor on a guest Wi-Fi VLAN, isolated from your main devices. A compromised monitor can't pivot to your laptop or NAS. Takes 5 minutes on most home routers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firmware updates over passwords&lt;/strong&gt; — a strong password won't protect you if the backend has an unpatched CVE. Enable auto-updates or check monthly. This matters more than password complexity on a vulnerable platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brand selection criteria that actually signal security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Published vulnerability disclosure program or bug bounty&lt;/li&gt;
&lt;li&gt;Documented patch history (not just "we take security seriously")&lt;/li&gt;
&lt;li&gt;Privacy policy that specifies data collected during idle operation — not just when you're actively viewing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What doesn't move the needle as much:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong passwords alone on a platform-level vulnerability&lt;/li&gt;
&lt;li&gt;"No cloud" marketing that still phones home for firmware and analytics&lt;/li&gt;
&lt;li&gt;Price as a proxy for security&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Regulatory Gap
&lt;/h2&gt;

&lt;p&gt;The FCC's US Cyber Trust Mark — a voluntary IoT security labeling program — is still in development. "Good cybersecurity is invisible to consumers. They can't tell what products are risky," Stacey Higginbotham of Consumer Reports said following the Meari disclosure.&lt;/p&gt;

&lt;p&gt;Until mandatory baseline security requirements exist for IoT devices sold in the US, the research burden falls on buyers. Consumer Reports now runs security and privacy evaluations on baby monitors specifically — worth checking before purchase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Checklist
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Monitor is on a dedicated guest/IoT VLAN
[ ] Firmware auto-updates enabled (or manual check set monthly)
[ ] Default credentials changed immediately on setup
[ ] Brand has published vulnerability disclosure program
[ ] Privacy policy reviewed for third-party data sharing
[ ] App permissions audited (disable anything not needed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Meari disclosure made the problem concrete at a scale that's hard to ignore. 1.1 million cameras. Five critical CVEs. Footage of children's bedrooms accessible to anyone with a free account and a few minutes.&lt;/p&gt;

&lt;p&gt;That's not a hacking story. It's a product design story.&lt;/p&gt;




&lt;p&gt;Full breakdown: &lt;a href="https://lucas8.com/baby-monitor-privacy-risks" rel="noopener noreferrer"&gt;https://lucas8.com/baby-monitor-privacy-risks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>Two-Factor Auth Isn't the Shield You Think It Is.</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Mon, 22 Jun 2026 13:58:28 +0000</pubDate>
      <link>https://dev.to/spicykim/two-factor-auth-isnt-the-shield-you-think-it-is-5c77</link>
      <guid>https://dev.to/spicykim/two-factor-auth-isnt-the-shield-you-think-it-is-5c77</guid>
      <description>&lt;p&gt;You enabled two-factor authentication. Good call. But here's what most security guides skip: the biggest account takeovers of the past three years didn't require attackers to crack your 2FA code at all. They found ways to make you hand it over — or made the authentication step irrelevant entirely.&lt;/p&gt;

&lt;p&gt;MFA fatigue attacks are now one of the most documented techniques in real breaches, and they work precisely because 2FA gave everyone a false sense of being done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 2FA Still Gets Beaten in 2026
&lt;/h2&gt;

&lt;p&gt;Traditional 2FA was designed to stop credential stuffing — someone stealing your password and trying to log in. For that specific threat, it works brilliantly. Microsoft's data shows MFA blocks over 99% of automated credential attacks.&lt;/p&gt;

&lt;p&gt;The problem is the threat didn't stay still. Attackers noticed 2FA made the password less valuable, so they shifted focus: steal the session, exhaust the user, or reroute the auth flow entirely. These aren't exotic techniques. They show up in the Verizon DBIR every year, and the companies hit aren't careless ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Techniques Hackers Use to Walk Past MFA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. MFA Fatigue (Push Bombing)
&lt;/h3&gt;

&lt;p&gt;An attacker who has your username and password triggers your push notification repeatedly — sometimes dozens of times overnight. Most people eventually tap "Approve" to make it stop. That single tap hands over a valid session token.&lt;/p&gt;

&lt;p&gt;This exact method took down Uber in 2022. The attacker bought leaked credentials, bombed the contractor's phone with push requests, then sent a WhatsApp message posing as Uber IT saying "approve once and it'll stop." The contractor did. Game over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation:&lt;/strong&gt; Enable number matching on push apps. You type a two-digit code shown on your screen into the push prompt — blind approvals stop working immediately. Microsoft Authenticator and Duo both support this and Microsoft enabled it by default across Entra ID in 2023.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Adversary-in-the-Middle (AiTM) Attacks
&lt;/h3&gt;

&lt;p&gt;An attacker sets up a proxy that looks identical to a real login page. When you log in through it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The proxy relays your credentials to the real site&lt;/li&gt;
&lt;li&gt;The real site sends back a 2FA challenge — the proxy relays that too&lt;/li&gt;
&lt;li&gt;You enter your code — the proxy captures your authenticated &lt;strong&gt;session cookie&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The attacker replays that cookie from a clean browser, no further auth required
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You → [Fake Proxy] → Real Site
             ↓
      Session Cookie stolen
             ↓
Attacker → [Replays Cookie] → Logged In
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The session cookie is the key. Your 2FA was used — legitimately — to create a session the attacker now owns. The Verizon 2025 DBIR flags stolen session tokens as a growing proportion of breach vectors for exactly this reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation:&lt;/strong&gt; Only passkeys and hardware security keys are immune to this. They use cryptographic proofs tied to the exact origin domain — a proxy gets a different challenge and the login fails silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. SIM Swapping
&lt;/h3&gt;

&lt;p&gt;An attacker calls your carrier posing as you — armed with info from data brokers or social media — and convinces a rep to transfer your number to a SIM they control. From that point, every SMS code sent to your phone goes to them instead.&lt;/p&gt;

&lt;p&gt;No malware. Nothing on your device. The FTC has received thousands of SIM swap reports annually since 2021.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation:&lt;/strong&gt; Move off SMS 2FA entirely for anything financial or high-value. Authenticator apps (Google Authenticator, Aegis, Authy) generate codes locally on your device — a SIM swap can't intercept them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2FA Method Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Stops Credential Stuffing&lt;/th&gt;
&lt;th&gt;Stops Push Bombing&lt;/th&gt;
&lt;th&gt;Stops AiTM&lt;/th&gt;
&lt;th&gt;Stops SIM Swap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SMS OTP&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authenticator App (TOTP)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Push (standard)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Push + Number Matching&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware Key / Passkey&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What "Phishing-Resistant" Actually Means
&lt;/h2&gt;

&lt;p&gt;Phishing-resistant MFA uses public-key cryptography where your device proves it's physically present at the &lt;strong&gt;real&lt;/strong&gt; domain. There's no code to intercept, no push to trick you into approving, no SMS to redirect.&lt;/p&gt;

&lt;p&gt;Both hardware security keys (YubiKey etc.) and passkeys qualify. FIDO Alliance reports 5 billion active passkeys worldwide as of 2026, and 68% of organizations surveyed are actively deploying them for employee sign-in. Google, Apple, and Microsoft have all made passkeys the default for new accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Practical Upgrade Path
&lt;/h2&gt;

&lt;p&gt;You don't need to switch everything at once:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This week:&lt;/strong&gt; Remove SMS 2FA from any financial account. Switch to an authenticator app — takes ~30 seconds per account in settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This month:&lt;/strong&gt; Enable number matching on any push-based MFA app. Check your organization's Entra ID or Okta settings to confirm it's on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When ready:&lt;/strong&gt; Add a hardware key ($25–55 for a basic YubiKey) for email, password manager, and any admin access. Set up passkeys on Google, Apple, and GitHub — each takes under three minutes.&lt;/p&gt;




&lt;p&gt;The goal isn't to scare you off 2FA — it's to make sure what you're running matches the current threat, not the threat from 2019. Getting off SMS and onto an authenticator app this week already moves you out of the most exploited tier.&lt;/p&gt;

&lt;p&gt;Full breakdown with real breach examples: &lt;a href="https://lucas8.com/mfa-fatigue-attack-two-factor-bypass" rel="noopener noreferrer"&gt;https://lucas8.com/mfa-fatigue-attack-two-factor-bypass&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>security</category>
    </item>
    <item>
      <title>Browser Fingerprinting in Practice — The Signals, the Math, and What Actually Defeats It</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Thu, 18 Jun 2026 16:38:31 +0000</pubDate>
      <link>https://dev.to/spicykim/browser-fingerprinting-in-practice-the-signals-the-math-and-what-actually-defeats-it-15k0</link>
      <guid>https://dev.to/spicykim/browser-fingerprinting-in-practice-the-signals-the-math-and-what-actually-defeats-it-15k0</guid>
      <description>&lt;p&gt;Most privacy advice still centers on cookies — clear them, block them, use incognito. Meanwhile, fingerprinting has become the dominant tracking method precisely because it doesn't touch cookies at all. Here's what's actually happening at the API level, and what countermeasures hold up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Signals (And the Code Behind Them)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Canvas fingerprinting&lt;/strong&gt; exploits subtle rendering differences between GPU/driver combinations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCanvasFingerprint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Draw text with specific font, size, and emoji — &lt;/span&gt;
  &lt;span class="c1"&gt;// rendering varies by OS font rasterizer and GPU&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textBaseline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;14px Arial&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f60&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;125&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#069&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cwm fjordbank glyphs vext quiz 🎮&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(102, 204, 0, 0.7)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cwm fjordbank glyphs vext quiz 🎮&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Hash the resulting pixel data&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Hash the output for compact comparison&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hashFingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHA-256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hashBuffer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same code produces different pixel-level output across GPU vendors (NVIDIA vs AMD vs Apple Silicon), driver versions, and even font hinting settings — none of which the user can see, but all of which produce a consistent hash per device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebGL fingerprinting&lt;/strong&gt; goes deeper into hardware identification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWebGLFingerprint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webgl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;experimental-webgl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debugInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WEBGL_debug_renderer_info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debugInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNMASKED_VENDOR_WEBGL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debugInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNMASKED_RENDERER_WEBGL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// e.g. "ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11..."&lt;/span&gt;
    &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSupportedExtensions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;maxTextureSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAX_TEXTURE_SIZE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This often reveals your exact GPU model, which on its own significantly narrows the population of matching devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AudioContext fingerprinting&lt;/strong&gt; uses hardware-dependent audio processing variance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAudioFingerprint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AudioContext&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webkitAudioContext&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oscillator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createOscillator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;analyser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createAnalyser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gainNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;audioCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createGain&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;gainNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// silent — user hears nothing&lt;/span&gt;
  &lt;span class="nx"&gt;oscillator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;triangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;oscillator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;analyser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;analyser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gainNode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;gainNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;oscillator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Float32Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;analyser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequencyBinCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;analyser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFloatFrequencyData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;oscillator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// sample of frequency data&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Font enumeration&lt;/strong&gt; via measurement comparison (no direct font list API exists, so it's inferred):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;detectFonts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testFonts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseFonts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monospace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mmmmmmmmmmlli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;72px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseWidths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="nx"&gt;baseFonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontFamily&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;baseWidths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testFonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;baseFonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontFamily&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`'&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;', &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;baseWidths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;detected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Composite Scoring — How These Combine
&lt;/h2&gt;

&lt;p&gt;A single signal rarely identifies anyone uniquely. The entropy comes from combining them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal_distribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Shannon entropy in bits — higher = more identifying
    e.g. if 1 in 1000 users share your exact value, 
    that signal contributes ~10 bits of entropy
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal_distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;signal_distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
        &lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;entropy&lt;/span&gt;

&lt;span class="c1"&gt;# Example combined fingerprint entropy
&lt;/span&gt;&lt;span class="n"&gt;signals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;screen_resolution&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# bits
&lt;/span&gt;    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;canvas_hash&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;8.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;webgl_renderer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;6.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;font_list&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timezone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;audio_fingerprint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;total_entropy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# ~32.3 bits
# 2^32.3 ≈ 5.3 billion possible combinations
# Global population ~8 billion — this fingerprint 
# alone approaches unique identification
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why the &lt;a href="https://coveryourtracks.eff.org/" rel="noopener noreferrer"&gt;EFF Panopticlick research&lt;/a&gt; consistently found most tested browsers had fingerprints unique among hundreds of thousands of samples — the combinatorial entropy adds up fast even when no individual signal is rare.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Reduces Entropy (Tested)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Firefox &lt;code&gt;privacy.resistFingerprinting&lt;/code&gt;&lt;/strong&gt; standardizes the highest-entropy signals:&lt;br&gt;
Testing before/after on the same machine with &lt;a href="https://amiunique.org/" rel="noopener noreferrer"&gt;amiunique.org&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Default Firefox&lt;/th&gt;
&lt;th&gt;resistFingerprinting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Canvas&lt;/td&gt;
&lt;td&gt;Unique hash&lt;/td&gt;
&lt;td&gt;Blocked/randomized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timezone&lt;/td&gt;
&lt;td&gt;Local (e.g. PST)&lt;/td&gt;
&lt;td&gt;UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen resolution&lt;/td&gt;
&lt;td&gt;Exact (e.g. 1512x982)&lt;/td&gt;
&lt;td&gt;Rounded (1500x950)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts detected&lt;/td&gt;
&lt;td&gt;40+ system fonts&lt;/td&gt;
&lt;td&gt;~12 bundled fonts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebGL renderer&lt;/td&gt;
&lt;td&gt;Full GPU string&lt;/td&gt;
&lt;td&gt;Generic string&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The tradeoff: sites relying on accurate viewport dimensions for layout can render incorrectly, and some canvas-dependent web apps (image editors, games) break entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brave's fingerprinting protection&lt;/strong&gt; takes a different approach — randomizing per-session rather than blocking:&lt;br&gt;
&lt;strong&gt;Tor Browser&lt;/strong&gt; standardizes nearly everything to a single shared profile across all users, which is the only approach that achieves near-zero fingerprint uniqueness — at the cost of significant performance and compatibility overhead.&lt;/p&gt;


&lt;h2&gt;
  
  
  Detection-Side: If You're Building Anti-Fraud Systems
&lt;/h2&gt;

&lt;p&gt;For legitimate use cases (fraud detection, not tracking users for ads), fingerprinting libraries like FingerprintJS provide production-ready implementations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;FingerprintJS&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@fingerprintjs/fingerprintjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fpPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FingerprintJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fpPromise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visitorId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// stable identifier&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 0-1 reliability&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worth noting: privacy-focused browsers and extensions specifically target known fingerprinting libraries, so production fraud detection systems increasingly see degraded signal quality from privacy-conscious users — which itself becomes a (weaker) signal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Takeaway
&lt;/h2&gt;

&lt;p&gt;Cookie-based privacy controls (clearing cookies, incognito mode, cookie blockers) have zero effect on any of the above. If you're building privacy-respecting infrastructure or just hardening your own setup, the signals that matter are canvas, WebGL, audio context, and font enumeration — and the only consistently effective countermeasures are resistFingerprinting-style signal normalization or full standardization (Tor).&lt;/p&gt;

&lt;p&gt;Consumer-level explanation without the code: &lt;a href="https://lucas8.com/incognito-mode-browser-fingerprinting" rel="noopener noreferrer"&gt;lucas8.com/incognito-mode-browser-fingerprinting&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
    <item>
      <title>Data Brokers: What They Collect, How the Industry Works, and How to Opt Out at Scale</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Sun, 14 Jun 2026 15:16:43 +0000</pubDate>
      <link>https://dev.to/spicykim/data-brokers-what-they-collect-how-the-industry-works-and-how-to-opt-out-at-scale-4pg2</link>
      <guid>https://dev.to/spicykim/data-brokers-what-they-collect-how-the-industry-works-and-how-to-opt-out-at-scale-4pg2</guid>
      <description>&lt;p&gt;Most developers know abstractly that data brokers exist. Fewer have actually looked up their own profile and seen what's there — their home address, every previous address, relatives' names and addresses, income estimate, vehicle history, court records, and consumer interest categories.&lt;/p&gt;

&lt;p&gt;Here's how the data pipeline actually works, what a profile contains at the data level, and how to approach opt-outs at scale rather than one form at a time.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Data Broker Pipelines Work
&lt;/h2&gt;

&lt;p&gt;Data brokers aggregate from three primary source categories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public records&lt;/strong&gt; — property records, voter registration, court filings, professional licenses, business registrations, marriage/divorce records, death records. These are legally public in the US and most other countries. Brokers ingest them continuously via bulk data agreements with county, state, and federal agencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commercial data&lt;/strong&gt; — purchase history from retailers (via loyalty programs and direct sales), subscription records, warranty registrations, financial transaction metadata (purchased from banks and credit card processors), insurance records, telecommunications data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party data&lt;/strong&gt; — scraped from social media and public web, purchased from other data brokers (the industry extensively resells to itself), purchased from app developers who include data-sharing SDKs.&lt;/p&gt;

&lt;p&gt;The aggregation logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified version of identity resolution logic
# (what brokers call "entity resolution" or "data matching")
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PersonProfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Match records across sources using probabilistic 
    identity resolution — name + address + DOB + phone
    combinations weighted by confidence score
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;clusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;identity_match_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PersonCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_profile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;clusters&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;identity_match_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;fuzzy_name_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dob&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why a data broker profile contains people you've lived with — shared address history creates a probabilistic link that their systems treat as a relationship signal.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Actually in a Profile (Data Schema)
&lt;/h2&gt;

&lt;p&gt;A typical commercial data broker profile at the API level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"person"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"names"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Jane Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane A. Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Adams"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dob_range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1985-01-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1985-12-31"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phones"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+15559876543"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"emails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"jane@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jane.smith@oldwork.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"locations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123 Main St, Austin TX 78701"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"current"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"date_range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-03"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"present"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"456 Oak Ave, Denver CO 80203"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"previous"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"date_range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2018-06"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-02"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"associates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Robert Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"relationship"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"relative"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"shared_addresses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St, Austin TX 78701"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"financials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"income_estimate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;75000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"net_worth_estimate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;150000&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"homeowner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"property_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;385000&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"records"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"criminal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"civil"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bankruptcies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"liens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"consumer_segments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"health_conscious_shopper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"frequent_traveler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="s2"&gt;"suburban_homeowner"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"political_donor_democrat"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;consumer_segments&lt;/code&gt; field is the advertising product — these interest/demographic categories are what marketers buy. The address and associate data is what stalkers, scammers, and PI firms buy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Opt-Out Landscape
&lt;/h2&gt;

&lt;p&gt;There are approximately 4,000 data brokers. Manually opting out of each one is not realistic. The practical approach is tiered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 1 — High-traffic consumer-facing sites (manual opt-out, highest priority)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Site&lt;/th&gt;
&lt;th&gt;Opt-Out URL&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;TTL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spokeo&lt;/td&gt;
&lt;td&gt;spokeo.com/optout&lt;/td&gt;
&lt;td&gt;Email form&lt;/td&gt;
&lt;td&gt;~3-6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhitePages&lt;/td&gt;
&lt;td&gt;whitepages.com/suppression_requests&lt;/td&gt;
&lt;td&gt;Web form&lt;/td&gt;
&lt;td&gt;~3-6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BeenVerified&lt;/td&gt;
&lt;td&gt;beenverified.com/opt-out&lt;/td&gt;
&lt;td&gt;Web form&lt;/td&gt;
&lt;td&gt;~3-6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MyLife&lt;/td&gt;
&lt;td&gt;mylife.com&lt;/td&gt;
&lt;td&gt;Phone call required&lt;/td&gt;
&lt;td&gt;~3-6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Radaris&lt;/td&gt;
&lt;td&gt;radaris.com/page/privacy&lt;/td&gt;
&lt;td&gt;Email form&lt;/td&gt;
&lt;td&gt;~3-6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intelius&lt;/td&gt;
&lt;td&gt;intelius.com/optout&lt;/td&gt;
&lt;td&gt;Web form&lt;/td&gt;
&lt;td&gt;~3-6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;TTL = time before listing typically reappears from re-aggregation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 2 — Automated opt-out via paid services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Services like DeleteMe, Incogni, and Privacy Bee submit opt-outs across 100–750 brokers and resubmit on a schedule. Worth the cost if you're doing this for yourself or building it into a product for users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 3 — Enterprise data brokers (requires legal process)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Acxiom, LexisNexis, CoreLogic, Equifax (non-credit), TransUnion Marketing — these serve enterprise customers and have different opt-out mechanisms. Acxiom has an opt-out at aboutthedata.com. LexisNexis requires a written request with ID verification. California CCPA requests get the fastest response for these sources.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automating Opt-Out Submissions
&lt;/h2&gt;

&lt;p&gt;For the manual tier, the process is repetitive and automatable for the sites that use web forms rather than email or phone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Playwright automation for form-based opt-outs&lt;/span&gt;
&lt;span class="c1"&gt;// (Shown for educational purposes — &lt;/span&gt;
&lt;span class="c1"&gt;//  check each site's ToS before automating)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;submitOptOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profileUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;spokeo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.spokeo.com/optout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#profile_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profileUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[type="submit"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;radaris&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://radaris.com/page/privacy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="email"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="url"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profileUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.submit-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Rate limit to avoid triggering bot detection&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;batchOptOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;profiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;submitOptOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main friction points: CAPTCHA on some forms, email confirmation required on most, and a few sites require the user to find their own profile URL first (can't just submit a name).&lt;/p&gt;




&lt;h2&gt;
  
  
  CCPA as a Lever
&lt;/h2&gt;

&lt;p&gt;For California residents (and US residents targeting California-based brokers), the CCPA gives individuals the right to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Know what data is collected about them&lt;/li&gt;
&lt;li&gt;Request deletion&lt;/li&gt;
&lt;li&gt;Opt out of sale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Submitting a CCPA deletion request often gets faster and more thorough responses than the standard opt-out form, even from brokers that theoretically don't have to respond. Use the Global Privacy Control (GPC) signal in your browser header — it's legally recognized in California and several other states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// GPC header — supported by Firefox and Brave natively&lt;/span&gt;
&lt;span class="c1"&gt;// Can be set programmatically:&lt;/span&gt;
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalPrivacyControl&lt;/span&gt; &lt;span class="c1"&gt;// true if GPC enabled&lt;/span&gt;

&lt;span class="c1"&gt;// For server-side requests:&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sec-GPC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Realistic Picture
&lt;/h2&gt;

&lt;p&gt;Manual opt-outs from the top 10-15 consumer-facing sites takes about 2-3 hours and provides meaningful short-term privacy improvement, particularly for address exposure. The data comes back in 3-6 months.&lt;/p&gt;

&lt;p&gt;The deeper problem is that the legal architecture in the US makes this a whack-a-mole exercise until federal privacy legislation passes. For users who need durable protection — domestic violence survivors, public figures, journalists — the paid services plus CCPA requests plus synthetic identity strategies (PO boxes, registered agents for property) are the more serious toolkit.&lt;/p&gt;

&lt;p&gt;Consumer-facing explanation of the same topic, including the exact opt-out steps for each major site: &lt;a href="https://lucas8.com/data-broker-opt-out-guide" rel="noopener noreferrer"&gt;lucas8.com/data-broker-opt-out-guide&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How AI Phishing Emails Are Built (And the One Pattern That Always Gives Them Away)</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Sun, 14 Jun 2026 14:24:56 +0000</pubDate>
      <link>https://dev.to/spicykim/how-ai-phishing-emails-are-built-and-the-one-pattern-that-always-gives-them-away-2pnm</link>
      <guid>https://dev.to/spicykim/how-ai-phishing-emails-are-built-and-the-one-pattern-that-always-gives-them-away-2pnm</guid>
      <description>&lt;p&gt;Most phishing detection advice is now actively harmful. Teaching users to look for typos and generic greetings made sense when phishing was a spray-and-pray operation running on bad grammar. That era is over.&lt;/p&gt;

&lt;p&gt;Here's how modern AI phishing is actually constructed, what signals remain reliable, and how to implement detection logic that accounts for the current threat model.&lt;/p&gt;




&lt;h2&gt;
  
  
  How an AI Spear Phishing Email Gets Built
&lt;/h2&gt;

&lt;p&gt;The workflow an attacker follows in 2026 is largely automated:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Target reconnaissance&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Typical OSINT data sources for a targeted attack
&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linkedin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;job title, manager name, team structure, tenure&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company_website&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email format (first.last@company.com), press releases&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;social_media&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recent posts, projects mentioned, travel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data_broker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;personal email, phone, home address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;previous_breaches&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password patterns, security question answers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this is publicly available or purchasable. A targeted attack on a finance manager will include their correct name, their CFO's actual name, and may reference a real business event pulled from a press release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Prompt engineering for the attack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The attacker doesn't write the email. They prompt a model:&lt;br&gt;
The output is indistinguishable from a real internal email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lookalike domains are registered with realistic names (&lt;code&gt;company-billing.com&lt;/code&gt;, &lt;code&gt;companyfinance.io&lt;/code&gt;), SSL certificates acquired (free via Let's Encrypt — the padlock means nothing), and emails sent through legitimate SMTP infrastructure to pass basic spam filters.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Traditional Detection Gets Wrong
&lt;/h2&gt;

&lt;p&gt;The signals security training still teaches:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Why It Fails Now&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Typos / bad grammar&lt;/td&gt;
&lt;td&gt;LLMs produce perfect prose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic greeting&lt;/td&gt;
&lt;td&gt;OSINT provides correct names trivially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown sender&lt;/td&gt;
&lt;td&gt;Lookalike domains pass visual inspection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suspicious links&lt;/td&gt;
&lt;td&gt;Links go to legitimate sites that redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Urgency alone&lt;/td&gt;
&lt;td&gt;Legitimate emails also have urgency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these are reliable discriminators in 2026.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Still Works: Authentication Layer Checks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SPF, DKIM, DMARC&lt;/strong&gt; — these operate at the email infrastructure level and can't be faked without compromising the legitimate domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check authentication results for a received email&lt;/span&gt;
&lt;span class="c"&gt;# Look for these headers in the raw message&lt;/span&gt;

&lt;span class="c"&gt;# SPF: did the email originate from an authorized server?&lt;/span&gt;
Received-SPF: pass &lt;span class="o"&gt;(&lt;/span&gt;google.com: domain of cfo@company.com designates 
  198.51.100.1 as permitted sender&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# DKIM: was the email cryptographically signed by the domain?&lt;/span&gt;
DKIM-Signature: &lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rsa-sha256&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;company.com&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;selector1&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;# DMARC: does the domain's policy require both to pass?&lt;/span&gt;
Authentication-Results: mx.google.com&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;dkim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass header.d&lt;span class="o"&gt;=&lt;/span&gt;company.com&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;spf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass smtp.mailfrom&lt;span class="o"&gt;=&lt;/span&gt;company.com&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;dmarc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pass &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;REJECT&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A legitimate internal email from your CFO should pass all three. Any failure is a hard signal — not a soft one. Most attackers can't pass DMARC on the domain they're spoofing without compromising it directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Programmatic header parsing:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Parse authentication results from email headers&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseAuthResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authentication-results&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/spf=&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;pass|fail|softfail|neutral&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;missing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dkim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/dkim=&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;pass|fail|none&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;missing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dmarc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/dmarc=&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;pass|fail|none&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;missing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isAuthenticationSuspicious&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dkim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dmarc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authResults&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Any failure on a supposedly internal or financial email = flag&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;spf&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dkim&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dmarc&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Signal That Doesn't Depend on Content
&lt;/h2&gt;

&lt;p&gt;Authentication checks require access to headers. The signal that works at the human layer — and that AI cannot defeat — is the &lt;strong&gt;request pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Legitimate organizations have consistent behavioral signatures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PHISHING_REQUEST_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wire transfer outside normal approval chain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;request for credentials or MFA codes via email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;urgency to bypass standard process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confidentiality instruction (do not tell X)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new payment method or vendor not in system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;action requested on behalf of unavailable approver&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// The key insight: legitimate urgent requests&lt;/span&gt;
&lt;span class="c1"&gt;// arrive through established channels with context.&lt;/span&gt;
&lt;span class="c1"&gt;// Phishing creates the urgency in the email itself.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern holds regardless of how the email is written. AI can generate perfect prose but cannot change the fact that a real CFO initiating a real wire transfer uses the company's actual payment system, not a direct email to a finance manager with a new bank account.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Detection Heuristic
&lt;/h2&gt;

&lt;p&gt;For teams building email security tooling or internal automation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;phishing_risk_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="c1"&gt;# Authentication failures (high weight)
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dkim&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dmarc&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

    &lt;span class="c1"&gt;# Domain analysis
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_lookalike_domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply_to&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

    &lt;span class="c1"&gt;# Request pattern signals (content analysis)
&lt;/span&gt;    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wire transfer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bank account&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;routing number&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;urgent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;immediately&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;today only&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;close of business&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;keep this confidential&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t mention&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;just between us&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

    &lt;span class="c1"&gt;# High score = route to additional verification, not auto-block
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_lookalike_domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;legitimate_domains&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jellyfish&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jaro_winkler_similarity&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;jaro_winkler_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;legit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt; 
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;legit&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;legit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;legitimate_domains&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key design decision: high-risk emails should trigger an out-of-band verification requirement, not an auto-block. Auto-blocking has false positive costs; requiring phone verification for flagged financial requests has almost none.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Defense That Defeats All Variants
&lt;/h2&gt;

&lt;p&gt;Out-of-band verification: any email requesting financial action, credential changes, or process exceptions gets verified via phone call to a number already on record.&lt;/p&gt;

&lt;p&gt;This rule is architecturally sound because it breaks the attack at the social engineering layer regardless of how convincing the email is. It doesn't matter how good the AI gets at writing emails — it can't intercept a phone call the target initiates to a known number.&lt;/p&gt;

&lt;p&gt;The consumer version of this — what non-technical users should watch for — is at &lt;a href="https://lucas8.com/how-to-spot-ai-phishing-email" rel="noopener noreferrer"&gt;lucas8.com/how-to-spot-ai-phishing-email&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
    </item>
    <item>
      <title>What a VPN Actually Does (And Why Most Devs Use It Wrong)</title>
      <dc:creator>Spicy</dc:creator>
      <pubDate>Thu, 11 Jun 2026 17:02:35 +0000</pubDate>
      <link>https://dev.to/spicykim/what-a-vpn-actually-does-and-why-most-devs-use-it-wrong-8ok</link>
      <guid>https://dev.to/spicykim/what-a-vpn-actually-does-and-why-most-devs-use-it-wrong-8ok</guid>
      <description>&lt;p&gt;Every developer I know has a VPN. Most of them have it running while they're logged into Google, sending data through Chrome, and using apps that do their own certificate pinning — which means the VPN is protecting approximately nothing meaningful in that moment.&lt;/p&gt;

&lt;p&gt;This isn't a knock on VPNs. It's a scoping problem. Here's what the tool actually covers, what leaks around it, and how to test it properly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Actually Happening at the Network Layer
&lt;/h2&gt;

&lt;p&gt;A VPN operates at Layer 3 (Network) of the OSI model. It creates an encrypted tunnel — typically using WireGuard, OpenVPN, or IKEv2/IPSec — between your device and a VPN server. All IP traffic gets routed through that tunnel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protocol comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Security&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WireGuard&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Fast&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Strong&lt;/td&gt;
&lt;td&gt;UDP 51820&lt;/td&gt;
&lt;td&gt;Modern, audited, ~4000 lines of code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenVPN&lt;/td&gt;
&lt;td&gt;⭐⭐ Medium&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Strong&lt;/td&gt;
&lt;td&gt;TCP 443 / UDP 1194&lt;/td&gt;
&lt;td&gt;Battle-tested, ~100k lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IKEv2/IPSec&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Fast&lt;/td&gt;
&lt;td&gt;⭐⭐ Good&lt;/td&gt;
&lt;td&gt;UDP 500/4500&lt;/td&gt;
&lt;td&gt;Native on mobile, good reconnect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L2TP/IPSec&lt;/td&gt;
&lt;td&gt;⭐ Slow&lt;/td&gt;
&lt;td&gt;⭐ Weak&lt;/td&gt;
&lt;td&gt;UDP 1701&lt;/td&gt;
&lt;td&gt;Avoid — potentially compromised&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;WireGuard is the correct choice in 2026 unless you have a specific reason not to use it. Smaller codebase = smaller attack surface. Most audited VPN providers now use it by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Leaks Around the Tunnel
&lt;/h2&gt;

&lt;p&gt;The VPN handles IP routing. It doesn't handle everything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DNS leaks&lt;/strong&gt; are the most common issue. If your system's DNS resolver isn't explicitly routed through the tunnel, your DNS queries go directly to your ISP — revealing every domain you visit even with the VPN active.&lt;/p&gt;

&lt;p&gt;Test this from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check your current DNS resolver&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf

&lt;span class="c"&gt;# Test for DNS leak (run while VPN is active)&lt;/span&gt;
&lt;span class="c"&gt;# Should show your VPN provider's DNS, not your ISP's&lt;/span&gt;
nslookup whoami.akamai.net

&lt;span class="c"&gt;# More thorough test&lt;/span&gt;
dig +short myip.opendns.com @resolver1.opendns.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most reputable VPN clients handle DNS routing automatically, but worth verifying — especially on Linux where DNS management is fragmented across systemd-resolved, dnsmasq, and NetworkManager depending on your distro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebRTC leaks&lt;/strong&gt; expose your real IP through browser APIs even when a VPN is active. This is a browser-layer problem, not a network-layer problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WebRTC leak test — run in browser console while on VPN&lt;/span&gt;
&lt;span class="c1"&gt;// If this returns your real IP, you have a WebRTC leak&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RTCPeerConnection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;iceServers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stun:stun.l.google.com:19302&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDataChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createOffer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLocalDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onicecandidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IP exposed via WebRTC:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix: Firefox has &lt;code&gt;media.peerconnection.enabled&lt;/code&gt; in &lt;code&gt;about:config&lt;/code&gt;. Chrome requires an extension or a VPN client with WebRTC leak protection built in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application-layer tracking&lt;/strong&gt; doesn't touch the network layer at all. If you're authenticated in your browser, that session follows you. Cookies, localStorage, IndexedDB — none of this is affected by routing your IP through Amsterdam.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Kill Switch and Why It Matters
&lt;/h2&gt;

&lt;p&gt;A kill switch blocks all traffic if the VPN connection drops. Without it, your traffic briefly reverts to your real IP when the tunnel reconnects. This is the correct default for any privacy-sensitive setup.&lt;/p&gt;

&lt;p&gt;On Linux with ufw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Block all traffic by default&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default deny outgoing
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default deny incoming

&lt;span class="c"&gt;# Allow only traffic through VPN interface (tun0 for OpenVPN, wg0 for WireGuard)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow out on tun0
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow out on wg0

&lt;span class="c"&gt;# Allow LAN traffic if needed&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow out on eth0 to 192.168.0.0/16
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow out on eth0 to 10.0.0.0/8

&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WireGuard users can use &lt;code&gt;PostUp&lt;/code&gt;/&lt;code&gt;PreDown&lt;/code&gt; hooks in the config for a more integrated approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Interface]&lt;/span&gt;
&lt;span class="py"&gt;PrivateKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;your_private_key&amp;gt;&lt;/span&gt;
&lt;span class="py"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.x.x.x/32&lt;/span&gt;
&lt;span class="py"&gt;DNS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1.1.1.1&lt;/span&gt;

&lt;span class="py"&gt;PostUp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;iptables -I OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -j REJECT&lt;/span&gt;
&lt;span class="py"&gt;PreDown&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;iptables -D OUTPUT ! -o wg0 -m mark ! --mark $(wg show wg0 fwmark) -j REJECT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Split Tunneling: Route Only What You Need
&lt;/h2&gt;

&lt;p&gt;Split tunneling lets you route specific traffic through the VPN while everything else goes directly. Useful when you need VPN for specific services but don't want to tunnel your local development traffic or internal network requests.&lt;/p&gt;

&lt;p&gt;Most GUI clients support this natively. For WireGuard directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Peer]&lt;/span&gt;
&lt;span class="py"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;server_public_key&amp;gt;&lt;/span&gt;
&lt;span class="py"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;server_ip&amp;gt;:51820&lt;/span&gt;

&lt;span class="c"&gt;# Route only specific subnets through VPN instead of all traffic
&lt;/span&gt;&lt;span class="py"&gt;AllowedIPs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.0.0.0/8, 172.16.0.0/12&lt;/span&gt;

&lt;span class="c"&gt;# vs. route everything:
# AllowedIPs = 0.0.0.0/0, ::/0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What a VPN Actually Buys You (Honest Summary)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Your ISP can't see which domains you visit&lt;/li&gt;
&lt;li&gt;✅ Traffic encrypted against public WiFi eavesdropping&lt;/li&gt;
&lt;li&gt;✅ Your IP hidden from destination servers&lt;/li&gt;
&lt;li&gt;✅ DNS queries protected (if configured correctly)&lt;/li&gt;
&lt;li&gt;❌ No protection against cookie/session tracking&lt;/li&gt;
&lt;li&gt;❌ No protection against browser fingerprinting&lt;/li&gt;
&lt;li&gt;❌ No protection while authenticated in any app or service&lt;/li&gt;
&lt;li&gt;❌ No protection from the VPN provider themselves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool solves a specific set of network-layer problems. For application-layer privacy, you need application-layer solutions — content blocking, session isolation, fingerprint-resistant browsers.&lt;/p&gt;

&lt;p&gt;If you're setting up a VPN for a team or personal setup and want the technical layer done properly: WireGuard, verified DNS routing, kill switch enabled, and DNS leak tested before you trust it. Everything else is marketing.&lt;/p&gt;




&lt;p&gt;Consumer version — what this means for people who don't want to touch iptables: &lt;a href="https://lucas8.com/what-vpn-actually-protects" rel="noopener noreferrer"&gt;lucas8.com/what-vpn-actually-protects&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
