SecurityManager: Restrict valid author token values
parent
b89ae69202
commit
6d4085f5f0
|
@ -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',
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue