Best practices form processing with Express

body-parser, express, node.js, security

Solution

Should I do a POST request + 302 redirect for form processing or rather something else like an AJAX request?

No, best practice for a good user experience since 2004 or so (basically since gmail launched) has been form submission via AJAX and not web 1.0 full-page load form POSTs. In particular, error handling via AJAX is less likely to leave your user at a dead end browser error page and then hit issues with the back button. The AJAX in this case should send an HTTP PATCH request to be most semantically correct but POST or PUT will also get the job done.

How should I handle invalid FORM requests (for example invalid login, or email address is already in use during signup)?

Invalid user input should result in an HTTP `400 Bad Request` status code response, with details about the specific error(s) in a JSON response body (the format varies per application but either a general message or field-by-field errors are common themes)

For email already in use I use the HTTP `409 Conflict` status code as a more particular flavor of general bad request payload.

I assume before inserting anything into my DB I need to sanitize and validate all form fields on the server side. How would you do that?

Absolutely. There are many tools. I generally define a schema for a valid request in JSON Schema and use a library from npm to validate that such as is-my-json-valid or ajv. In particular, I recommend being as strict as possible: reject incorrect types, or coerce types if you must, remove unexpected properties, use small but reasonable string length limits and strict regular expression patterns for strings when you can, and of course make sure your DB library property prevents injection attacks.

I read some things about CSRF but I have never implemented a CSRF protection.

The OWSAP Node Goat Project CSRF Exercise is a good place to start with a vulnerable app, understand and exploit the vulnerability, then implement the fix (in this case with a straightforward integration of the `express.csrf()` middleware.

Do I need to take care of any other possible vulnerabilities when processing forms with Express?

Yes generally application developers must understand and actively code securely. There's a lot of material out there on this but particular care must be taken when user input gets involved in database queries, subprocess spawning, or being written back out to HTML. Solid query libraries and template engines will handle most of the work here, you just need to be aware of the mechanics and potential places malicious user input could sneak in (like image filenames, etc).

Problem

I'm writing a website which implements a usermanagement system and I wonder what best practices regarding form processing I have to consider. Especially performance, security, SEO and user experience are important to me. When I was working on it I came across a couple questions and I didn't find an complete node/express code snippet where I could figure out all of my below questions. Use case: Someone is going to update the birthday of his profile. Right now I am doing a POST request to the same URL to process the form on that page and the POST request will respond with a 302 redirect to the same URL. General questions about form processing: - Should I do a POST request + 302 redirect for form processing or rather something else like an AJAX request? - How should I handle invalid FORM requests (for example invalid login, or email address is already in use during signup)? Express specific questions about form processing: I assume before inserting anything into my DB I need to sanitize and validate all form fields on the server side. How would you do that? I read some things about CSRF but I have never implemented a CSRF protection. I'd be happy to see that in the code snippet too Do I need to take care of any other possible vulnerabilities when processing forms with Express? Example HTML/Pug: ``` form#profile(method='POST', action='/settings/profile') input#profile-real-name.validate(type='text', name='profileRealName', value=profile.name) label(for='profile-real-name') Name textarea#profile-bio.materialize-textarea(placeholder='Tell a little about yourself', name='profileBio') | #{profile.bio} label(for='profile-bio') About input#profile-url.validate(type='url', name='profileUrl', value=profile.bio) label(for='profile-url') URL input#profile-location.validate(type='text', name='profileLocation', value=profile.location) label(for='profile-location') Location .form-action-buttons.right-align a.btn.grey(href='' onclick='resetForm()') Reset button.btn.waves-effect.waves-light(type='submit') ``` Example Route Handlers: ``` router.get('/settings/profile', isLoggedIn, profile) router.post('/settings/profile', isLoggedIn, updateProfile) function profile(req, res) { res.render('user/profile', { title: 'Profile', profile: req.user.profile }) } function updateProfile(req, res) { var userId = req.user._id var form = req.body var profile = { name: form.profileRealName, bio: form.profileBio, url: form.profileUrl, location: form.profileLocation } // Insert into DB } ``` Note: A complete code snippet which takes care of all form processing best practices adapted to the given example is highly appreciated. I'm fine with using any publicly available express middleware.

Original source