Added CSRF tokens to non-partial templates.

This commit is contained in:
rainbow napkin 2024-12-29 15:02:37 -05:00
parent 2ea3c72a61
commit 83f76af6e8
24 changed files with 94 additions and 22 deletions

View file

@ -8,6 +8,7 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"connect-mongo": "^5.1.0", "connect-mongo": "^5.1.0",
"csrf-sync": "^4.0.3",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"express": "^4.18.2", "express": "^4.18.2",
"express-session": "^1.18.0", "express-session": "^1.18.0",

View file

@ -16,9 +16,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//Config //Config
const config = require('../../config.json'); const config = require('../../config.json');
//Local Imports
const {userModel} = require('../schemas/user/userSchema'); const {userModel} = require('../schemas/user/userSchema');
const permissionModel = require('../schemas/permissionSchema'); const permissionModel = require('../schemas/permissionSchema');
const channelModel = require('../schemas/channel/channelSchema'); const channelModel = require('../schemas/channel/channelSchema');
const csrfUtils = require('../utils/csrfUtils');
const {exceptionHandler, errorHandler} = require("../utils/loggerUtils"); const {exceptionHandler, errorHandler} = require("../utils/loggerUtils");
//register page functions //register page functions
@ -41,7 +44,8 @@ module.exports.get = async function(req, res){
rankEnum: permissionModel.rankEnum, rankEnum: permissionModel.rankEnum,
chanGuide: chanGuide, chanGuide: chanGuide,
userList: userList, userList: userList,
permList: permList permList: permList,
csrfToken: csrfUtils.generateToken(req)
}); });
}catch(err){ }catch(err){

View file

@ -17,7 +17,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//Config //Config
const config = require('../../config.json'); const config = require('../../config.json');
//Local Imports
const csrfUtils = require('../utils/csrfUtils');
//channel functions //channel functions
module.exports.get = function(req, res){ module.exports.get = function(req, res){
res.render('channel', {instance: config.instanceName, user: req.session.user}); res.render('channel', {instance: config.instanceName, user: req.session.user, csrfToken: csrfUtils.generateToken(req)});
} }

View file

@ -18,9 +18,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
const config = require('../../config.json'); const config = require('../../config.json');
//local imports //local imports
const {exceptionHandler, errorHandler} = require('../utils/loggerUtils');
const channelModel = require('../schemas/channel/channelSchema'); const channelModel = require('../schemas/channel/channelSchema');
const permissionModel = require('../schemas/permissionSchema'); const permissionModel = require('../schemas/permissionSchema');
const csrfUtils = require('../utils/csrfUtils');
const {exceptionHandler, errorHandler} = require('../utils/loggerUtils');
//root index functions //root index functions
module.exports.get = async function(req, res){ module.exports.get = async function(req, res){
@ -38,7 +39,7 @@ module.exports.get = async function(req, res){
throw new Error("Channel not found."); throw new Error("Channel not found.");
} }
return res.render('channelSettings', {instance: config.instanceName, user: req.session.user, channel: chanDB, reqRank, rankEnum: permissionModel.rankEnum}); return res.render('channelSettings', {instance: config.instanceName, user: req.session.user, channel: chanDB, reqRank, rankEnum: permissionModel.rankEnum, csrfToken: csrfUtils.generateToken(req)});
}catch(err){ }catch(err){
return exceptionHandler(res, err); return exceptionHandler(res, err);
} }

View file

@ -18,14 +18,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
const config = require('../../config.json'); const config = require('../../config.json');
//local imports //local imports
const {exceptionHandler, errorHandler} = require('../utils/loggerUtils');
const channelModel = require('../schemas/channel/channelSchema'); const channelModel = require('../schemas/channel/channelSchema');
const csrfUtils = require('../utils/csrfUtils');
const {exceptionHandler, errorHandler} = require('../utils/loggerUtils');
//root index functions //root index functions
module.exports.get = async function(req, res){ module.exports.get = async function(req, res){
try{ try{
const chanGuide = await channelModel.getChannelList(); const chanGuide = await channelModel.getChannelList();
return res.render('index', {instance: config.instanceName, user: req.session.user, chanGuide: chanGuide}); return res.render('index', {instance: config.instanceName, user: req.session.user, chanGuide: chanGuide, csrfToken: csrfUtils.generateToken(req)});
}catch(err){ }catch(err){
return exceptionHandler(res, err); return exceptionHandler(res, err);
} }

View file

@ -23,6 +23,7 @@ const {validationResult, matchedData} = require('express-validator');
//Local Imports //Local Imports
const sessionUtils = require('../utils/sessionUtils'); const sessionUtils = require('../utils/sessionUtils');
const altchaUtils = require('../utils/altchaUtils'); const altchaUtils = require('../utils/altchaUtils');
const csrfUtils = require('../utils/csrfUtils');
//register page functions //register page functions
module.exports.get = async function(req, res){ module.exports.get = async function(req, res){
@ -44,7 +45,7 @@ module.exports.get = async function(req, res){
//if we have previous attempts for this user //if we have previous attempts for this user
if(attempts != null){ if(attempts != null){
if(attempts.count > sessionUtils.maxAttempts){ if(attempts.count > sessionUtils.maxAttempts){
return res.render('lockedAccount', {instance: config.instanceName, user: req.session.user}); return res.render('lockedAccount', {instance: config.instanceName, user: req.session.user, csrfToken: csrfUtils.generateToken(req)});
} }
//If the users login's are being throttled //If the users login's are being throttled
@ -55,16 +56,16 @@ module.exports.get = async function(req, res){
const challenge = await altchaUtils.genCaptcha(difficulty, user); const challenge = await altchaUtils.genCaptcha(difficulty, user);
//Render page //Render page
return res.render('login', {instance: config.instanceName, user: req.session.user, challenge}); return res.render('login', {instance: config.instanceName, user: req.session.user, challenge, csrfToken: csrfUtils.generateToken(req)});
} }
//otherwise //otherwise
}else{ }else{
//Render generic page //Render generic page
return res.render('login', {instance: config.instanceName, user: req.session.user, challenge: null}); return res.render('login', {instance: config.instanceName, user: req.session.user, challenge: null, csrfToken: csrfUtils.generateToken(req)});
} }
//if we received invalid input //if we received invalid input
}else{ }else{
//Render pretend nothing happened, send out a generic page //Render pretend nothing happened, send out a generic page
return res.render('login', {instance: config.instanceName, user: req.session.user, challenge: null}); return res.render('login', {instance: config.instanceName, user: req.session.user, challenge: null, csrfToken: csrfUtils.generateToken(req)});
} }
} }

View file

@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//Local Imports //Local Imports
const altchaUtils = require('../utils/altchaUtils'); const altchaUtils = require('../utils/altchaUtils');
const csrfUtils = require('../utils/csrfUtils');
//Config //Config
const config = require('../../config.json'); const config = require('../../config.json');
@ -26,5 +27,5 @@ module.exports.get = async function(req, res){
const challenge = await altchaUtils.genCaptcha(); const challenge = await altchaUtils.genCaptcha();
//render the page //render the page
return res.render('newChannel', {instance: config.instanceName, user: req.session.user, challenge}); return res.render('newChannel', {instance: config.instanceName, user: req.session.user, challenge, csrfToken: csrfUtils.generateToken(req)});
} }

View file

@ -22,6 +22,7 @@ const {validationResult, matchedData} = require('express-validator');
//Local Imports //Local Imports
const altchaUtils = require('../utils/altchaUtils'); const altchaUtils = require('../utils/altchaUtils');
const csrfUtils = require('../utils/csrfUtils');
//register page functions //register page functions
module.exports.get = async function(req, res){ module.exports.get = async function(req, res){
@ -46,11 +47,11 @@ module.exports.get = async function(req, res){
*/ */
//Render page //Render page
return res.render('passwordReset', {instance: config.instanceName, user: req.session.user, challenge, token}); return res.render('passwordReset', {instance: config.instanceName, user: req.session.user, challenge, token, csrfToken: csrfUtils.generateToken(req)});
//If we didn't get a valid token //If we didn't get a valid token
}else{ }else{
//otherwise render generic page //otherwise render generic page
return res.render('passwordReset', {instance: config.instanceName, user: req.session.user, challenge, token: null}); return res.render('passwordReset', {instance: config.instanceName, user: req.session.user, challenge, token: null, csrfToken: csrfUtils.generateToken(req)});
} }
}catch(err){ }catch(err){
return exceptionHandler(res, err); return exceptionHandler(res, err);

View file

@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//Local Imports //Local Imports
const {userModel} = require('../schemas/user/userSchema'); const {userModel} = require('../schemas/user/userSchema');
const csrfUtils = require('../utils/csrfUtils');
const {exceptionHandler, errorHandler} = require('../utils/loggerUtils'); const {exceptionHandler, errorHandler} = require('../utils/loggerUtils');
//Config //Config
@ -27,18 +28,21 @@ module.exports.get = async function(req, res){
try{ try{
var profileName = req.url.slice(1) == '' ? (req.session.user ? req.session.user.user : null) : req.url.slice(1); var profileName = req.url.slice(1) == '' ? (req.session.user ? req.session.user.user : null) : req.url.slice(1);
const profile = await userModel.findProfile({user: profileName}) const profile = await userModel.findProfile({user: profileName});
if(profile){ if(profile){
res.render('profile', { res.render('profile', {
instance: config.instanceName, instance: config.instanceName,
user: req.session.user, user: req.session.user,
profile profile,
csrfToken: csrfUtils.generateToken(req)
}); });
}else{ }else{
res.render('profile', {instance: config.instanceName, res.render('profile', {
instance: config.instanceName,
user: req.session.user, user: req.session.user,
profile: null profile: null,
csrfToken: csrfUtils.generateToken(req)
}); });
} }
}catch(err){ }catch(err){

View file

@ -19,6 +19,7 @@ const config = require('../../config.json');
//Local Imports //Local Imports
const altchaUtils = require('../utils/altchaUtils'); const altchaUtils = require('../utils/altchaUtils');
const csrfUtils = require('../utils/csrfUtils');
//register page functions //register page functions
module.exports.get = async function(req, res){ module.exports.get = async function(req, res){
@ -26,5 +27,5 @@ module.exports.get = async function(req, res){
const challenge = await altchaUtils.genCaptcha(); const challenge = await altchaUtils.genCaptcha();
//Render page //Render page
return res.render('register', {instance: config.instanceName, user: req.session.user, challenge}); return res.render('register', {instance: config.instanceName, user: req.session.user, challenge, csrfToken: csrfUtils.generateToken(req)});
} }

View file

@ -269,10 +269,13 @@ userSchema.statics.authenticate = async function(user, pass){
} }
userSchema.statics.findProfile = async function(user){ userSchema.statics.findProfile = async function(user){
//Catch null users
if(user == null || user.user == null){
return null;
//If someone's looking for tokebot //If someone's looking for tokebot
if(user.user.toLowerCase() == "tokebot"){ }else if(user.user.toLowerCase() == "tokebot"){
//fake a profile hashtable for tokebot //fake a profile hashtable for tokebot
profile = { const profile = {
id: -420, id: -420,
user: "Tokebot", user: "Tokebot",
date: (await statModel.getStats()).firstLaunch, date: (await statModel.getStats()).firstLaunch,

26
src/utils/csrfUtils.js Normal file
View file

@ -0,0 +1,26 @@
/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//NPM Imports
const { csrfSync } = require('csrf-sync');
//Pull needed methods from csrfSync
const {generateToken, revokeToken, csrfSynchronisedProtection,} = csrfSync();
//Export them per csrfSync documentation
module.exports.generateToken = generateToken;
module.exports.revokeToken = revokeToken;
module.exports.csrfSynchronisedProtection = csrfSynchronisedProtection;

View file

@ -14,7 +14,7 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//local imports //Local Imports
const {userModel} = require('../schemas/user/userSchema'); const {userModel} = require('../schemas/user/userSchema');
const userBanModel = require('../schemas/user/userBanSchema') const userBanModel = require('../schemas/user/userBanSchema')
const altchaUtils = require('../utils/altchaUtils'); const altchaUtils = require('../utils/altchaUtils');
@ -71,7 +71,6 @@ module.exports.authenticateSession = async function(user, pass, req){
//Not sure if this is just how connect-mongo is implemented or if it's an express issue, but connect-mongodb-session seems to not implement the all() function what so ever... //Not sure if this is just how connect-mongo is implemented or if it's an express issue, but connect-mongodb-session seems to not implement the all() function what so ever...
req.session.seshid = req.session.id; req.session.seshid = req.session.id;
req.session.authdate = new Date(); req.session.authdate = new Date();
req.session.authip = req.ip;
req.session.user = { req.session.user = {
user: userDB.user, user: userDB.user,
id: userDB.id, id: userDB.id,

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/adminPanel.css"> <link rel="stylesheet" type="text/css" href="/css/adminPanel.css">
<title><%= instance %> - Admin Panel</title> <title><%= instance %> - Admin Panel</title>
</head> </head>

View file

@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/channel.css"> <link rel="stylesheet" type="text/css" href="/css/channel.css">
<link rel="stylesheet" type="text/css" href="/css/panel.css"> <link rel="stylesheet" type="text/css" href="/css/panel.css">
<title><%= instance %> - *DISCONNECTED*</title> <title><%= instance %> - *DISCONNECTED*</title>

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<!-- Gonna be a shitter and re-use the adminPanel css :P --> <!-- Gonna be a shitter and re-use the adminPanel css :P -->
<link rel="stylesheet" type="text/css" href="/css/adminPanel.css"> <link rel="stylesheet" type="text/css" href="/css/adminPanel.css">
<title><%= instance %> - <%= channel.name %> - Channel Settings</title> <title><%= instance %> - <%= channel.name %> - Channel Settings</title>

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="css/index.css"> <link rel="stylesheet" type="text/css" href="css/index.css">
<title><%= instance %></title> <title><%= instance %></title>
</head> </head>

View file

@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/login.css"> <link rel="stylesheet" type="text/css" href="/css/login.css">
<title><%= instance %> - Account Locked!</title> <title><%= instance %> - Account Locked!</title>
</head> </head>

View file

@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/login.css"> <link rel="stylesheet" type="text/css" href="/css/login.css">
<link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css"> <link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css">
<title><%= instance %> - Log-In</title> <title><%= instance %> - Log-In</title>

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/newChannel.css"> <link rel="stylesheet" type="text/css" href="/css/newChannel.css">
<link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css"> <link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css">
<title><%= instance %> - New Channel</title> <title><%= instance %> - New Channel</title>

View file

@ -0,0 +1,16 @@
<!--Canopy - The next generation of stoner streaming software
Copyright (C) 2024 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<meta name="csrf-token" content="<%= csrfToken %>">

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/passwordReset.css"> <link rel="stylesheet" type="text/css" href="/css/passwordReset.css">
<link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css"> <link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css">
<title><%= instance %></title> <title><%= instance %></title>

View file

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<% var selfProfile = user ? profile ? profile.user == user.user : false : false %> <% var selfProfile = user ? profile ? profile.user == user.user : false : false %>
<link rel="stylesheet" type="text/css" href="/css/profile.css"> <link rel="stylesheet" type="text/css" href="/css/profile.css">
<% if(profile){ %> <% if(profile){ %>

View file

@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<html> <html>
<head> <head>
<%- include('partial/styles', {instance, user}); %> <%- include('partial/styles', {instance, user}); %>
<%- include('partial/csrfToken', {csrfToken}); %>
<link rel="stylesheet" type="text/css" href="/css/register.css"> <link rel="stylesheet" type="text/css" href="/css/register.css">
<link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css"> <link rel="stylesheet" type="text/css" href="/lib/altcha/altcha.css">
<title><%= instance %> - Account Registration</title> <title><%= instance %> - Account Registration</title>