swaf/src/components/CsrfProtectionComponent.ts

70 lines
2.5 KiB
TypeScript
Raw Normal View History

2020-04-22 15:52:17 +02:00
import ApplicationComponent from "../ApplicationComponent";
import {Request, Router} from "express";
2020-04-22 15:52:17 +02:00
import crypto from "crypto";
import {BadRequestError} from "../HttpError";
import {AuthMiddleware} from "../auth/AuthComponent";
2020-04-22 15:52:17 +02:00
export default class CsrfProtectionComponent extends ApplicationComponent {
private static readonly excluders: ((req: Request) => boolean)[] = [];
2020-07-08 13:28:22 +02:00
public static getCsrfToken(session: Express.Session): string {
if (typeof session.csrf !== 'string') {
session.csrf = crypto.randomBytes(64).toString('base64');
}
return session.csrf;
}
public static addExcluder(excluder: (req: Request) => boolean): void {
this.excluders.push(excluder);
2020-07-08 13:28:22 +02:00
}
public async handle(router: Router): Promise<void> {
router.use(async (req, res, next) => {
for (const excluder of CsrfProtectionComponent.excluders) {
if (excluder(req)) return next();
2020-07-08 13:28:22 +02:00
}
const session = req.getSession();
res.locals.getCsrfToken = () => {
return CsrfProtectionComponent.getCsrfToken(session);
2020-04-22 15:52:17 +02:00
};
if (!['GET', 'HEAD', 'OPTIONS'].find(s => s === req.method)) {
try {
if ((await req.as(AuthMiddleware).getAuthGuard().getProofsForRequest(req)).length === 0) {
if (session.csrf === undefined) {
return next(new InvalidCsrfTokenError(req.baseUrl, `You weren't assigned any CSRF token.`));
} else if (req.body.csrf === undefined) {
return next(new InvalidCsrfTokenError(req.baseUrl, `You didn't provide any CSRF token.`));
} else if (session.csrf !== req.body.csrf) {
return next(new InvalidCsrfTokenError(req.baseUrl, `Tokens don't match.`));
}
}
} catch (e) {
return next(e);
2020-04-22 15:52:17 +02:00
}
}
next();
});
}
}
class InvalidCsrfTokenError extends BadRequestError {
public constructor(url: string, details: string, cause?: Error) {
2020-04-22 15:52:17 +02:00
super(
`Invalid CSRF token`,
`${details} We can't process this request. Please try again.`,
url,
cause,
2020-04-22 15:52:17 +02:00
);
}
public get name(): string {
2020-04-22 15:52:17 +02:00
return 'Invalid CSRF Token';
}
public get errorCode(): number {
2020-04-22 15:52:17 +02:00
return 401;
}
}