From 7e3d340feca9879f661d38b21d429150c676cdbe Mon Sep 17 00:00:00 2001 From: zefie Date: Sun, 8 Aug 2021 19:40:52 -0400 Subject: [PATCH] improve session retention --- zefie_wtvp_minisrv/WTVClientSessionData.js | 49 ++++++++++++++++------ zefie_wtvp_minisrv/app.js | 37 ++++++++++------ 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/zefie_wtvp_minisrv/WTVClientSessionData.js b/zefie_wtvp_minisrv/WTVClientSessionData.js index bf1099dd..082f6e97 100644 --- a/zefie_wtvp_minisrv/WTVClientSessionData.js +++ b/zefie_wtvp_minisrv/WTVClientSessionData.js @@ -1,3 +1,5 @@ +const { lib } = require('crypto-js'); + class WTVClientSessionData { fs = require('fs'); @@ -38,7 +40,8 @@ class WTVClientSessionData { } } - constructor(hide_ssid_in_logs, session_storage_directory) { + constructor(ssid, hide_ssid_in_logs, session_storage_directory) { + this.ssid = ssid; if (hide_ssid_in_logs) this.hide_ssid_in_logs = hide_ssid_in_logs; if (!session_storage_directory) session_storage_directory = __dirname + "/SessionStore"; this.session_storage = session_storage_directory; @@ -172,11 +175,13 @@ class WTVClientSessionData { return outstring; } - loadSessionData() { + loadSessionData(raw_data = false) { try { if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) { - var session_data_file = this.fs.readFileSync(this.session_storage + this.path.sep + this.ssid + ".json", 'Utf8'); - var session_data = JSON.parse(session_data_file); + var json_data = this.fs.readFileSync(this.session_storage + this.path.sep + this.ssid + ".json", 'Utf8') + if (raw_data) return json_data; + + var session_data = JSON.parse(json_data); this.session_store = session_data; return true; } @@ -188,14 +193,17 @@ class WTVClientSessionData { } saveSessionData() { - if (!this.session_store.registered) { - var temp_store = this.session_store; - this.loadSessionData(); - this.session_store = Object.assign(this.session_store, temp_store); - } + // load data from disk and merge new data + var temp_store = this.session_store; + if (this.loadSessionData()) this.session_store = Object.assign(this.session_store, temp_store); + else this.session_store = temp_store; + temp_store = null; + try { - var store_data = JSON.stringify(this.session_store); - this.fs.writeFileSync(this.session_storage + this.path.sep + this.ssid + ".json", store_data, "Utf8"); + // only save if file has changed + var json_save_data = JSON.stringify(this.session_store); + var json_load_data = loadSessionData(true); + if (json_save_data != json_load_data) this.fs.writeFileSync(this.session_storage + this.path.sep + this.ssid + ".json", JSON.stringify(this.session_store), "Utf8"); return true; } catch (e) { console.error(" # Error saving session data for", this.filterSSID(this.ssid), e); @@ -213,16 +221,31 @@ class WTVClientSessionData { return this.saveSessionData(); } + SaveIfRegistered() { + if (this.isRegistered()) this.saveSessionData(); + } + + isRegistered() { + var self = this; + var ssid_match = false; + this.fs.readdirSync(this.session_storage).forEach(file => { + if (!file.match(/.*\.json/ig)) return; + if (ssid_match) return; + if (file.split('.')[0] == self.ssid) ssid_match = true; + }); + return ssid_match; + } unregisterBox() { try { if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) { - return this.fs.unlinkSync(this.session_storage + this.path.sep + this.ssid + ".json"); + this.fs.unlinkSync(this.session_storage + this.path.sep + this.ssid + ".json"); this.session_store = {}; + return true; } } catch (e) { // Don't log error 'file not found', it just means the client isn't registered yet - if (e.code != "ENOENT") console.error(" # Error deleting session data for", this.filterSSID(this.ssid), e); + console.error(" # Error deleting session data for", this.filterSSID(this.ssid), e); return false; } } diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index 95752d97..e88752a1 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -279,8 +279,8 @@ function filterSSID(obj) { return obj.substr(0, 6) + ('*').repeat(9); } } else { - if (obj["wtv-client-serial-number"]) { - var ssid = obj["wtv-client-serial-number"]; + if (makeSafeSSID(obj["wtv-client-serial-number"])) { + var ssid = makeSafeSSID(obj["wtv-client-serial-number"]); if (ssid.substr(0, 8) == "MSTVSIMU") { obj["wtv-client-serial-number"] = ssid.substr(0, 10) + ('*').repeat(10) + ssid.substr(20); } else if (ssid.substr(0, 5) == "1SEGA") { @@ -296,6 +296,12 @@ function filterSSID(obj) { } } +function makeSafeSSID(ssid) { + ssid = ssid.replace(/[^a-zA-Z0-9]/g, ""); + if (ssid.length == 0) ssid = null; + return ssid; +} + function makeSafePath(base, target) { target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, ""); if (path.sep != "/") target = target.replace(/\//g, path.sep); @@ -356,8 +362,11 @@ async function processURL(socket, request_headers) { if (shortURL.indexOf(':/') >= 0 && shortURL.indexOf('://') < 0) { var ssid = socket.ssid; if (ssid == null) { - ssid = request_headers["wtv-client-serial-number"]; + // prevent possible injection attacks via SSID and filesystem SessionStore + ssid = makeSafeSSID(request_headers["wtv-client-serial-number"]); + if (ssid == "") ssid = null; } + var reqverb = "Request"; if (request_headers.encrypted || request_headers.secure) { reqverb = "Encrypted " + reqverb; @@ -757,7 +766,7 @@ function isUnencryptedString(string, verbose = false) { } function filterSSID(ssid) { - var WTVCSD = new WTVClientSessionData(minisrv_config.config.hide_ssid_in_logs); + var WTVCSD = new WTVClientSessionData(null,minisrv_config.config.hide_ssid_in_logs); return WTVCSD.filterSSID(ssid); } @@ -827,14 +836,17 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq if (!headers) return; - if (headers["wtv-client-serial-number"] != null) { - socket.ssid = headers["wtv-client-serial-number"]; - if (!ssid_sessions[socket.ssid]) { - ssid_sessions[socket.ssid] = new WTVClientSessionData(minisrv_config.config.hide_ssid_in_logs); + if (headers["wtv-client-serial-number"] != null && socket.ssid == null) { + socket.ssid = makeSafeSSID(headers["wtv-client-serial-number"]); + if (socket.ssid != null) { + if (!ssid_sessions[socket.ssid]) { + ssid_sessions[socket.ssid] = new WTVClientSessionData(socket.ssid,minisrv_config.config.hide_ssid_in_logs); + ssid_sessions[socket.ssid].SaveIfRegistered(); + } + if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Set(); + ssid_sessions[socket.ssid].ssid = socket.ssid; + ssid_sessions[socket.ssid].data_store.sockets.add(socket); } - if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Set(); - ssid_sessions[socket.ssid].ssid = socket.ssid; - ssid_sessions[socket.ssid].data_store.sockets.add(socket); } var ip2long = function (ip) { @@ -920,7 +932,8 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq if (headers["wtv-capability-flags"] != null) { if (!ssid_sessions[socket.ssid]) { - ssid_sessions[socket.ssid] = new WTVClientSessionData(minisrv_config.config.hide_ssid_in_logs); + ssid_sessions[socket.ssid] = new WTVClientSessionData(socket.ssid,minisrv_config.config.hide_ssid_in_logs); + ssid_sessions[socket.ssid].SaveIfRegistered(); } if (!ssid_sessions[socket.ssid].capabilities) ssid_sessions[socket.ssid].capabilities = new WTVClientCapabilities(headers["wtv-capability-flags"]); }