11

抱歉,我不太明白 koa 中的密钥是如何工作的。在 koa 中,对象keys上有一个字段app,它将像这样使用:

const app = new Koa();
app.keys = ['some secret', 'another secret', 'or more ...']; // it's an
                                                             // array right?

然后,在使用koa-csrf中间件时,默认情况下内置csrf.middleware不使用app.keys. 如果我使用默认中间件,我需要创建另一个在会话中设置密钥的中间件。

app.use(session()); // koa-generic-session
app.use(async (ctx, next) => {               // without this custom middleware
  ctx.session.secret = 'yet another secret'; // POST /protected-route
  await next();                              // will give 403
});                                          // missing secret
csrf(app);
app.use(csrf.middleware);

当我使用 Flask 时,我只需要提供一个由stringarray. 为什么需要多个密钥?整个应用程序只使用一个还不够吗?

4

1 回答 1

12

好像你在这里问很多事情。

  1. 您可以为 Koa 提供app.keys = [...]多个密钥用于密钥轮换。

    例如,如果您想每个月生成一个新密钥,您可以用它签署新的 cookie,而不会立即使所有旧 cookie 失效。相反,您更愿意让旧 cookie 自然过期。

    如果密钥轮换不是您关心的事情,那么您只需使用app.keys = ['mysecret']您永远不会更改的内容。

  2. koa-csrf的中间件实际上确实使用了您设置的键app.keys=

    Koa 传递app.keys到它的 Cookies 实例(https://github.com/pillarjs/cookies),以便内置this.cookies.get()并将this.cookies.set()使用密钥(如果提供)。

    koa-session使用 Koa 的内置this.cookies.{get,set}.

    koa-csrf使用koa-session.

但这一切都无关紧要。

403 响应不是抱怨你没有设置app.keys=秘密。它抱怨您没有提供 CSRF 令牌(又名秘密),更不用说有效的了。

您手动设置的“修复”this.session.secret只是手动设置 koa-csrf 查找 CSRF 令牌的值。您正在绕过 CSRF 系统的整个安全措施。

CSRF 令牌系统的全部意义在于确保访问受保护端点的人实际上来自于从<form>您控制的页面发布到该端点的 a。

它通过生成一个令牌、将其保存在 cookie 中、将令牌附加到表单上,然后在提交时确保表单令牌与会话令牌匹配来做到这一点。

您似乎缺少的是您必须:

  1. 生成 CSRF 令牌
  2. 将其设置secret为客户端的 cookie
  3. 向客户端公开 CSRF 令牌,以便他们将其提交到受保护的端点。
  4. 在受保护的端点上,确保客户端发送的 CSRF 令牌与其“秘密”cookie 中的 CSRF 令牌匹配。以下是koa-csrf 检查以找到该令牌的地方。

koa-csrf 的this.csrf调用执行 #1 和 #2。你必须实现#3。koa-csrf 的this.assertCSRF#4。

所以,总而言之,这就是它的外观(未经测试):

var koa = require('koa')
var csrf = require('koa-csrf')
var session = require('koa-session')
var Router = require('koa-router');
var bodyParser = require('koa-bodyparser');

var app = koa()
app.keys = ['session secret']
app.use(session())
app.use(bodyParser())
csrf(app)
app.use(csrf.middleware)

var router = new Router();

router.get('/messages', function*() {
  this.render('new_message_form.html', {
    token: this.csrf   // this call also sets `this.session.secret` for you
  });
});

router.post('/messages', function*() {
  this.assertCSRF(this.request.body);

  // If we get this far, then the CSRF check passed
  yield database.insertMessage(this.body.message);
});

app.use(router.routes());
app.listen(3000, () => console.log('server listening on 3000'));

这就是“new_message_form.html”的样子。请注意,我设置了一个隐藏字段_csrf,以便当用户提交它时,生成的令牌this.csrf会发送到我的受保护端点,并且该_csrf字段是 koa-csrf 检查以查找提交的令牌的地方之一。

<form action="/messages" method="POST">
  <input type="hidden" name="_csrf" value="{{ token }}">
  <input type="message" name="message" placeholder="Write your message here...">
  <button type="submit">Save Message<button>
</form>
于 2016-02-13T01:26:44.273 回答