<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="378442" data-post-id="378442">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>The masking is needed to avoid the BREACH attack, a variant of the <a href="https://en.wikipedia.org/wiki/CRIME" rel="noopener nofollow ugc">CRIME compression attack</a>. CRIME was an attack on compressed headers and was fixed in HTTP2 with <a href="https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2/" rel="noopener nofollow ugc">the HPACK algorithm</a> which compresses headers in such a way that they are not vulnerable. Unfortunately the CSRF token is present <em>in the body</em> and does not change between requests, meaning it can slowly be revealed if an attacker can tweak the page contents and observe the compression ratio.</p>
<p>Masking literally just <a href="https://github.com/elixir-plug/plug/blob/v1.18.1/lib/plug/csrf_protection.ex#L430" rel="noopener nofollow ugc">generates another token’s worth of bytes</a> as a mask, xors that with the token, and returns the concatenation of the two. This essentially hides (masks) the original token from the compression algorithm, and the mask changes on every request so it cannot be iteratively revealed.</p>
<p>Personally I find the complexity (and therefore bug surface) of CSRF protection quite distressing given that <a href="https://words.filippo.io/csrf/" rel="noopener nofollow ugc">experts believe the token approach is no longer necessary</a>. But of course this is brand new guidance and it will take time for everyone to adapt.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378442" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-378442" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378442"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="378444" data-post-id="378444">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="12" data-topic="31809">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>Personally I find the complexity (and therefore bug surface) of CSRF protection quite distressing given that <a href="https://words.filippo.io/csrf/" rel="nofollow">experts believe the token approach is no longer necessary</a>. But of course this is brand new guidance and it will take time for everyone to adapt.</p>
</blockquote>
</aside>
<p>Reading the article I’m also not sure if this is any less complex tbh.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378444" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-378444" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378444"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="378446" data-post-id="378446">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>That article is a pretty thorough technical deep dive into whether this new approach is <em>secure</em> (and the conclusion is that it is).</p>
<p>In practice the new approach is just comparing a couple of headers sent by the browser, versus creating a magic token (which you have to do very carefully, see above), writing it into the session, and propagating it into every single form and checking it on submit.</p>
<p>The CSRF token approach is a gross hack that has to touch an unreasonable amount of the stack just to check that the request came from the correct origin, which is something that the browser just, like, tells you for free now.</p>
<p>Not to mention the only reason this topic even <em>came up</em> in the Go community is that they found a serious security bug in their CSRF library that had been there for years lol.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378446" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-378446" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378446"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="378448" data-post-id="378448">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Obviously this is security-sensitive code and I am just typing into a forum box so <strong>DO NOT USE THIS FOR ANYTHING</strong>, but here is a port of <a href="https://github.com/golang/go/blob/go1.25.4/src/net/http/csrf.go#L130" rel="noopener nofollow ugc">the Go implementation</a>.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def check(conn) when conn.method in ["GET", "HEAD", "OPTIONS"] do
  :ok
end

def check(conn) do
  case get_req_header(conn, "sec-fetch-site") do
    [value] when value in ["same-origin", "none"] -&gt; :ok
    [_other] -&gt; {:error, :cross_origin}
    [] -&gt; check_origin(conn)
  end
end

# Fallback for 2020-2023 browsers with Origin but without Sec-Fetch-Site
defp check_origin(conn) do
  case get_req_header(conn, "origin") do
    [origin] -&gt;
      %URI{host: origin_host} = URI.new!(origin)
      case origin_host == conn.host do
        true -&gt; :ok
        false -&gt; {:error, :cross_origin_older_browser}
      end
    [] -&gt;
      # No Sec-Fetch-Site or Origin headers were present
      # This is not a browser (or is a pre-2020 browser)
      :ok
  end
end
</code></pre>
<p>Note that the entire <code>Origin</code> fallback section isn’t even necessary if you’re willing to ditch support for pre-2023 browsers (which I personally would tbh), at which point this is like 5 lines of actual code.</p>
<p>Contrast that with using cryptography to generate, sign, and mask a token, write it to a session cookie, and propagate the value into every single form in your application, which is hundreds of lines at least and leaks all the way into the high-level abstractions (forms) of the framework.</p>
<p>I think the path forward is pretty clear. If you’re writing a new framework there is no reason to even think about the token approach. Existing frameworks will adapt over time.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378448" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-378448" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378448"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #14"></div>
  </section>
</div>
    <div class="postbit" id="378460" data-post-id="378460">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adamu" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/120/31482_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adamu
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p><a class="mention" href="/u/garrison" rel="nofollow">@garrison</a> that is super interesting. Thanks for going into the details. I had read Filippo’s post before and mentally bookmarked it to come back to. My objective in this thread is to understand Phoenix’s <em>current</em> default behaviour, although the explanation of CRIME has raised another question:</p>
<ol>
<li>
<p>The fix seems to depend on the way the headers are compressed (or not). As Plug stores the unmasked token in the cookie, does this mean the attack is still possible in some cases? Maybe this is mitigated by the way the session is serialised (external term format, cryptographically signed, base64)?</p>
</li>
<li>
<p>The original question <a class="mention" href="/u/i-n-g-m-a-r" rel="nofollow">@i-n-g-m-a-r</a> asked 5 years ago that I’m re-asking: what is the purpose of sending the (masked) CSRF token back to LV via the socket via JS, considering web components can and do fetch it themselves directly on the server anyway?</p>
</li>
</ol> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378460" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-378460" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378460"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #15"></div>
  </section>
</div>
    <div class="postbit" id="378473" data-post-id="378473">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adamu" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/120/31482_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adamu
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="i-n-g-m-a-r" data-post="1" data-topic="31809">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/i/c67d28/48.png" class="avatar"> i-n-g-m-a-r:</div>
<blockquote>
<p>Does anyone know what is the point of:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
</code></pre>
</blockquote>
</aside>
<aside class="quote no-group" data-username="adamu" data-post="16" data-topic="31809">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/48/31482_2.png" class="avatar"> adamu:</div>
<blockquote>
<p>what is the purpose of sending the (masked) CSRF token back to LV via the socket via JS, considering web components can and do fetch it themselves directly on the server anyway?</p>
</blockquote>
</aside>
<p>Answering my own question in the hope of activating Cunningham’s Law.</p>
<p>The reason that the (masked) CSRF token is passed to the LiveSocket via JS is because this is a new HTTP request, so CSRF is possible (somebody could be serving up a page that loads JS that tries to connect to our web socket using our cookie). As the socket connections are handled <a href="https://github.com/phoenixframework/phoenix/blob/675e924f73da1a594e07ba22fcbda6636790ce4a/installer/templates/phx_web/endpoint.ex#L14" rel="noopener nofollow ugc">in the <code>Endpoint</code></a>, before the <a href="https://github.com/phoenixframework/phoenix/blob/675e924f73da1a594e07ba22fcbda6636790ce4a/installer/templates/phx_web/router.ex#L9" rel="noopener nofollow ugc"><code>:protect_from_forgery</code> plug in the <code>Router</code></a>, the socket performs its own validation of the CSRF token:</p>
<p><a href="https://github.com/phoenixframework/phoenix/blob/675e924f73da1a594e07ba22fcbda6636790ce4a/lib/phoenix/socket/transport.ex#L564-L570" class="onebox" target="_blank" rel="noopener nofollow ugc">https://github.com/phoenixframework/phoenix/blob/675e924f73da1a594e07ba22fcbda6636790ce4a/lib/phoenix/socket/transport.ex#L564-L570</a></p>
<p>In this code, <code>csrf_token</code> is the masked CSRF token passed to the <code>LiveSocket</code> connection request, and <code>csrf_state</code> is the unmasked one from the cookie.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378473" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-378473" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378473"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #16"></div>
  </section>
</div>
    <div class="postbit" id="378488" data-post-id="378488">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Headers are not vulnerable to CRIME in HTTP2 because they are compressed with an algorithm called HPACK which <a href="https://datatracker.ietf.org/doc/html/rfc7541" rel="noopener nofollow ugc">is part of the standard</a>.</p>
<p>Originally HTTP headers were not compressed at all (in <code>1.1</code>), but then people started trying to compress them naively (in SPDY) to save on ingress costs. As shown in that CF blog post it actually saves like half of ingress costs because headers are most of a request (not so much for a response).</p>
<p>Unfortunately it was shown that naively compressing headers with e.g. DEFLATE can leak them to an attacker (in an unlikely but plausible scenario), so everyone turned off compression. But compression was nice (made things cheaper), so they designed a special compression algorithm that is not vulnerable, and that’s HPACK.</p>
<p>On a modern stack you can put whatever you want in the headers and it won’t be leaked because they are compressed with HPACK. The reason you have to mask the CSRF token is that it is sent <em>in the body</em> (with the forms).</p>
<p>As a side note, I believe this also means that you should never put any sort of confidential secret into a compressed body alongside variable content or it could theoretically leak. Try not to think about the implications there.</p>
<aside class="quote no-group" data-username="adamu" data-post="16" data-topic="31809">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/48/31482_2.png" class="avatar"> adamu:</div>
<blockquote>
<p>As Plug stores the unmasked token in the cookie, does this mean the attack is still possible in some cases?</p>
</blockquote>
</aside>
<p>Again the reason the attack is not possible is because HTTP headers <em>are not</em> compressed this way, but if they were then the entire session would be vulnerable to exfiltration.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378488" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/18">Post #17</a>
	                </div>
	            </div>
              <div id="likers-container-378488" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378488"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #17"></div>
  </section>
</div>
    <div class="postbit" id="378490" data-post-id="378490">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="adamu" data-post="17" data-topic="31809">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/48/31482_2.png" class="avatar"> adamu:</div>
<blockquote>
<p>The reason that the (masked) CSRF token is passed to the LiveSocket via JS is because this is a new HTTP request, so CSRF is possible (somebody could be serving up a page that loads JS that tries to connect to our web socket using our cookie).</p>
</blockquote>
</aside>
<p>I have not read the code but this makes perfect sense and I think it’s correct. Note that the token <em>has</em> to be masked because it is again <em>in the body</em> and could be compressed naively.</p>
<p>I know it’s not what you’re asking about but I can’t help but point out that it would be a whole lot easier to just check <code>Sec-Fetch-Site</code> and be done with it rather than pass a token through like four levels of abstraction. Of course this was not possible when the code was written!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378490" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/19">Post #18</a>
	                </div>
	            </div>
              <div id="likers-container-378490" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378490"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #18"></div>
  </section>
</div>
    <div class="postbit" id="378536" data-post-id="378536">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="adamu" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/adamu/120/31482_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  adamu
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>A summary for anyone else landing here.</p>
<ol>
<li>Phoenix uses <a href="https://hexdocs.pm/plug/Plug.CSRFProtection.html" rel="noopener nofollow ugc">Plug.CSRFProtection</a> to generate the CSRF token. A token has two forms: a short/unmasked form, which is what is stored in the session, and a long/masked form, which is what is returned by <code>Plug.CSRFProtection.get_csrf_token/0</code> and injected into the HTML responses. The long form in the HTML is necessary to guard against <a href="https://en.wikipedia.org/wiki/BREACH" rel="noopener nofollow ugc">BREACH</a> attacks.</li>
<li>This is not mentioned in the thread, but it’s also possible for a single HTML page to have <em>multiple</em> masked CSRF tokens that correspond to the same unmasked one in the session. This is because the masked tokens are generated (from the token in the session) per process on the server. The process that returns the initial HTML response from the controller is different to the one that updates the DOM from LiveView.</li>
<li>The reason the masked/long CSRF token is passed to the web socket is to prevent a CSRF request occurring when the websocket is created. Normally the masked CSRF token is validated against the one in the session by the <code>:protect_from_forgery</code> plug, but websocket requests are set up in the <code>Endpoint</code> and don’t use <code>:protect_from_forgery</code>, so the web socket code has <a href="https://github.com/phoenixframework/phoenix/blob/675e924f73da1a594e07ba22fcbda6636790ce4a/lib/phoenix/socket/transport.ex#L564-L570" rel="noopener nofollow ugc">its own code</a> to validate the token (which ultimately uses <code>Plug.CSRFProtection</code> under the hood, the same as <code>:protect_from_forgery</code>). Additionally, the purpose of this is <em>not</em> because LiveView needs to otherwise know the long/masked token - it’s just to protect against CSRF when establishing the socket.</li>
<li>As explained in <a href="https://words.filippo.io/csrf/" rel="noopener nofollow ugc">this article by Filippo Valsorda</a>, in 2025 CSRF tokens may not be the best way to protect against CSRF, instead the <code>Sec-Fetch-Site</code> header can be used - but this method is relatively new and is not currently used by Phoenix out of the box.</li>
</ol> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378536" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/20">Post #19</a>
	                </div>
	            </div>
              <div id="likers-container-378536" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378536"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #19"></div>
  </section>
</div>
    <div class="postbit" id="378577" data-post-id="378577">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>The only other thing I would note is that calling it a “long” token makes it sound like some sort of magic is going on, but if you look at <a href="https://github.com/elixir-plug/plug/blob/v1.18.1/lib/plug/csrf_protection.ex#L429" rel="noopener nofollow ugc">the implementation</a> it’s actually quite simple.</p>
<p>Generating the masked token is literally equivalent to this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">token = Base.url_encode64(:crypto.strong_rand_bytes(18))
mask = Base.url_encode64(:crypto.strong_rand_bytes(18))

masked_token = Base.url_encode64(:crypto.exor(token, mask)) &lt;&gt; mask
</code></pre>
<p>So the masked token is roughly twice as long because the mask is simply concatenated to the token! That way, on the receiving end, the mask can be used to decode the token.</p>
<p>It’s also interesting that the token ends up being base64-encoded twice, which I had not noticed until I typed it out. Seems slightly suboptimal, but I would imagine fixing it wasn’t worth changing the rest of the code.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="378577" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/session-csrf-token-versus-liveview-csrf-token/31809/21">Post #20</a>
	                </div>
	            </div>
              <div id="likers-container-378577" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="378577"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #20"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/31809/load_more?page=3">Load more posts (5 remaining)</a>
</div></template></turbo-stream>