feat(clickTtHttpPageRoutes): add sensitive data masking and form body summarization
- Introduced functions to mask sensitive values in form submissions and summarize form body data, enhancing security and logging capabilities. - Updated the proxy POST route to log detailed information about submitted forms, including field counts and redacted values for sensitive keys. - Enhanced error handling for URL-encoded body parsing, improving robustness in form data processing.
This commit is contained in:
@@ -56,6 +56,41 @@ function serializeFormBody(req) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function maskSensitiveValue(key, value) {
|
||||
if (!key) return value;
|
||||
return /(pass|password|passwd|pwd|token|secret)/i.test(String(key)) ? '[redacted]' : value;
|
||||
}
|
||||
|
||||
function summarizeFormBody(body) {
|
||||
if (!body || typeof body !== 'string') {
|
||||
return { fieldCount: 0, fields: [] };
|
||||
}
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams(body);
|
||||
const fields = [];
|
||||
|
||||
for (const [key, value] of params.entries()) {
|
||||
fields.push({
|
||||
key,
|
||||
value: maskSensitiveValue(key, value),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
fieldCount: fields.length,
|
||||
fields,
|
||||
parseError: null,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
fieldCount: 0,
|
||||
fields: [],
|
||||
parseError: 'body is not URL-encoded',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Domains, deren Links durch den Proxy umgeleitet werden (für Folge-Logs) */
|
||||
const PROXY_DOMAINS = ['click-tt.de', 'httv.de', 'liga.nu'];
|
||||
|
||||
@@ -287,6 +322,7 @@ function injectProxyNavigationScript(html, proxyBaseUrl, pageBaseUrl, sid) {
|
||||
'if(!anchor||event.defaultPrevented)return;',
|
||||
"var targetUrl=normalizeUrl(anchor.getAttribute('href'),PAGE_BASE_URL);",
|
||||
'if(!targetUrl||!shouldProxyUrl(targetUrl))return;',
|
||||
"try{console.log('[ClickTT Proxy] anchor click',{href:anchor.getAttribute('href'),targetUrl:targetUrl,text:(anchor.textContent||'').trim().slice(0,120)});}catch(e){}",
|
||||
'event.preventDefault();',
|
||||
'navigateViaProxy(targetUrl);',
|
||||
'},false);',
|
||||
@@ -296,6 +332,17 @@ function injectProxyNavigationScript(html, proxyBaseUrl, pageBaseUrl, sid) {
|
||||
'var submitter=event.submitter||null;',
|
||||
'var targetUrl=getSubmitTarget(form,submitter);',
|
||||
'if(!targetUrl||!shouldProxyUrl(targetUrl))return;',
|
||||
'try{',
|
||||
'var formDataEntries=[];',
|
||||
'if(window.FormData){',
|
||||
'var formData=new FormData(form);',
|
||||
'formData.forEach(function(value,key){',
|
||||
"var safeValue=/(pass|password|passwd|pwd|token|secret)/i.test(String(key))?'[redacted]':String(value);",
|
||||
'formDataEntries.push({key:key,value:safeValue});',
|
||||
'});',
|
||||
'}',
|
||||
"console.log('[ClickTT Proxy] form submit',{action:form.getAttribute('action'),targetUrl:targetUrl,submitterName:submitter&&submitter.name?submitter.name:null,submitterValue:submitter&&submitter.value?submitter.value:null,fieldCount:formDataEntries.length,fields:formDataEntries.slice(0,40)});",
|
||||
'}catch(e){}',
|
||||
"form.setAttribute('action',buildProxyUrl(targetUrl));",
|
||||
'},true);',
|
||||
'if(window.fetch){',
|
||||
@@ -321,6 +368,7 @@ function injectProxyNavigationScript(html, proxyBaseUrl, pageBaseUrl, sid) {
|
||||
'var nativeSubmit=window.HTMLFormElement.prototype.submit;',
|
||||
'window.HTMLFormElement.prototype.submit=function patchedSubmit(){',
|
||||
'var targetUrl=getSubmitTarget(this,null);',
|
||||
'try{console.log("[ClickTT Proxy] direct form.submit()",{action:this.getAttribute("action"),targetUrl:targetUrl});}catch(e){}',
|
||||
"if(targetUrl&&shouldProxyUrl(targetUrl)){this.setAttribute('action',buildProxyUrl(targetUrl));}",
|
||||
'return nativeSubmit.apply(this,arguments);',
|
||||
'};',
|
||||
@@ -469,6 +517,16 @@ router.post('/proxy', async (req, res, next) => {
|
||||
|
||||
const contentType = req.get('content-type') || 'application/x-www-form-urlencoded';
|
||||
const body = serializeFormBody(req);
|
||||
const bodySummary = summarizeFormBody(body);
|
||||
|
||||
console.log('[ClickTT Proxy POST]', JSON.stringify({
|
||||
sid,
|
||||
targetUrl,
|
||||
contentType,
|
||||
fieldCount: bodySummary.fieldCount,
|
||||
fields: bodySummary.fields,
|
||||
parseError: bodySummary.parseError,
|
||||
}));
|
||||
|
||||
const result = await clickTtHttpPageService.fetchWithLogging({
|
||||
url: targetUrl,
|
||||
@@ -658,10 +716,9 @@ router.get('/fetch', authenticate, async (req, res, next) => {
|
||||
return res.status(400).json({ error: 'url ist erforderlich' });
|
||||
}
|
||||
|
||||
// Nur click-tt.de und httv.de erlauben
|
||||
if (!url.includes('click-tt.de') && !url.includes('httv.de')) {
|
||||
if (!isAllowedProxyUrl(url)) {
|
||||
return res.status(400).json({
|
||||
error: 'Nur URLs von click-tt.de oder httv.de sind erlaubt',
|
||||
error: 'Nur URLs von click-tt.de, httv.de oder liga.nu sind erlaubt',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user