<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Gorav Singal</title>
    <description>The latest articles on Forem by Gorav Singal (@goravsingal).</description>
    <link>https://forem.com/goravsingal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F424431%2F51873086-a050-45d4-9f75-b94b16c705da.jpg</url>
      <title>Forem: Gorav Singal</title>
      <link>https://forem.com/goravsingal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/goravsingal"/>
    <language>en</language>
    <item>
      <title>CORS &amp; Same-Origin Policy — The Security Rule Every Developer Gets Wrong</title>
      <dc:creator>Gorav Singal</dc:creator>
      <pubDate>Tue, 21 Apr 2026 13:41:37 +0000</pubDate>
      <link>https://forem.com/goravsingal/cors-same-origin-policy-the-security-rule-every-developer-gets-wrong-48lj</link>
      <guid>https://forem.com/goravsingal/cors-same-origin-policy-the-security-rule-every-developer-gets-wrong-48lj</guid>
      <description>&lt;p&gt;Your fetch call fails. The console screams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access to fetch at 'https://api.example.com' from origin 'https://app.example.com' 
has been blocked by CORS policy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You Google it. Stack Overflow says add &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;. You do. It works. You move on.&lt;/p&gt;

&lt;p&gt;But do you actually know what just happened? And more importantly — did you just open a security hole?&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/E_H89iWyErQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




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

&lt;p&gt;Before we talk CORS, we need to define &lt;strong&gt;origin&lt;/strong&gt;. An origin has three parts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Host&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:443&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;All three must match&lt;/strong&gt; for two URLs to be "same-origin."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com:443/page    ← Reference

https://example.com:443/other   ✅ Same origin (path doesn't matter)
http://example.com:443/page     ❌ Different protocol
https://api.example.com:443     ❌ Different host (subdomains count!)
https://example.com:8080        ❌ Different port
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One mismatch in any of the three — and the browser treats it as a completely different origin. No exceptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Same-Origin Policy (SOP)
&lt;/h2&gt;

&lt;p&gt;The Same-Origin Policy is the &lt;strong&gt;most fundamental security rule&lt;/strong&gt; in the browser. Here's what it does:&lt;/p&gt;

&lt;h3&gt;
  
  
  Blocked by SOP
&lt;/h3&gt;

&lt;p&gt;When JavaScript on origin A tries to &lt;strong&gt;read a response&lt;/strong&gt; from origin B:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fetch()&lt;/code&gt; to another domain — &lt;strong&gt;response blocked&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reading an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; DOM from another origin — &lt;strong&gt;blocked&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Accessing &lt;code&gt;window.opener&lt;/code&gt; DOM cross-origin — &lt;strong&gt;blocked&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Allowed Cross-Origin
&lt;/h3&gt;

&lt;p&gt;But SOP doesn't block everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags load cross-origin images — &lt;strong&gt;allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags load cross-origin scripts — &lt;strong&gt;allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; submissions go cross-origin — &lt;strong&gt;allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the pattern? &lt;strong&gt;SOP blocks &lt;em&gt;reading&lt;/em&gt; cross-origin responses. It does NOT block &lt;em&gt;sending&lt;/em&gt; cross-origin requests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That gap is exactly what CSRF exploits — and what CORS was built to solve.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why CORS Exists
&lt;/h2&gt;

&lt;p&gt;Modern web apps don't live on a single origin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend  →  app.example.com
API       →  api.example.com
CDN       →  cdn.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three different origins. All blocked by SOP. If SOP blocked everything, the modern web wouldn't work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CORS&lt;/strong&gt; (Cross-Origin Resource Sharing) is the controlled exception. It's a protocol that lets the server say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I trust this specific origin. Let their JavaScript read my response."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The server opts in. The browser enforces it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Simple CORS Works
&lt;/h2&gt;

&lt;p&gt;For simple requests — &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;, or &lt;code&gt;POST&lt;/code&gt; with basic headers — no preflight is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Browser sends the request with an &lt;code&gt;Origin&lt;/code&gt; header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/api/data&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
&lt;span class="na"&gt;Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Server responds with &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browser checks: does &lt;code&gt;Origin&lt;/code&gt; match &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match&lt;/strong&gt; → JavaScript can read the response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No match or missing&lt;/strong&gt; → Browser blocks it. CORS error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The request still went through. The server still processed it. But your code can't see the result.&lt;/p&gt;




&lt;h2&gt;
  
  
  Preflight Requests
&lt;/h2&gt;

&lt;p&gt;Not all requests are simple. When you use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, or &lt;code&gt;PATCH&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Custom headers like &lt;code&gt;Authorization&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser sends an &lt;strong&gt;OPTIONS&lt;/strong&gt; request first — the preflight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;OPTIONS&lt;/span&gt; &lt;span class="nn"&gt;/api/data&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
&lt;span class="na"&gt;Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Request-Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PUT&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Request-Headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization, Content-Type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server responds with what it allows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt; &lt;span class="ne"&gt;No Content&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET, PUT, DELETE&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization, Content-Type&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Max-Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;86400&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything checks out, &lt;strong&gt;then&lt;/strong&gt; the browser sends the actual &lt;code&gt;PUT&lt;/code&gt; request. Two round trips instead of one — but the server explicitly opted in to every method and header.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Max-Age: 86400&lt;/code&gt; caches the preflight for 24 hours so subsequent requests skip the OPTIONS call.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Dangerous CORS Misconfigurations
&lt;/h2&gt;

&lt;p&gt;This is where developers get burned.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Wildcard Origin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Access-Control-Allow-Origin: *
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any website can read your API responses. For a truly public API with no auth — maybe fine. But &lt;code&gt;*&lt;/code&gt; &lt;strong&gt;cannot be used with credentials&lt;/strong&gt; (&lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt;). The browser explicitly forbids it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reflecting the Origin (The Dangerous One)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server code — DON'T DO THIS&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&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="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Credentials&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;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whatever origin the request comes from, the server echoes it back. &lt;code&gt;evil.com&lt;/code&gt; sends a request? Server responds with &lt;code&gt;Allow-Origin: evil.com&lt;/code&gt;. With credentials enabled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's a full CORS bypass.&lt;/strong&gt; The attacker's page can now read authenticated responses from your API.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Trusting Null Origin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Access-Control-Allow-Origin: null
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sandboxed iframes and &lt;code&gt;file://&lt;/code&gt; requests send &lt;code&gt;Origin: null&lt;/code&gt;. If your server trusts &lt;code&gt;null&lt;/code&gt;, an attacker can craft a sandboxed iframe that makes authenticated requests to your API.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Explicit Allowlist
&lt;/h2&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;ALLOWED_ORIGINS&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;https://app.example.com&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;https://admin.example.com&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&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="nx"&gt;origin&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;ALLOWED_ORIGINS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Credentials&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;true&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="nf"&gt;next&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;Three rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use an explicit allowlist&lt;/strong&gt; of trusted origins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never reflect the Origin header&lt;/strong&gt; blindly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never trust &lt;code&gt;null&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;One-liner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protocol + Host + Port — all three must match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SOP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser blocks JS from &lt;em&gt;reading&lt;/em&gt; cross-origin responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server opts in with &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GET/POST with basic headers — goes straight through&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preflight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OPTIONS check before complex requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wildcard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*&lt;/code&gt; works for public APIs, forbidden with credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reflected origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Echoing back any origin = full CORS bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Null origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sandboxed iframes exploit this — never trust it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Watch the Full Video
&lt;/h2&gt;

&lt;p&gt;I made an animated video walking through all of this with visual diagrams — origins breaking apart, requests flowing between browser and server, preflight handshakes, and attack scenarios.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/E_H89iWyErQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is part of my &lt;strong&gt;Web Security series&lt;/strong&gt; on &lt;a href="https://www.youtube.com/@GyanByte" rel="noopener noreferrer"&gt;GyanByte&lt;/a&gt;. Previous episodes covered &lt;a href="https://www.youtube.com/watch?v=AXvx7Gzowfs&amp;amp;t=235s" rel="noopener noreferrer"&gt;Authentication&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=fXCSnbEsbuk" rel="noopener noreferrer"&gt;Authorization&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=uVEMmtjeB1w&amp;amp;t=196s" rel="noopener noreferrer"&gt;XSS&lt;/a&gt;, and &lt;a href="https://www.youtube.com/watch?v=Ke7LtAXz6Us&amp;amp;t=1s" rel="noopener noreferrer"&gt;CSRF&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next up: Content Security Policy (CSP)&lt;/strong&gt; — the header that controls what your page can load, run, and connect to.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you been burned by a CORS misconfiguration? Drop your war story in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>websecurity</category>
      <category>appsecurity</category>
      <category>security</category>
      <category>cors</category>
    </item>
    <item>
      <title>Secrets Management — Vault, SSM, and Secrets Manager Compared</title>
      <dc:creator>Gorav Singal</dc:creator>
      <pubDate>Tue, 14 Apr 2026 12:14:15 +0000</pubDate>
      <link>https://forem.com/goravsingal/secrets-management-vault-ssm-and-secrets-manager-compared-4j4j</link>
      <guid>https://forem.com/goravsingal/secrets-management-vault-ssm-and-secrets-manager-compared-4j4j</guid>
      <description>&lt;p&gt;I've watched a production database get wiped because someone committed a root password to a public GitHub repo. It took less than twelve minutes from push to compromise. Automated bots scan every public commit for secrets — and they find them constantly.&lt;/p&gt;

&lt;p&gt;If secrets management isn't the first security problem you solve, nothing else matters. Here's a condensed comparison of the three tools I reach for in practice.&lt;/p&gt;

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

&lt;p&gt;A "secret" is any credential your app needs at runtime but should never be visible in source code, logs, or config files — database passwords, API keys, TLS certs, OAuth tokens.&lt;/p&gt;

&lt;p&gt;The naive approach (env vars, config files in version control) fails because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git history is forever&lt;/strong&gt; — deleting a secret in a later commit doesn't remove it from history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Env vars leak&lt;/strong&gt; — process listings, crash dumps, and logging frameworks routinely expose them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No rotation&lt;/strong&gt; — baked-in secrets mean redeploying everything to rotate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No audit trail&lt;/strong&gt; — you can't tell who accessed what and when&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Three Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. SSM Parameter Store
&lt;/h3&gt;

&lt;p&gt;The simplest option. A key-value store baked into AWS with native IAM integration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm put-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/prod/myapp/db-password"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"s3cureP@ssw0rd!"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; SecureString &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-id&lt;/span&gt; &lt;span class="s2"&gt;"alias/myapp-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hierarchical naming (&lt;code&gt;/prod/myapp/db-password&lt;/code&gt;) maps directly to IAM policies — grant access to &lt;code&gt;/prod/myapp/*&lt;/code&gt; without exposing &lt;code&gt;/prod/billing/*&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use when:&lt;/strong&gt; Simple config and secrets that don't need auto-rotation. Free standard tier covers most teams (up to 10,000 parameters).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AWS Secrets Manager
&lt;/h3&gt;

&lt;p&gt;The killer feature is &lt;strong&gt;built-in automatic rotation&lt;/strong&gt; via Lambda, plus first-class RDS/Redshift/DocumentDB support.&lt;/p&gt;

&lt;p&gt;The rotation flow: a Lambda creates a new credential, sets it as pending, tests it, then promotes it to current. If any step fails, the current secret stays untouched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use when:&lt;/strong&gt; Database credentials needing auto-rotation, versioned secrets, cross-account sharing. $0.40/secret/month.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. HashiCorp Vault
&lt;/h3&gt;

&lt;p&gt;Vault isn't just a secrets store — it's a secrets &lt;em&gt;engine&lt;/em&gt;. It generates &lt;strong&gt;short-lived, on-demand credentials&lt;/strong&gt; for databases, cloud providers, PKI, and SSH.&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;# Get a dynamic credential (valid for 1 hour)&lt;/span&gt;
vault &lt;span class="nb"&gt;read &lt;/span&gt;database/creds/myapp-readonly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every call creates a brand-new database user with a unique password. When the TTL expires, Vault revokes it automatically. No rotation needed — credentials are ephemeral by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use when:&lt;/strong&gt; Multi-cloud environments, dynamic credentials, PKI management, encryption-as-a-service. The tradeoff is operational complexity.&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;Dimension&lt;/th&gt;
&lt;th&gt;SSM Parameter Store&lt;/th&gt;
&lt;th&gt;Secrets Manager&lt;/th&gt;
&lt;th&gt;HashiCorp Vault&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (standard)&lt;/td&gt;
&lt;td&gt;$0.40/secret/month&lt;/td&gt;
&lt;td&gt;Self-hosted or HCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto-Rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual only&lt;/td&gt;
&lt;td&gt;Built-in (Lambda)&lt;/td&gt;
&lt;td&gt;Dynamic secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-Cloud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS only&lt;/td&gt;
&lt;td&gt;AWS only&lt;/td&gt;
&lt;td&gt;Any cloud + on-prem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Secrets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;My rule of thumb:&lt;/strong&gt; Start with SSM. Graduate to Secrets Manager when you need rotation. Move to Vault when you need multi-cloud or dynamic secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secrets in env vars logged to stdout&lt;/strong&gt; — frameworks like Express, Django, and Spring dump env vars in error pages. Use a secrets SDK instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No caching layer&lt;/strong&gt; — calling Secrets Manager on every request adds 5-15ms latency &lt;em&gt;and&lt;/em&gt; costs money. Cache with a 5-minute TTL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Terraform state with plaintext secrets&lt;/strong&gt; — &lt;code&gt;aws_secretsmanager_secret_version&lt;/code&gt; stores values in plaintext in state. Encrypt your state backend (S3 + KMS).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Overly broad IAM policies&lt;/strong&gt; — &lt;code&gt;ssm:GetParameter&lt;/code&gt; on &lt;code&gt;*&lt;/code&gt; means every Lambda reads every secret. Scope to specific paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No secret scanning in CI/CD&lt;/strong&gt; — tools like &lt;code&gt;gitleaks&lt;/code&gt; or GitHub's built-in scanning should be mandatory. The twelve-minute push-to-compromise window is real.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;Never hardcode secrets — not in code, config, Docker images, or logged env vars&lt;/li&gt;
&lt;li&gt;Encrypt at rest (KMS) and in transit (TLS), no exceptions&lt;/li&gt;
&lt;li&gt;Cache aggressively to balance freshness against latency&lt;/li&gt;
&lt;li&gt;Audit everything — if you can't answer "who accessed this at 3am Tuesday," it's incomplete&lt;/li&gt;
&lt;li&gt;Rotate or go ephemeral — long-lived, never-rotated secrets are ticking time bombs&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This is a condensed version. For the full article with complete code examples (Python, Node.js, Terraform), rotation Lambda patterns, and detailed implementation walkthroughs, read the &lt;a href="https://www.gyanbyte.com/Cloud/secrets-management-vault-ssm/" rel="noopener noreferrer"&gt;full post on gyanbyte.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
    </item>
    <item>
      <title>ReactJS - How to restrict data type for different kind of data</title>
      <dc:creator>Gorav Singal</dc:creator>
      <pubDate>Sun, 05 Jul 2020 16:46:31 +0000</pubDate>
      <link>https://forem.com/goravsingal/reactjs-how-to-restrict-data-type-for-different-kind-of-data-2hnm</link>
      <guid>https://forem.com/goravsingal/reactjs-how-to-restrict-data-type-for-different-kind-of-data-2hnm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Javascript is not a strict type language. We can use a variable to save any kind of data. Whether its a string or integer or boolean or object. Well, this gives aa flexibility in using the variabales. But, it brings some complexity of making sure that the data is of certain expected type only.&lt;/p&gt;

&lt;p&gt;For example, you are showing data of students as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Id&lt;/li&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Image&lt;/li&gt;
&lt;li&gt;Age&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from "react";

const Student = ({ name, age, img }) =&amp;gt; {
  return (
    &amp;lt;article&amp;gt;
      &amp;lt;img src={img} alt="student" /&amp;gt;
      &amp;lt;h5&amp;gt;name : {name}&amp;lt;/h5&amp;gt;
      &amp;lt;h5&amp;gt;age : {age}&amp;lt;/h5&amp;gt;
    &amp;lt;/article&amp;gt;
  );
};

class StudentList extends Component {
  state = {
    students: [
      {
        id: 1,
        img: "some img path",
        name: "Raman",
        age: 21
      },
      {
        id: 2,
        img: "Some image path",
        name: "Rahul",
        age: 22
      }
    ]
  };
  render() {
    return (
      &amp;lt;section&amp;gt;
        {this.state.students.map(student =&amp;gt; (
          &amp;lt;Student
            key={student.id}
            img={student.img}
            name={student.name}
            age={student.age}
          /&amp;gt;
        ))}
      &amp;lt;/section&amp;gt;
    );
  }
}

class App extends Component {
  render() {
    return &amp;lt;StudentList /&amp;gt;;
  }
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you messed up somewhere in your data. You might ends up showing age in place of name, or name in place of age. Since, there will be no error or warning unless you are doing some special operation with that variable.&lt;/p&gt;

&lt;p&gt;Lets discuss abaout making our type strict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prop Types
&lt;/h2&gt;

&lt;p&gt;There is a npm module for this purpose: prop-types. To install this module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install prop-types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to use
&lt;/h2&gt;

&lt;p&gt;Lets see the modified component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from "react";
import PropTypes from "prop-types";

const Student = ({ name, age, img }) =&amp;gt; {
  return (
    &amp;lt;article&amp;gt;
      &amp;lt;img src={img} alt="student" /&amp;gt;
      &amp;lt;h5&amp;gt;name : {name}&amp;lt;/h5&amp;gt;
      &amp;lt;h5&amp;gt;age : {age}&amp;lt;/h5&amp;gt;
    &amp;lt;/article&amp;gt;
  );
};
Student.propTypes = {
  name: PropTypes.string,
  age: PropTypes.number,
  img: PropTypes.string
};

class StudentList extends Component {
  state = {
    students: [
      {
        id: 1,
        img: "some img path",
        name: "Raman",
        age: 21
      },
      {
        id: 2,
        img: "Some image path",
        name: "Rahul",
        age: 22
      }
    ]
  };
  render() {
    return (
      &amp;lt;section&amp;gt;
        {this.state.students.map(student =&amp;gt; (
          &amp;lt;Student
            key={student.id}
            img={student.img}
            name={student.name}
            age={student.age}
          /&amp;gt;
        ))}
      &amp;lt;/section&amp;gt;
    );
  }
}

class App extends Component {
  render() {
    return &amp;lt;StudentList /&amp;gt;;
  }
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the usage of propTypes as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student.propTypes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are declaring the data types of certain type of data. And, if we try to provide any other kind of data to such variables, react will complain. It will throw errors. And you will come to know the mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to restrict data to be Required (Not Null)
&lt;/h2&gt;

&lt;p&gt;In many scenarios we would require that the data not be null. With prop-types you can specify or restrict with some attributes that this particular data must be present.&lt;/p&gt;

&lt;p&gt;Lets see how to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired,
  img: PropTypes.string
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Above code will work if you are receiving your values as individual fields. If you are receiving the object, above code will not work. For objects, we have a slightly different way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student.propTypes = {
  student: PropTypes.shape({
    name: PropTypes.string.isRequired,
    age: PropTypes.number.isRequired,
    img: PropTypes.string
  })
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In above code, we have decided that name and age must be present. If our data does not have these fields, then we will get errors.&lt;/p&gt;

&lt;p&gt;Also see &lt;a href="https://www.gyanblog.com/gyan/reactjs-how-create-components/" rel="noopener noreferrer"&gt;How to create ReactJS Components&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to configure default properties
&lt;/h2&gt;

&lt;p&gt;In many cases, our data does not have some of the attributes. Example: Image is not there. And we would want to declare some default values. It means, if our data does not have any value for particular attribute, please use this default value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student.defaultProps = {
  img: "some default image path"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;prop-types does not support Objects for default values.&lt;/p&gt;

&lt;p&gt;Originally posted at: &lt;a href="https://www.gyanblog.com/gyan/reactjs-how-restrict-data-types-data/" rel="noopener noreferrer"&gt;ReactJS - How to restrict data type for different kind of data&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
