<?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: Sudip Bhandari</title>
    <description>The latest articles on DEV Community by Sudip Bhandari (@sudeepbhandari).</description>
    <link>https://dev.to/sudeepbhandari</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%2F475903%2F688148db-07dd-45f1-ad82-daaf5ea45ed6.jpg</url>
      <title>DEV Community: Sudip Bhandari</title>
      <link>https://dev.to/sudeepbhandari</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sudeepbhandari"/>
    <language>en</language>
    <item>
      <title>How Zero-Knowledge Encryption Actually Works (with the Web Crypto API)</title>
      <dc:creator>Sudip Bhandari</dc:creator>
      <pubDate>Tue, 14 Jul 2026 01:21:53 +0000</pubDate>
      <link>https://dev.to/sudeepbhandari/how-zero-knowledge-encryption-actually-works-with-the-web-crypto-api-1f53</link>
      <guid>https://dev.to/sudeepbhandari/how-zero-knowledge-encryption-actually-works-with-the-web-crypto-api-1f53</guid>
      <description>&lt;p&gt;&lt;strong&gt;"Zero-knowledge"&lt;/strong&gt; gets thrown around a lot in privacy marketing, but it has a precise, testable meaning: the server operator has no ability to read your data — not as a policy promise, but as a mathematical fact. Let's build the mental model from primitives, using nothing but the browser's native window.crypto.subtle (the Web Crypto API). No third-party crypto libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The threat model
&lt;/h2&gt;

&lt;p&gt;Assume the server is hostile, or will be subpoenaed, or will be breached. A note-taking app that "encrypts at rest" and promises not to peek doesn't survive this model — the operator holds the keys. Zero-knowledge flips it: the key never exists on the server in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Deriving a key from a password (PBKDF2)
&lt;/h2&gt;

&lt;p&gt;Passwords are low-entropy, so you never use them directly as keys. You stretch them with a slow KDF. A production-safe configuration in 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Algorithm: PBKDF2 with SHA-256&lt;/li&gt;
&lt;li&gt;Iterations: 100,000 (raises brute-force cost)&lt;/li&gt;
&lt;li&gt;Salt: 128-bit random per note (defeats rainbow tables)
&lt;/li&gt;
&lt;/ul&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;deriveKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&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;enc&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;keyMaterial&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;importKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;raw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;enc&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;password&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PBKDF2&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deriveKey&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="k"&gt;return&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;deriveKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PBKDF2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;keyMaterial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encrypt&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="s2"&gt;decrypt&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Encrypting with AES-256-GCM
&lt;/h2&gt;

&lt;p&gt;GCM is authenticated encryption: it gives you confidentiality and tamper-detection in one primitive. Generate a fresh 96-bit IV for every encryption so identical plaintext never yields identical ciphertext.&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;encryptNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRandomValues&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="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 128-bit&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRandomValues&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="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 96-bit&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deriveKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&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;ciphertext&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;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;key&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;TextEncoder&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;plaintext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Only these three values leave the browser:&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ciphertext&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 critical line is the last comment. The server receives salt, iv, and ciphertext — three values that are indistinguishable from random bytes without the password. There is no plaintext to leak, subpoena, or sell.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Why this is the only honest form of "private notes"
&lt;/h2&gt;

&lt;p&gt;If a service can read your notes, then "we don't" is a policy that can change with an acquisition, a court order, or a breach. If a service architecturally cannot read your notes, the guarantee holds even when the company doesn't. That's the whole point of a &lt;a href="https://www.securetext.cloud/how-it-works" rel="noopener noreferrer"&gt;zero-knowledge architecture&lt;/a&gt;: compromise of the server yields ciphertext, not content.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Persistent vs. ephemeral
&lt;/h2&gt;

&lt;p&gt;There are two families here. Ephemeral tools (think one-time secret links) destroy the note after a single read — great for handing someone a password once. Persistent zero-knowledge notepads keep the encrypted blob at a stable URL so you can come back to it across devices. They're complementary, not competitors.&lt;/p&gt;

&lt;p&gt;I've been using &lt;a href="https://www.securetext.cloud/" rel="noopener noreferrer"&gt;SecureText, a free encrypted notepad,&lt;/a&gt; as my persistent scratchpad because it implements exactly the flow above — AES-256-GCM, PBKDF2-SHA-256 at 100k iterations, Web Crypto only — and adds developer-friendly touches like multi-tab notes and &lt;a href="https://www.securetext.cloud/features" rel="noopener noreferrer"&gt;custom URL slugs&lt;/a&gt; instead of random IDs. No account, no tracking, and you can inspect the client-side crypto in your own dev tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Never use a password as a key — stretch it with PBKDF2 (or Argon2).&lt;br&gt;
Use authenticated encryption (AES-GCM), fresh IV every time.&lt;br&gt;
If plaintext or keys touch the server, it isn't zero-knowledge.&lt;br&gt;
Audit the client. Open dev tools and confirm what actually gets sent.&lt;br&gt;
If you're building anything that stores user text, run the network tab and ask one question: can the server read this? If yes, your users are trusting you, not math.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Copy source code of any website || Web scraping</title>
      <dc:creator>Sudip Bhandari</dc:creator>
      <pubDate>Mon, 26 Sep 2022 02:00:24 +0000</pubDate>
      <link>https://dev.to/sudeepbhandari/copy-source-code-of-any-website-web-scraping-31m0</link>
      <guid>https://dev.to/sudeepbhandari/copy-source-code-of-any-website-web-scraping-31m0</guid>
      <description>&lt;p&gt;&lt;a href="https://merostudy.xyz/copy-html-css-js-of-any-website/" rel="noopener noreferrer"&gt;https://merostudy.xyz/copy-html-css-js-of-any-website/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I want to rank on my portfolio site. Suggestions please..</title>
      <dc:creator>Sudip Bhandari</dc:creator>
      <pubDate>Sun, 27 Sep 2020 01:06:48 +0000</pubDate>
      <link>https://dev.to/sudeepbhandari/i-want-to-rank-on-my-portfolio-site-suggestions-please-47dk</link>
      <guid>https://dev.to/sudeepbhandari/i-want-to-rank-on-my-portfolio-site-suggestions-please-47dk</guid>
      <description></description>
      <category>rank</category>
      <category>seo</category>
      <category>portfolio</category>
      <category>website</category>
    </item>
  </channel>
</rss>
