이 기사에서는 실용적인 Nodejs npm 패키지---koa-csrf를 공유합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
koa-csrf은 csrf 공격을 방지하는 데 사용되는 koa 미들웨어입니다.
물론 여기서는 CSRF가 무엇인지, 이를 방지하는 방법에 대해 자세히 설명하지 않겠습니다. 관심 있는 분들은 understanding-csrf를 읽어보세요. egg는 csrf 솔루션을 처리합니다.
먼저 간단한 예를 살펴보겠습니다.
const router = require('koa-router')();
const CSRF = require('koa-csrf');
const csrfMD = new CSRF({
invalidSessionSecretMessage: 'Invalid session secret',
invalidTokenMessage: 'Invalid CSRF token',
invalidTokenStatusCode: 403,
});
router
.get('/',csrfMD,async (ctx, next) => {
ctx.cookies.set('cid','1234', cookieSet);
// 下发csrf token
await ctx.render('index', {
title: 'EJS !',
csrf: ctx.csrf
});
})
.post('/post', csrfMD ,async (ctx, next) => {
console.log(ctx.method);
ctx.session.email = ctx.request.body.email;
ctx.session.name = ctx.request.body.name;
ctx.redirect('/');
})
module.exports = router;
작업 흐름에 대한 간단한 이해:
클라이언트 요청 페이지:
- 서버는 사용자의 현재 세션에 대한 비밀 값을 생성하고 이를 세션에 저장합니다. 비밀을 기반으로 하는 csrf 토큰, ctx._csrf에 저장
- csrf 토큰을 클라이언트에 보냅니다
- 게시 요청을 보낼 때와 같은 사용자 쓰기 작업:
서버는 클라이언트가 전달한 csrf 토큰을 얻습니다. 클라이언트;
- 서버는 현재 세션에서 csrf 토큰을 얻습니다. 비밀 값을 찾습니다.
- 서버는 받은 csrf 토큰을 확인하기 위해 비밀 값을 사용합니다.
-
koa-csrf
koa-csrf의 토큰 생성 및 확인 로직은 모두 이
csrf
패키지에 있습니다. const csrf = require('csrf');
class CSRF {
constructor(opts = {}) {
// opts配置对象、并且有提供默认配置
// 允许自定义配置对象进行覆盖
this.opts = Object.assign(
{
// 使 koa 抛出的错误信息内容,默认值为:'Invalid CSRF token'。
// 它可以是一个接收 ctx 作为参数的函数,函数最后返回错误信息内容。
invalidTokenMessage: 'Invalid CSRF token',
// 验证失败时的响应状态码,默认值为:403(Forbidden)
// 跟 invalidTokenMessage 参数一样,它也会被传递给 ctx.throw,用于抛出错误和拒绝请求。
invalidTokenStatusCode: 403,
// 排除的请求方法,默认值为:['GET', 'HEAD', 'OPTIONS']。
excludedMethods: ['GET', 'HEAD', 'OPTIONS'],
// 是否禁止通过查询字符串传递 _csrf 校验 token,默认值为 false
// 如果校验 token 出现在 URL 中,则可能会通过 Referer 泄露,应尽量把 Token 放在表单中,把敏感操作由 GET 改为 POST。
disableQuery: false
},
opts
);
// 生成token generation/verification instance
this.tokens = csrf(opts);
// 早期很多这样的中间件写法、对接koa中间件
return this.middleware.bind(this);
}
// koa中间件
middleware(ctx, next) {
// __defineGetter__ API已经不推荐使用
// 在ctx上挂载csrf属性。
// 当读取ctx.csrf会执行该回调
ctx.__defineGetter__('csrf', () => {
// 如果已经存在直接返回、避免多次生成
if (ctx._csrf) {
return ctx._csrf;
}
// csrf依赖于koa session
if (!ctx.session) {
return null;
}
// 生成一个secret存储在ctx.session.secret
if (!ctx.session.secret) {
ctx.session.secret = this.tokens.secretSync();
}
// 根据上述的secret生成csrf toke存到ctx._csrf
// 一般非框架本身属性,都建议_xx表示私有
ctx._csrf = this.tokens.create(ctx.session.secret);
// 返回
return ctx._csrf;
});
// 挂栽ctx.response.csrf属性
ctx.response.__defineGetter__('csrf', () => ctx.csrf);
// 如果是请求方法黑名单直接不处理
if (this.opts.excludedMethods.indexOf(ctx.method) !== -1) {
return next();
}
if (!ctx.session.secret) {
ctx.session.secret = this.tokens.secretSync();
}
// 从ctx.request.body取出客户端传递过来的_csrf token
// 因此依赖于koa-bodyparser类库
const bodyToken =
ctx.request.body && typeof ctx.request.body._csrf === 'string'
? ctx.request.body._csrf
: false;
// 除了从body获取token
// 支持从ctx.query._csrf即get方法查询字符串
// 支持从请求头获取 'csrf-token'、'xsrf-token'、'x-csrf-token'、'x-xsrf-token'
const token =
bodyToken ||
(!this.opts.disableQuery && ctx.query && ctx.query._csrf) ||
ctx.get('csrf-token') ||
ctx.get('xsrf-token') ||
ctx.get('x-csrf-token') ||
ctx.get('x-xsrf-token');
// 如果获取失败、根据配置对象的信息、抛出异常
if (!token) {
return ctx.throw(
this.opts.invalidTokenStatusCode,
typeof this.opts.invalidTokenMessage === 'function'
? this.opts.invalidTokenMessage(ctx)
: this.opts.invalidTokenMessage
);
}
// 校验token失败
if (!this.tokens.verify(ctx.session.secret, token)) {
return ctx.throw(
this.opts.invalidTokenStatusCode,
typeof this.opts.invalidTokenMessage === 'function'
? this.opts.invalidTokenMessage(ctx)
: this.opts.invalidTokenMessage
);
}
// 校验成功
return next();
}
}
module.exports = CSRF;
다음 호에서는 더 많은 관련 로직을 처리하는 csrf 라이브러리에 대해 살펴보겠습니다.
오늘은 여기까지입니다. 다음에 뵙겠습니다.
ps: node에도 관심이 있으시다면 제 공식 계정인 xyz 프로그래밍 다이어리를 팔로우해주세요.
관련 추천: "
nodejs 튜토리얼
"
위 내용은 실용적인 Nodejs npm 패키지 공유: koa-csrf의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!