Backend
- Take this express backend server
const app = express();
app.use(rTracer.expressMiddleware());
app.use(requestLogger);
app.use(
cors({
credentials: true,
origin: getAllowedOrigins,
}),
);
app.use(
helmet({
contentSecurityPolicy: {
directives: {
frameAncestors: ["'self'", ''],
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
baseUri: ["'self'"],
fontSrc: ["'self'", 'https:', 'data:'],
imgSrc: ["'self'", 'data:'],
},
},
referrerPolicy: {
policy: 'same-origin',
},
}),
);
app.set('trust proxy', 1);
app.use('/admin', adminRouter);
app.use(express.json({ limit: BODY_PARSER_LIMIT }));
app.use(express.urlencoded({ limit: BODY_PARSER_LIMIT, extended: false }));
app.use(sessionHandler);
app.use(passport.initialize());
app.use(passport.session());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/admin/queues', serverAdapter.getRouter());
const csrfProtection = new Tokens();
app.use((req: Request, res: Response, next: NextFunction) => {
if (!req.cookies.csrfToken) {
const token = csrfProtection.create('abracadabragilligillifoo');
res.cookie('csrfToken', token, {
httpOnly: false,
maxAge: 86400000,
sameSite: true,
secure: false,
});
}
next();
});
function verifyCsrfToken(req: Request, res: Response, next: NextFunction) {
const csrfToken = req.cookies.csrfToken;
const requestToken = req.headers['x-csrf-token'];
// havent added verification yet
// PRINTS undefined undefined on the first run since nuxtServerInit doesn't have the token
console.log(csrfToken, requestToken, 'WWWWTTTTFFFF');
next();
}
app.use('/', verifyCsrfToken, router);
Frontend
- As you notice, unless someone actually hits an API endpoint inside router, a cookie will not be sent back containing the csrf token
- But notice this nuxtServerInit endpoint where it makes all types of axios requests
export const actions = {
async nuxtServerInit({ dispatch, rootGetters }, { error, req }) {
try {
console.log(req.headers.cookie);
await dispatch('auth/fetchSession')
await dispatch('news/popular/fetchNewsItems')
} catch (err) {
if (err.code === 'ECONNREFUSED') {
error(
'Servers are unreachable currently. If this issue persists, kindly open a support ticket here',
)
} else {
error(err.message)
}
}
},
}
- The cookie with the value of csrfToken will be undefined before any endpoint is hit
- But the endpoint requires a CSRF token to be present in order to actually work
Questions
- How to beat this chicken/egg problem?
- Is is actually safe to expose some GET /api/csrf endpoint?
- What happens if the cookie is HTTP only?