mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Security Fix: Computational Resource Abuse in Export endpoints.
Thanks to Anynymous Security Researcher and xet7 !
This commit is contained in:
parent
c481443667
commit
d0f118e7af
3 changed files with 160 additions and 2 deletions
106
models/export.js
106
models/export.js
|
|
@ -32,8 +32,37 @@ if (Meteor.isServer) {
|
|||
let user = null;
|
||||
let impersonateDone = false;
|
||||
let adminId = null;
|
||||
|
||||
// First check if board exists and is public to avoid unnecessary authentication
|
||||
const board = ReactiveCache.getBoard(boardId);
|
||||
if (!board) {
|
||||
JsonRoutes.sendResult(res, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
// If board is public, skip expensive authentication operations
|
||||
if (board.isPublic()) {
|
||||
// Public boards don't require authentication - skip hash operations
|
||||
const exporter = new Exporter(boardId);
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: exporter.build(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Only perform expensive authentication for private boards
|
||||
const loginToken = req.query.authToken;
|
||||
if (loginToken) {
|
||||
// Validate token length to prevent resource abuse
|
||||
if (loginToken.length > 10000) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Suspiciously long auth token received, rejecting to prevent resource abuse');
|
||||
}
|
||||
JsonRoutes.sendResult(res, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const hashToken = Accounts._hashLoginToken(loginToken);
|
||||
user = ReactiveCache.getUser({
|
||||
'services.resume.loginTokens.hashedToken': hashToken,
|
||||
|
|
@ -44,6 +73,7 @@ if (Meteor.isServer) {
|
|||
Authentication.checkUserId(req.userId);
|
||||
user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
|
||||
}
|
||||
|
||||
const exporter = new Exporter(boardId);
|
||||
if (exporter.canExport(user) || impersonateDone) {
|
||||
if (impersonateDone) {
|
||||
|
|
@ -94,8 +124,37 @@ if (Meteor.isServer) {
|
|||
let user = null;
|
||||
let impersonateDone = false;
|
||||
let adminId = null;
|
||||
|
||||
// First check if board exists and is public to avoid unnecessary authentication
|
||||
const board = ReactiveCache.getBoard(boardId);
|
||||
if (!board) {
|
||||
JsonRoutes.sendResult(res, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
// If board is public, skip expensive authentication operations
|
||||
if (board.isPublic()) {
|
||||
// Public boards don't require authentication - skip hash operations
|
||||
const exporter = new Exporter(boardId, attachmentId);
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: exporter.build(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Only perform expensive authentication for private boards
|
||||
const loginToken = req.query.authToken;
|
||||
if (loginToken) {
|
||||
// Validate token length to prevent resource abuse
|
||||
if (loginToken.length > 10000) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Suspiciously long auth token received, rejecting to prevent resource abuse');
|
||||
}
|
||||
JsonRoutes.sendResult(res, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const hashToken = Accounts._hashLoginToken(loginToken);
|
||||
user = ReactiveCache.getUser({
|
||||
'services.resume.loginTokens.hashedToken': hashToken,
|
||||
|
|
@ -106,6 +165,7 @@ if (Meteor.isServer) {
|
|||
Authentication.checkUserId(req.userId);
|
||||
user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
|
||||
}
|
||||
|
||||
const exporter = new Exporter(boardId, attachmentId);
|
||||
if (exporter.canExport(user) || impersonateDone) {
|
||||
if (impersonateDone) {
|
||||
|
|
@ -148,8 +208,53 @@ if (Meteor.isServer) {
|
|||
let user = null;
|
||||
let impersonateDone = false;
|
||||
let adminId = null;
|
||||
|
||||
// First check if board exists and is public to avoid unnecessary authentication
|
||||
const board = ReactiveCache.getBoard(boardId);
|
||||
if (!board) {
|
||||
res.writeHead(404);
|
||||
res.end('Board not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// If board is public, skip expensive authentication operations
|
||||
if (board.isPublic()) {
|
||||
// Public boards don't require authentication - skip hash operations
|
||||
const exporter = new Exporter(boardId);
|
||||
|
||||
if( params.query.delimiter == "\t" ) {
|
||||
// TSV file
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/tsv',
|
||||
});
|
||||
}
|
||||
else {
|
||||
// CSV file (comma or semicolon)
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/csv; charset=utf-8',
|
||||
});
|
||||
// Adding UTF8 BOM to quick fix MS Excel issue
|
||||
// use Uint8Array to prevent from converting bytes to string
|
||||
res.write(new Uint8Array([0xEF, 0xBB, 0xBF]));
|
||||
}
|
||||
res.write(exporter.buildCsv(params.query.delimiter, 'en'));
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only perform expensive authentication for private boards
|
||||
const loginToken = params.query.authToken;
|
||||
if (loginToken) {
|
||||
// Validate token length to prevent resource abuse
|
||||
if (loginToken.length > 10000) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Suspiciously long auth token received, rejecting to prevent resource abuse');
|
||||
}
|
||||
res.writeHead(400);
|
||||
res.end('Invalid token');
|
||||
return;
|
||||
}
|
||||
|
||||
const hashToken = Accounts._hashLoginToken(loginToken);
|
||||
user = ReactiveCache.getUser({
|
||||
'services.resume.loginTokens.hashedToken': hashToken,
|
||||
|
|
@ -163,6 +268,7 @@ if (Meteor.isServer) {
|
|||
isAdmin: true,
|
||||
});
|
||||
}
|
||||
|
||||
const exporter = new Exporter(boardId);
|
||||
if (exporter.canExport(user) || impersonateDone) {
|
||||
if (impersonateDone) {
|
||||
|
|
|
|||
|
|
@ -35,8 +35,34 @@ runOnServer(function() {
|
|||
let user = null;
|
||||
let impersonateDone = false;
|
||||
let adminId = null;
|
||||
|
||||
// First check if board exists and is public to avoid unnecessary authentication
|
||||
const board = ReactiveCache.getBoard(boardId);
|
||||
if (!board) {
|
||||
res.end('Board not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// If board is public, skip expensive authentication operations
|
||||
if (board.isPublic()) {
|
||||
// Public boards don't require authentication - skip hash operations
|
||||
const exporterExcel = new ExporterExcel(boardId, 'en');
|
||||
exporterExcel.build(res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only perform expensive authentication for private boards
|
||||
const loginToken = params.query.authToken;
|
||||
if (loginToken) {
|
||||
// Validate token length to prevent resource abuse
|
||||
if (loginToken.length > 10000) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Suspiciously long auth token received, rejecting to prevent resource abuse');
|
||||
}
|
||||
res.end('Invalid token');
|
||||
return;
|
||||
}
|
||||
|
||||
const hashToken = Accounts._hashLoginToken(loginToken);
|
||||
user = ReactiveCache.getUser({
|
||||
'services.resume.loginTokens.hashedToken': hashToken,
|
||||
|
|
|
|||
|
|
@ -37,8 +37,34 @@ runOnServer(function() {
|
|||
let user = null;
|
||||
let impersonateDone = false;
|
||||
let adminId = null;
|
||||
|
||||
// First check if board exists and is public to avoid unnecessary authentication
|
||||
const board = ReactiveCache.getBoard(boardId);
|
||||
if (!board) {
|
||||
res.end('Board not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// If board is public, skip expensive authentication operations
|
||||
if (board.isPublic()) {
|
||||
// Public boards don't require authentication - skip hash operations
|
||||
const exporterCardPDF = new ExporterCardPDF(boardId);
|
||||
exporterCardPDF.build(res);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only perform expensive authentication for private boards
|
||||
const loginToken = params.query.authToken;
|
||||
if (loginToken) {
|
||||
// Validate token length to prevent resource abuse
|
||||
if (loginToken.length > 10000) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.warn('Suspiciously long auth token received, rejecting to prevent resource abuse');
|
||||
}
|
||||
res.end('Invalid token');
|
||||
return;
|
||||
}
|
||||
|
||||
const hashToken = Accounts._hashLoginToken(loginToken);
|
||||
user = ReactiveCache.getUser({
|
||||
'services.resume.loginTokens.hashedToken': hashToken,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue