CSRF attack when form data isn't parsed



  • How can you do a CRSF attack on express if it only accepts JSON?

    Sample Node app:

    const express = require('express');
    
    const app = express();
    
    app.use(express.json());
     
    app.post('/item', (req, res, next) => {
        console.log('posting item');
        res.send(`posted item name: ${req.body.itemName}`);
      });
    
    app.listen(4000)
          .on('listening', console.log("HTTP server listening on port 4000"));
    
    

    What I tried:

    If you do an attack from an html form it sends form data:

    <!DOCTYPE html>
    <html lang="en">
      <body>
        <form action=http://localhost:4000/item method=post >  
            <input name ="itemName" value="Shirt" type="text">
         <input type=submit>  
           </form>  
     
        <script>
          document.forms[0].submit();
        </script>
      </body>
    </html>
    

    With this attack, req.body will be empty. express.json() only parses json, not form data. So in this instance, a POST where the end point requires user data, the attack doesn't do anything.

    My actual app uses fetch to send requests. fetch does populate req.body but my understanding is that CSRF can't be done with fetch- Browsers send preflight requests when using fetch. If the preflight request shows that the origin isn't allowed, it will not send the full request. So it will always fail with a CSRF request. But I'm not so certain of this.

    So is there another way to send a CSRF attack with user inputs? Or in this case, is there no way to do a successful csrf attack?



  • Browsers only send pre-flights when you make a "non-simple" request. A "simple" request is basically "anything an HTML form could send". Setting a content-type other than what a form can send (such as JSON) will trigger a preflight.

    HOWEVER... in many cases, you can send arbitrary content as "text/plain" (which doesn't trigger a pre-flight), and the server will parse it anyhow. There are also ways to send valid JSON from an HTML form as x-www-form-urlencoded (with weirdly-named fields and values, so you need something that isn't strict about allowed elements) sometimes, but sending text is almost certainly better. Most JSON parsers don't actually check what the reported content type is, they just try to parse the content as JSON. If it's parseable, it passes. Browsers can do that just fine, using fetch or XHR.



Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2