Skip to content

Commit

Permalink
[SECURITY] Avoid showing password hashes in backend edit forms
Browse files Browse the repository at this point in the history
Backend form fields of TCA `type=password` should never expose
the persisted value - especially, in case the value is explicitly
configured not to be hashed (having TCA `hashed=false`).

Resolves: #101965
Releases: main, 13.0, 12.4, 11.5
Change-Id: Ie05a708185c621b8a2120ad7851ac4caf180893f
Security-Bulletin: TYPO3-CORE-SA-2024-003
Security-References: CVE-2024-25118
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/82953
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
  • Loading branch information
ohader committed Feb 13, 2024
1 parent 47e897f commit 1186b2f
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 4 deletions.
6 changes: 6 additions & 0 deletions Build/Sources/TypeScript/backend/form-engine-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -549,6 +552,9 @@ export default (function() {
modified = true;
}
if (modified) {
if (field.disabled && field.dataset.enableOnModification) {
field.disabled = false;
}
field.value = newValue;
}
}
Expand Down
19 changes: 17 additions & 2 deletions typo3/sysext/backend/Classes/Form/Element/PasswordElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -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>';
Expand Down Expand Up @@ -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">';
Expand Down Expand Up @@ -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 '*********';
}
}
Loading

0 comments on commit 1186b2f

Please sign in to comment.