Information Disclosure of Hashed Passwords in TYPO3 Backend Forms
Description
TYPO3 is an open source PHP based web content management system released under the GNU GPL. Password hashes were being reflected in the editing forms of the TYPO3 backend user interface. This allowed attackers to crack the plaintext password using brute force techniques. Exploiting this vulnerability requires a valid backend user account. Users are advised to update to TYPO3 versions 8.7.57 ELTS, 9.5.46 ELTS, 10.4.43 ELTS, 11.5.35 LTS, 12.4.11 LTS, 13.0.1 that fix the problem described. There are no known workarounds for this issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
TYPO3 reflected password hashes in backend editing forms allowing authenticated attackers to crack plaintext passwords via brute-force techniques.
Vulnerability
Overview
CVE-2024-25118 affects TYPO3, an open-source PHP CMS. The backend user interface reflected password hashes in editing forms. This design flaw allowed the password hash to be visible in the HTML source of form fields, making it accessible to any authenticated backend user who could view the page source or inspect network traffic. The root cause was that the form rendering logic directly output the stored hash value into hidden input fields or disabled text fields instead of an obfuscated placeholder [1][3][4].
Exploitation
Prerequisites and Attack Path
Exploitation requires a valid backend user account [1]. An attacker with backend access can browse the user editing forms and extract the password hash from the hidden input field (or disabled field) that contained the actual hash. The commit diffs show the fix replaces the raw hash with getObfuscatedSecretValue() and uses a separate hidden field with disabled state and data attributes [3][4]. Before the patch, the hidden input value attribute contained htmlspecialchars((string)$itemValue), which was the hash [3]. An authenticated user could copy this hash and attempt offline brute-force or dictionary attacks to recover the plaintext password.
Impact
The vulnerability weakens the security of backend user accounts. By exposing password hashes, it enables attackers who already have limited backend access to escalate privileges by cracking the passwords of other users, potentially gaining administrative control. The CVSS score and vector were not provided, but the risk is significant because it reduces the effective strength of password hashing by making the hash directly observable.
Mitigation
TYPO3 released fixes in versions 8.7.57 ELTS, 9.5.46 ELTS, 10.4.43 ELTS, 11.5.35 LTS, 12.4.11 LTS, and 13.0.1 [1]. There are no known workarounds; upgrading to a patched version is required. The fix ensures that password fields in the backend display only obfuscated placeholders and do not expose the hash in hidden inputs.
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
typo3/cms-corePackagist | >= 8.0.0, < 8.7.57 | 8.7.57 |
typo3/cms-corePackagist | >= 9.0.0, < 9.5.46 | 9.5.46 |
typo3/cms-corePackagist | >= 10.0.0, < 10.4.43 | 10.4.43 |
typo3/cms-corePackagist | >= 11.0.0, < 11.5.35 | 11.5.35 |
typo3/cms-corePackagist | >= 12.0.0, < 12.4.11 | 12.4.11 |
typo3/cms-corePackagist | >= 13.0.0, < 13.0.1 | 13.0.1 |
Affected products
2Patches
31186b2fec8a6[SECURITY] Avoid showing password hashes in backend edit forms
4 files changed · +29 −4
Build/Sources/TypeScript/backend/form-engine-validation.ts+6 −0 modified@@ -220,6 +220,9 @@ export default (function() { // Only update fields if value actually changed if (field.value !== newValue) { + if (field.disabled && field.dataset.enableOnModification) { + field.disabled = false; + } field.value = newValue; // After updating the value of the main field, dispatch a "change" event to inform e.g. the "RequestUpdate" // component, which always listens to the main field instead of the "human readable field", about it. @@ -549,6 +552,9 @@ export default (function() { modified = true; } if (modified) { + if (field.disabled && field.dataset.enableOnModification) { + field.disabled = false; + } field.value = newValue; } }
typo3/sysext/backend/Classes/Form/Element/PasswordElement.php+17 −2 modified@@ -84,7 +84,7 @@ public function render(): array $html[] = '<div class="form-wizards-wrap">'; $html[] = '<div class="form-wizards-element">'; $html[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">'; - $html[] = '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="' . ($itemValue ? '*********' : '') . '" type="text" disabled>'; + $html[] = '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="' . htmlspecialchars($this->getObfuscatedSecretValue($itemValue)) . '" type="text" disabled>'; $html[] = '</div>'; $html[] = '</div>'; $html[] = '</div>'; @@ -139,7 +139,7 @@ public function render(): array $mainFieldHtml[] = '<div class="form-wizards-wrap">'; $mainFieldHtml[] = '<div class="form-wizards-element">'; $mainFieldHtml[] = '<input type="password" ' . GeneralUtility::implodeAttributes($attributes, true) . ' />'; - $mainFieldHtml[] = '<input type="hidden" name="' . $itemName . '" value="' . htmlspecialchars((string)$itemValue) . '" />'; + $mainFieldHtml[] = '<input type="hidden" disabled data-enable-on-modification="true" name="' . $itemName . '" value="' . htmlspecialchars($this->getObfuscatedSecretValue($itemValue)) . '" />'; $mainFieldHtml[] = '</div>'; if (!empty($fieldControlHtml)) { $mainFieldHtml[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">'; @@ -266,4 +266,19 @@ private function renderPasswordPolicyRequirements( return implode(LF, $passwordPolicyElement); } + + /** + * Obfuscated a (hashed) password secret with a static string. + * + * @todo + * + server-side password obfuscation value is `*********` (9 chars) + * + client-side password obfuscation value is `********` (8 chars) + */ + protected function getObfuscatedSecretValue(?string $value): string + { + if ($value === null || $value === '') { + return ''; + } + return '*********'; + } }
typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js+1 −1 modified@@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DomHelper from"@typo3/backend/utility/dom-helper.js";import{selector}from"@typo3/core/literals.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"};let formEngineFormElement;const customEvaluations=new Map;return FormEngineValidation.initialize=function(e){formEngineFormElement=e,formEngineFormElement.querySelectorAll("."+FormEngineValidation.errorClass).forEach((e=>e.classList.remove(FormEngineValidation.errorClass))),FormEngineValidation.initializeInputFields(),new RegularEvent("change",((e,n)=>{FormEngineValidation.validateField(n),FormEngineValidation.markFieldAsChanged(n)})).delegateTo(formEngineFormElement,FormEngineValidation.rulesSelector),FormEngineValidation.registerSubmitCallback();const n=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(n),FormEngineValidation.lastDate=FormEngineValidation.getDate(n),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){formEngineFormElement.querySelectorAll(FormEngineValidation.inputSelector).forEach((e=>{const n=JSON.parse(e.dataset.formengineInputParams).field,t=formEngineFormElement.querySelector(selector`[name="${n}"]`);"formengineInputInitialized"in e.dataset||(t.dataset.config=e.dataset.formengineInputParams,FormEngineValidation.initializeInputField(n))}))},FormEngineValidation.initializeInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=Utility.trimExplode(",",e.evalList);let i=n.value;for(let n=0;n<a.length;n++)i=FormEngineValidation.formatValue(a[n],i,e);i.length&&(t.value=i)}new RegularEvent("change",(()=>{FormEngineValidation.updateInputField(t.dataset.formengineInputName)})).bindTo(t),t.dataset.formengineInputInitialized="true"},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=Utility.trimExplode(",",e.evalList);let i=t.value;for(let n=0;n<a.length;n++)i=FormEngineValidation.processValue(a[n],i,e);let o=i;for(let n=0;n<a.length;n++)o=FormEngineValidation.formatValue(a[n],o,e);n.value!==i&&(n.value=i,n.dispatchEvent(new Event("change")),t.value=o)}},FormEngineValidation.validateField=function(e,n){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.validateField() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return n;if(n=n||e.value||"",void 0===e.dataset.formengineValidationRules)return n;const t=JSON.parse(e.dataset.formengineValidationRules);let a=!1,i=0;const o=n;let r,l,s;Array.isArray(n)||(n=n.trimStart());for(const o of t){if(a)break;switch(o.type){case"required":""===n&&(a=!0,e.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:parseInt(e.value,10),void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0))),void 0!==o.lower){const e=1*o.lower;!isNaN(e)&&parseInt(n,10)<e&&(a=!0)}if(void 0!==o.upper){const e=1*o.upper;!isNaN(e)&&parseInt(n,10)>e&&(a=!0)}}break;case"select":case"category":(o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:e instanceof HTMLSelectElement?e.querySelectorAll("option:checked").length:e.querySelectorAll("input[value]:checked").length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"group":case"folder":case"inline":(o.minItems||o.maxItems)&&(i=Utility.trimExplode(",",e.value).length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"min":(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&e.value.length>0&&e.value.length<e.minLength&&(a=!0)}}const m=!a,d=e.closest(FormEngineValidation.markerSelector);return null!==d&&d.classList.toggle(FormEngineValidation.errorClass,!m),FormEngineValidation.markParentTab(e,m),formEngineFormElement.dispatchEvent(new CustomEvent("t3-formengine-postfieldvalidation",{cancelable:!1,bubbles:!0})),o},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&formEngineFormElement.querySelectorAll(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").forEach((e=>{e.classList.remove(FormEngineValidation.errorClass,"has-validation-error")}));const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector))if(null===e.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted")){let n=!1;const t=e.value,a=FormEngineValidation.validateField(e,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)n=!0;else for(let e=0;e<a.length;e++)if(a[e]!==t[e]){n=!0;break}}else a.length&&t!==a&&(n=!0);n&&(e.value=a)}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.markFieldAsChanged() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){DomHelper.parents(e,".tab-pane").forEach((e=>{n&&(n=null===e.querySelector(".has-error"));const t=e.id;formEngineFormElement.querySelector('a[href="#'+t+'"]').closest(".t3js-tabmenu-item").classList.toggle("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((()=>{if(null===document.querySelector("."+FormEngineValidation.errorClass))return!0;const e=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);return e.addEventListener("button.clicked",(()=>e.hideModal())),!1}))},FormEngineValidation}()); \ No newline at end of file +import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DomHelper from"@typo3/backend/utility/dom-helper.js";import{selector}from"@typo3/core/literals.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"};let formEngineFormElement;const customEvaluations=new Map;return FormEngineValidation.initialize=function(e){formEngineFormElement=e,formEngineFormElement.querySelectorAll("."+FormEngineValidation.errorClass).forEach((e=>e.classList.remove(FormEngineValidation.errorClass))),FormEngineValidation.initializeInputFields(),new RegularEvent("change",((e,n)=>{FormEngineValidation.validateField(n),FormEngineValidation.markFieldAsChanged(n)})).delegateTo(formEngineFormElement,FormEngineValidation.rulesSelector),FormEngineValidation.registerSubmitCallback();const n=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(n),FormEngineValidation.lastDate=FormEngineValidation.getDate(n),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){formEngineFormElement.querySelectorAll(FormEngineValidation.inputSelector).forEach((e=>{const n=JSON.parse(e.dataset.formengineInputParams).field,t=formEngineFormElement.querySelector(selector`[name="${n}"]`);"formengineInputInitialized"in e.dataset||(t.dataset.config=e.dataset.formengineInputParams,FormEngineValidation.initializeInputField(n))}))},FormEngineValidation.initializeInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=Utility.trimExplode(",",e.evalList);let i=n.value;for(let n=0;n<a.length;n++)i=FormEngineValidation.formatValue(a[n],i,e);i.length&&(t.value=i)}new RegularEvent("change",(()=>{FormEngineValidation.updateInputField(t.dataset.formengineInputName)})).bindTo(t),t.dataset.formengineInputInitialized="true"},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=Utility.trimExplode(",",e.evalList);let i=t.value;for(let n=0;n<a.length;n++)i=FormEngineValidation.processValue(a[n],i,e);let o=i;for(let n=0;n<a.length;n++)o=FormEngineValidation.formatValue(a[n],o,e);n.value!==i&&(n.disabled&&n.dataset.enableOnModification&&(n.disabled=!1),n.value=i,n.dispatchEvent(new Event("change")),t.value=o)}},FormEngineValidation.validateField=function(e,n){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.validateField() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return n;if(n=n||e.value||"",void 0===e.dataset.formengineValidationRules)return n;const t=JSON.parse(e.dataset.formengineValidationRules);let a=!1,i=0;const o=n;let r,l,s;Array.isArray(n)||(n=n.trimStart());for(const o of t){if(a)break;switch(o.type){case"required":""===n&&(a=!0,e.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:parseInt(e.value,10),void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0))),void 0!==o.lower){const e=1*o.lower;!isNaN(e)&&parseInt(n,10)<e&&(a=!0)}if(void 0!==o.upper){const e=1*o.upper;!isNaN(e)&&parseInt(n,10)>e&&(a=!0)}}break;case"select":case"category":(o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:e instanceof HTMLSelectElement?e.querySelectorAll("option:checked").length:e.querySelectorAll("input[value]:checked").length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"group":case"folder":case"inline":(o.minItems||o.maxItems)&&(i=Utility.trimExplode(",",e.value).length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"min":(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&e.value.length>0&&e.value.length<e.minLength&&(a=!0)}}const m=!a,d=e.closest(FormEngineValidation.markerSelector);return null!==d&&d.classList.toggle(FormEngineValidation.errorClass,!m),FormEngineValidation.markParentTab(e,m),formEngineFormElement.dispatchEvent(new CustomEvent("t3-formengine-postfieldvalidation",{cancelable:!1,bubbles:!0})),o},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&formEngineFormElement.querySelectorAll(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").forEach((e=>{e.classList.remove(FormEngineValidation.errorClass,"has-validation-error")}));const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector))if(null===e.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted")){let n=!1;const t=e.value,a=FormEngineValidation.validateField(e,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)n=!0;else for(let e=0;e<a.length;e++)if(a[e]!==t[e]){n=!0;break}}else a.length&&t!==a&&(n=!0);n&&(e.disabled&&e.dataset.enableOnModification&&(e.disabled=!1),e.value=a)}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.markFieldAsChanged() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){DomHelper.parents(e,".tab-pane").forEach((e=>{n&&(n=null===e.querySelector(".has-error"));const t=e.id;formEngineFormElement.querySelector('a[href="#'+t+'"]').closest(".t3js-tabmenu-item").classList.toggle("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((()=>{if(null===document.querySelector("."+FormEngineValidation.errorClass))return!0;const e=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);return e.addEventListener("button.clicked",(()=>e.hideModal())),!1}))},FormEngineValidation}()); \ No newline at end of file
typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicPasswordCest.php+5 −1 modified@@ -53,14 +53,18 @@ public function _before(ApplicationTester $I, PageTree $pageTree): void */ private function passwordInputFieldsDataProvider(): array { + // @todo + // + server-side password obfuscation value is `*********` (9 chars) + // + client-side password obfuscation value is `********` (8 chars) return [ // @todo add other password field variants [ 'label' => 'password_2', 'inputValue' => 'Kasper', 'expectedValue' => '********', 'expectedInternalValue' => 'Kasper', - 'expectedValueAfterSave' => 'Kasper', + // even if `password_2` is not hashed, it never should expose the value + 'expectedValueAfterSave' => '*********', 'comment' => '', ], ];
cafc5af7fdce[SECURITY] Avoid showing password hashes in backend edit forms
4 files changed · +29 −5
Build/Sources/TypeScript/backend/form-engine-validation.ts+6 −1 modified@@ -234,7 +234,9 @@ export default (function() { for (let i = 0; i < evalList.length; i++) { formattedValue = FormEngineValidation.formatValue(evalList[i], formattedValue, config); } - + if ($mainField.prop('disabled') && $mainField.data('enableOnModification')) { + $mainField.prop('disabled', false); + } $mainField.val(newValue); // After updating the value of the main field, dispatch a "change" event to inform e.g. the "RequestUpdate" // component, which always listens to the main field instead of the "human readable field", about it. @@ -568,6 +570,9 @@ export default (function() { modified = true; } if (modified) { + if ($field.prop('disabled') && $field.data('enableOnModification')) { + $field.prop('disabled', false); + } $field.val(newValue); } }
typo3/sysext/backend/Classes/Form/Element/PasswordElement.php+17 −2 modified@@ -94,7 +94,7 @@ public function render() $html[] = '<div class="form-wizards-wrap">'; $html[] = '<div class="form-wizards-element">'; $html[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">'; - $html[] = '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="' . ($itemValue ? '*********' : '') . '" type="text" disabled>'; + $html[] = '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="' . htmlspecialchars($this->getObfuscatedSecretValue($itemValue)) . '" type="text" disabled>'; $html[] = '</div>'; $html[] = '</div>'; $html[] = '</div>'; @@ -149,7 +149,7 @@ public function render() $mainFieldHtml[] = '<div class="form-wizards-wrap">'; $mainFieldHtml[] = '<div class="form-wizards-element">'; $mainFieldHtml[] = '<input type="password" ' . GeneralUtility::implodeAttributes($attributes, true) . ' />'; - $mainFieldHtml[] = '<input type="hidden" name="' . $itemName . '" value="' . htmlspecialchars((string)$itemValue) . '" />'; + $mainFieldHtml[] = '<input type="hidden" disabled data-enable-on-modification="true" name="' . $itemName . '" value="' . htmlspecialchars($this->getObfuscatedSecretValue($itemValue)) . '" />'; $mainFieldHtml[] = '</div>'; if (!empty($fieldControlHtml)) { $mainFieldHtml[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">'; @@ -276,4 +276,19 @@ private function renderPasswordPolicyRequirements( return implode(LF, $passwordPolicyElement); } + + /** + * Obfuscated a (hashed) password secret with a static string. + * + * @todo + * + server-side password obfuscation value is `*********` (9 chars) + * + client-side password obfuscation value is `********` (8 chars) + */ + protected function getObfuscatedSecretValue(?string $value): string + { + if ($value === null || $value === '') { + return ''; + } + return '*********'; + } }
typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js+1 −1 modified@@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import{selector}from"@typo3/core/literals.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$(selector`[name="${a}"]`);void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$(selector`[name="${e}"]`),t=$(selector`[data-formengine-input-name="${e}"]`);let a=$(selector`[name="${n.data("main-field")}"]`);0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=$(selector`[name="${e}"]`);let t=$(selector`[name="${n.data("main-field")}"]`);0===t.length&&(t=n);const a=$(selector`[data-formengine-input-name="${t.attr("name")}"]`),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i=!1,o=0;const r=n;let l,s,m;Array.isArray(n)||(n=n.trimStart());for(const e of a){if(i)break;switch(e.type){case"required":""===n&&(i=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((e.minItems||e.maxItems)&&(l=$(document).find(selector`[name="${t.dataset.relatedfieldname}"]`),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:parseInt(t.value,10),void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0))),void 0!==e.lower){const t=1*e.lower;!isNaN(t)&&parseInt(n,10)<t&&(i=!0)}if(void 0!==e.upper){const t=1*e.upper;!isNaN(t)&&parseInt(n,10)>t&&(i=!0)}}break;case"select":case"category":(e.minItems||e.maxItems)&&(l=$(document).find(selector`[name="${t.dataset.relatedfieldname}"]`),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"group":case"folder":case"inline":(e.minItems||e.maxItems)&&(o=FormEngineValidation.trimExplode(",",t.value).length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(i=!0)}}const d=!i,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),r},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector)){const n=$(e);if(!n.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const t=n.val(),a=FormEngineValidation.validateField(n,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)e=!0;else for(let n=0;n<a.length;n++)if(a[n]!==t[n]){e=!0;break}}else a.length&&t!==a&&(e=!0);e&&n.val(a)}}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}()); \ No newline at end of file +import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import{selector}from"@typo3/core/literals.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$(selector`[name="${a}"]`);void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$(selector`[name="${e}"]`),t=$(selector`[data-formengine-input-name="${e}"]`);let a=$(selector`[name="${n.data("main-field")}"]`);0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=$(selector`[name="${e}"]`);let t=$(selector`[name="${n.data("main-field")}"]`);0===t.length&&(t=n);const a=$(selector`[data-formengine-input-name="${t.attr("name")}"]`),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.prop("disabled")&&t.data("enableOnModification")&&t.prop("disabled",!1),t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i=!1,o=0;const r=n;let l,s,m;Array.isArray(n)||(n=n.trimStart());for(const e of a){if(i)break;switch(e.type){case"required":""===n&&(i=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((e.minItems||e.maxItems)&&(l=$(document).find(selector`[name="${t.dataset.relatedfieldname}"]`),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:parseInt(t.value,10),void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0))),void 0!==e.lower){const t=1*e.lower;!isNaN(t)&&parseInt(n,10)<t&&(i=!0)}if(void 0!==e.upper){const t=1*e.upper;!isNaN(t)&&parseInt(n,10)>t&&(i=!0)}}break;case"select":case"category":(e.minItems||e.maxItems)&&(l=$(document).find(selector`[name="${t.dataset.relatedfieldname}"]`),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"group":case"folder":case"inline":(e.minItems||e.maxItems)&&(o=FormEngineValidation.trimExplode(",",t.value).length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(i=!0)}}const d=!i,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),r},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector)){const n=$(e);if(!n.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const t=n.val(),a=FormEngineValidation.validateField(n,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)e=!0;else for(let n=0;n<a.length;n++)if(a[n]!==t[n]){e=!0;break}}else a.length&&t!==a&&(e=!0);e&&(n.prop("disabled")&&n.data("enableOnModification")&&n.prop("disabled",!1),n.val(a))}}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}()); \ No newline at end of file
typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicPasswordCest.php+5 −1 modified@@ -53,14 +53,18 @@ public function _before(ApplicationTester $I, PageTree $pageTree): void */ private function passwordInputFieldsDataProvider(): array { + // @todo + // + server-side password obfuscation value is `*********` (9 chars) + // + client-side password obfuscation value is `********` (8 chars) return [ // @todo add other password field variants [ 'label' => 'password_2', 'inputValue' => 'Kasper', 'expectedValue' => '********', 'expectedInternalValue' => 'Kasper', - 'expectedValueAfterSave' => 'Kasper', + // even if `password_2` is not hashed, it never should expose the value + 'expectedValueAfterSave' => '*********', 'comment' => '', ], ];
c7a135c25a14[SECURITY] Avoid showing password hashes in backend edit forms
3 files changed · +27 −4
typo3/sysext/backend/Classes/Form/Element/InputTextElement.php+19 −2 modified@@ -95,7 +95,7 @@ public function render() if ($config['readOnly'] ?? false) { // Early return for read only fields if (in_array('password', $evalList, true)) { - $itemValue = $itemValue ? '*********' : ''; + $itemValue = $this->getObfuscatedSecretValue($itemValue); } $disabledFieldAttributes = [ @@ -240,6 +240,7 @@ public function render() $fieldWizardHtml = $fieldWizardResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); $inputType = 'text'; + $hiddenElementProps = ' value="' . htmlspecialchars((string)$itemValue) . '"'; if (in_array('email', $evalList, true)) { $inputType = 'email'; @@ -255,14 +256,15 @@ public function render() } if (in_array('password', $evalList, true)) { $attributes['spellcheck'] = 'false'; + $hiddenElementProps = ' value="' . htmlspecialchars($this->getObfuscatedSecretValue($itemValue)) . '" disabled data-enable-on-modification="true"'; } $mainFieldHtml = []; $mainFieldHtml[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">'; $mainFieldHtml[] = '<div class="form-wizards-wrap">'; $mainFieldHtml[] = '<div class="form-wizards-element">'; $mainFieldHtml[] = '<input type="' . $inputType . '" ' . GeneralUtility::implodeAttributes($attributes, true) . ' />'; - $mainFieldHtml[] = '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars((string)$itemValue) . '" />'; + $mainFieldHtml[] = '<input type="hidden" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" ' . $hiddenElementProps . ' />'; $mainFieldHtml[] = '</div>'; if (!empty($valuePickerHtml) || !empty($valueSliderHtml) || !empty($fieldControlHtml)) { $mainFieldHtml[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">'; @@ -342,6 +344,21 @@ public function render() return $resultArray; } + /** + * Obfuscated a (hashed) password secret with a static string. + * + * @todo + * + server-side password obfuscation value is `*********` (9 chars) + * + client-side password obfuscation value is `********` (8 chars) + */ + protected function getObfuscatedSecretValue(?string $value): string + { + if ($value === null || $value === '') { + return ''; + } + return '*********'; + } + /** * @return LanguageService */
typo3/sysext/backend/Resources/Public/JavaScript/FormEngineValidation.js+6 −1 modified@@ -245,7 +245,9 @@ define([ for (i = 0; i < evalList.length; i++) { formattedValue = FormEngineValidation.formatValue(evalList[i], formattedValue, config); } - + if ($mainField.prop('disabled') && $mainField.data('enableOnModification')) { + $mainField.prop('disabled', false); + } $mainField.val(newValue); // After updating the value of the main field, dispatch a "change" event to inform e.g. the "RequestUpdate" // component, which always listens to the main field instead of the "human readable field", about it. @@ -570,6 +572,9 @@ define([ modified = true; } if (modified) { + if ($field.prop('disabled') && $field.data('enableOnModification')) { + $field.prop('disabled', false); + } $field.val(newValue); } }
typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputSimpleCest.php+2 −1 modified@@ -141,7 +141,8 @@ protected function simpleInputFieldsDataProvider(): array 'inputValue' => 'Kasper', 'expectedValue' => '********', 'expectedInternalValue' => 'Kasper', - 'expectedValueAfterSave' => 'Kasper', + // even if `password_2` is not hashed, it never should expose the value + 'expectedValueAfterSave' => '*********', 'comment' => '', ], [
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-38r2-5695-334wghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-25118ghsaADVISORY
- github.com/TYPO3/typo3/commit/1186b2fec8a665a8f228ed66e6d60abf8407c17bghsaWEB
- github.com/TYPO3/typo3/commit/c7a135c25a14b852eebe4335f21ba3c606188f3aghsaWEB
- github.com/TYPO3/typo3/commit/cafc5af7fdce7734e6c8f9ecf2efd17b246fc049ghsaWEB
- github.com/TYPO3/typo3/security/advisories/GHSA-38r2-5695-334wghsax_refsource_CONFIRMWEB
- typo3.org/security/advisory/typo3-core-sa-2024-003ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.