使用Express进行处理的最佳实践

我正在写一个实现用户pipe理系统的网站,我不知道有什么关于表单处理的最佳实践,我不得不考虑。

尤其是性能,安全性,SEO和用户体验对我来说非常重要。 当我正在处理这个问题时,我偶然发现了一些问题,并没有find一个完整的节点/快速代码片断,我可以在这里找出所有我下面的问题。

用例:有人将更新他的个人资料的生日。 现在我正在对同一个URL进行一个POST请求来处理该页面上的表单,POST请求将以302redirect到相同的URL进行响应。

关于表单处理的一般问题:

  1. 我应该做一个POST请求+ 302redirect的表单处理,而不是像AJAX请求其他东西?
  2. 我应该如何处理无效的FORM请求(例如,无效login或注册期间已经使用的电子邮件地址)?

expression关于表单处理的具体问题:

  1. 我之前插入任何东西,我想我需要清理和validation服务器端的所有表单域。 你会怎么做?

  2. 我阅读了有关CSRF的一些内容,但是我从来没有实施CSRF保护。 我也很高兴在代码片段中看到这一点

  3. 使用Express处理表单时是否需要处理任何其他可能的漏洞?

示例HTML /帕格:

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') 

示例路由处理程序:

 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 } 

注:一个完整的代码片段,照顾了所有适用于给定示例的表单处理最佳实践,值得高度赞赏。 我可以使用任何公开的快递中间件。

我应该做一个POST请求+ 302redirect的表单处理,而不是像AJAX请求其他东西?

,自2004年左右以来,一个良好的用户体验的最佳实践(基本上,因为Gmail启动)已经通过AJAX提交表单提交,而不是web 1.0全页加载表单POST。 尤其是,通过AJAX进行error handling不太可能让用户处于死胡同的浏览器错误页面,然后用后退button触发问题。 在这种情况下,AJAX应该发送一个HTTP PATCH请求在语义上是最正确的,但POST或PUT也将完成工作。

我应该如何处理无效的FORM请求(例如,无效login或注册期间已经使用的电子邮件地址)?

无效的用户input应该会导致HTTP 400 Bad Request状态码响应,并在JSON响应正文中提供有关特定错误的详细信息(格式因应用而异,但一般消息或字段错误是常见主题)

对于已经在使用的电子邮件,我使用HTTP 409 Conflict状态代码作为一般坏请求有效载荷的更特别的味道。

我之前插入任何东西,我想我需要清理和validation服务器端的所有表单域。 你会怎么做?

绝对。 有很多工具。 我通常在JSON Schema中定义一个有效请求的模式,并使用npm的库来validation,比如is-my-json-valid或ajv 。 特别是,我build议尽可能严格:拒绝不正确的types,或强制types(如果必须的话),删除意外的属性,尽可能使用小但合理的string长度限制和严格的正则expression式模式,当然,确保您数据库库属性可防止注入攻击。

我阅读了CSRF的一些内容,但是我从来没有实施过CSRF保护。

OWSAP节点山羊项目CSRF练习是一个从易受攻击的应用程序开始,理解和利用漏洞,然后实施修复(在这种情况下,直接集成express.csrf()中间件)的好地方。

使用Express处理表单时是否需要处理任何其他可能的漏洞?

一般来说,应用程序开发人员必须理解并主动编码。 这里有很多材料,但是当用户input涉及到数据库查询,subprocess产生或写回到HTML时,必须特别小心。 固体查询库和模板引擎将处理这里的大部分工作,您只需要注意恶意用户input可能潜入的机制和潜在的地方(如图像文件名等)。

我当然不是Express专家,但我想我至less可以回答#1:

你应该按照Post / Redirect / Get Web开发模式来防止重复的表单提交。 我听说303redirect是redirect表单提交的正确http状态码。

我使用POST路由处理表单,一旦完成,我触发302redirect。

从#3开始,我build议您查看一下express-validator,这里介绍一下: https : //developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/forms 。 这是一个中间件,可以让你像这样validation和消毒:

 req.checkBody('name', 'Invalid name').isAlpha(); req.checkBody('age', 'Invalid age').notEmpty().isInt(); req.sanitizeBody('name').escape(); 

即使这不是一个完整的答案,我也不能评论这个答案。 只是认为这可能会帮助你。

如果用户体验是你正在考虑的事情,那么页面redirect是一个很好的select。 为访问您的网站的人提供一个平稳的stream程对于防止丢失非常重要,而且由于表单已经不是很满意的事情,所以缓解它们的使用是主要的。 你不想重新加载他们的页面,可能已经花了一些时间来加载只是为了显示错误消息。 一旦表单有效并且您创build了用户cookie,即使您可以在客户端应用程序上执行某些操作来阻止该redirect,但redirect也没有问题,但这是超出范围的。

正如Levent所述,您应该签出express-validator ,这是为此类目的build立的更为完善的解决scheme。

 req.check('profileRealName', 'Bad name provided').notEmpty().isAlpha() req.check('profileLocation', 'Invalid location').optional().isAlpha(); req.getValidationResult().then(function (result) { if (result.isEmpty()) { return null } var errors = result.array() // [ // { param: "profileRealName", msg: "Bad name provided", value: ".." }, // { param: "profileLocation", msg: "Invalid location", value: ".." } // ] res.status(400).send(errors) }) .then(function () { // everything is fine! insert into the DB and respond.. }) 

从看起来像,我可以假设你正在使用MongoDB。 鉴于此,我build议使用ODM,如mongoose 。 它将允许您为模式定义模型,并直接在模型上设置限制,让模型为您处理这些冗余validation。

例如,您的用户的模型可能是

 var User = new Schema({ name: { type: String, required: [true, 'Name required'] }, bio: { type: String, match: /[az]/ }, age: { type: Number, min: 18 }, // I don't know the kind of site you run ;) }) 

在你的路线上使用这个模式将看起来像

 var user = new User({ name: form.profileRealName, bio: form.profileBio, url: form.profileUrl, location: form.profileLocation }) user.save(function (err) { // and you could grab the error here if it exists, and react accordingly }); 

正如你所看到的,它提供了一个非常酷的api,如果你想知道更多,你应该在他们的文档中阅读。

关于CRSF,你应该安装csurf ,在自述文件中有很好的说明和示例用法。

在那之后,你几乎可以走了,除了关键的依赖关系外,没有太多可以考虑的事情,例如,在发生0天的情况下,例如发生在2015年的情况智威汤逊,但这还是不多见的。