title: CSRF protection - is a JWT and CORS whitelist combination sufficient?
body: <p>I'm working to address CSRF vulnerabilities in my React/Phoenix app, and it seems to me like my app is safe... but I'm not an expert in these matters, and wanted to turn to the community to see if I've overlooked something or am being naïve.</p>
  
  <p>The Phoenix is a pure API, running separately from the React client, so I'm dealing with CORS - the whitelist of allowed origins is set in the Phoenix <code>router.ex</code>:</p>
  
  <pre><code>pipeline :api do
    plug CORSPlug, [origin: "localhost:3000"]
    plug :accepts, ["json"]
    plug Guardian.Plug.VerifyHeader, realm: "Bearer"
    plug Guardian.Plug.LoadResource
  end
  </code></pre>
  
  <p>and, as you can see, I'm using Guardian (which uses JWT for user authentication) for handling authorization.</p>
  
  <p>Authorized clients store the JWT in <code>localStorage</code>, and Guardian is set to look for that value in the <code>Authorization</code> header of requests as a <code>Bearer</code>... protected Phoenix controllers include:</p>
  
  <pre><code>plug Guardian.Plug.EnsureAuthenticated
  </code></pre>
  
  <p>I set up a test attacker, running on <code>localhost:5000</code> to try to mock a CSRF attack. First, I tried an AJAX attack - I copied a valid JWT value from the <code>localStorage</code> of a logged-in window and set it in the request header of my mock attacker. As expected, this fails because <code>localhost:5000</code> is not whitelisted</p>
  
  <pre><code>The 'Access-Control-Allow-Origin' header contains the invalid value 'null'. Origin 'http://localhost:5000' is therefore not allowed access.
  </code></pre>
  
  <p>To test, I added <code>localhost:5000</code> to the Phoenix whitelist and the request did indeed work... so it seems that, even if an attacker managed to steal a valid JWT, they'd be stopped by the whitelist.</p>
  
  <p>I then tested an automated form submission, borrowed from the OWASP docs:</p>
  
  <pre><code>&lt;body onload='document.CSRF.submit()'&gt;
      &lt;form action='http://localhost:4000/api/v1/user' method='POST' name='CSRF'&gt;
          &lt;input type='hidden' name='name' value='Hacked'&gt;
          &lt;input type='hidden' name='password' value='Hacked'&gt;
      &lt;/form&gt;
  &lt;body&gt;
  </code></pre>
  
  <p>but this gets caught by <code>Guardian.Plug.EnsureAuthenticated</code>, set in the API's controllers, since there is no <code>Authorization</code> header nor valid JWT present:</p>
  
  <pre><code>[info] POST /api/v1/user
  [debug] Processing with MyApp.UserController.create/2
    Parameters: %{"name" =&gt; "Hacked", "password" =&gt; "[FILTERED]"}
    Pipelines: [:api]
  [info] Sent 401 in 21ms
  [debug] MyApp.UserController halted in 
  Guardian.Plug.EnsureAuthenticated.call/2
  </code></pre>
  
  <p>So my impression is that AJAX attacks will fail, even with a valid JWT, because of the CORS whitelist... simple requests will fail because they don't include the <code>Authorization</code> header.</p>
  
  <p>I've been reading a lot about CSRF protection when using JWTs for authorization, but it seems like no two people can agree on what is and isn't safe. Am I missing something, or is the combination of the CORS whitelist and Guardian JWT check sufficient to protect agains CSRF?</p>