SecurityManager: Restrict valid author token values

pull/5460/head
Richard Hansen 2022-02-28 19:27:52 -05:00
parent b89ae69202
commit 6d4085f5f0
3 changed files with 25 additions and 1 deletions

View File

@ -28,6 +28,7 @@ const settings = require('../utils/Settings');
const webaccess = require('../hooks/express/webaccess');
const log4js = require('log4js');
const authLogger = log4js.getLogger('auth');
const {padutils} = require('../../static/js/pad_utils');
const DENY = Object.freeze({accessStatus: 'deny'});
@ -106,6 +107,11 @@ exports.checkAccess = async (padID, sessionCookie, token, userSettings) => {
authLogger.debug('access denied: HTTP API session is required');
return DENY;
}
if (!sessionAuthorID && token != null && !padutils.isValidAuthorToken(token)) {
// The author token should be kept secret, so do not log it.
authLogger.debug('access denied: invalid author token');
return DENY;
}
const grant = {
accessStatus: 'grant',

View File

@ -177,7 +177,7 @@ const sendClientReady = (isReconnect) => {
}
let token = Cookies.get('token');
if (token == null) {
if (token == null || !padutils.isValidAuthorToken(token)) {
token = padutils.generateAuthorToken();
Cookies.set('token', token, {expires: 60});
}

View File

@ -88,6 +88,9 @@ const urlRegex = (() => {
`(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g');
})();
// https://stackoverflow.com/a/68957976
const base64url = /^(?=(?:.{4})*$)[A-Za-z0-9_-]*(?:[AQgw]==|[AEIMQUYcgkosw048]=)?$/;
const padutils = {
/**
* Prints a warning message followed by a stack trace (to make it easier to figure out what code
@ -328,6 +331,21 @@ const padutils = {
}
}),
/**
* Returns whether a string has the expected format to be used as a secret token identifying an
* author. The format is defined as: 't.' followed by a non-empty base64url string (RFC 4648
* section 5 with padding).
*
* Being strict about what constitutes a valid token enables unambiguous extensibility (e.g.,
* conditional transformation of a token to a database key in a way that does not allow a
* malicious user to impersonate another user).
*/
isValidAuthorToken: (t) => {
if (typeof t !== 'string' || !t.startsWith('t.')) return false;
const v = t.slice(2);
return v.length > 0 && base64url.test(v);
},
/**
* Returns a string that can be used in the `token` cookie as a secret that authenticates a
* particular author.