fix(profile): neutralize stored XSS in profile descriptions [#250] (#252)

This commit is contained in:
Ferywir
2026-06-22 15:31:20 +02:00
committed by GitHub
parent 4b208dbfd8
commit 9d2d7699a9
5 changed files with 36 additions and 7 deletions
+6 -1
View File
@@ -31,7 +31,12 @@ if(isset($id))
{
include_once("../GameEngine/Ranking.php");
$varmedal = $database->getProfileMedal($id);
$profiel="".$user['desc1']."".md5('skJkev3')."".$user['desc2']."";
// Issue #250: escape the user-controlled descriptions before they reach the
// raw nl2br() render in playerinfo.tpl, so a stored HTML/JS payload (e.g.
// <details open ontoggle="eval(...)">) is shown as inert text. BBCode is
// intentionally NOT expanded here: the admin panel has no $generator (the
// [coor] tag would fatal) and seeing the raw markup helps moderation.
$profiel=htmlspecialchars($user['desc1'] ?? '', ENT_QUOTES, 'UTF-8').md5('skJkev3').htmlspecialchars($user['desc2'] ?? '', ENT_QUOTES, 'UTF-8');
$separator="../";
require("../Templates/Profile/medal.php");
$profiel=explode("".md5('skJkev3')."", $profiel);
+4 -2
View File
@@ -1309,8 +1309,10 @@ class MYSQLi_DB implements IDbConnection {
$gender = (int)$gender;
$location = mb_substr(trim($location), 0, 30, 'UTF-8');
$birthday = trim($birthday);
$desc1 = trim($desc1);
$desc2 = trim($desc2);
// Issue #250: cap profile descriptions (BBCode is rendered as HTML on
// display) so a single field cannot store an oversized payload.
$desc1 = mb_substr(trim($desc1), 0, 3000, 'UTF-8');
$desc2 = mb_substr(trim($desc2), 0, 3000, 'UTF-8');
$stmt = $this->dblink->prepare(
"UPDATE `".TB_PREFIX."users`
+5
View File
@@ -239,6 +239,11 @@ class Profile {
$database->setVillageName($varray[$i]['wref'], $newName);
}
// Invalidate the 30s session user-cache (see Session::PopulateVar) so the
// saved description/birthday/etc. show up immediately on the edit form and
// header, without waiting for the cache to expire (issue #250).
unset($_SESSION['cache_user_' . ($_SESSION['username'] ?? '')]);
header("Location: spieler.php?uid=" . $session->uid);
exit;
}
+19 -2
View File
@@ -40,8 +40,17 @@ $varmedal = $database->getProfileMedal($uid);
// marker legacy (păstrat pentru compatibilitate DB)
$profileSeparator = md5('skJkev3');
// păstrăm exact formatul original (IMPORTANT pentru medal.php)
$profiel = $displayarray['desc1'] . $profileSeparator . $displayarray['desc2'];
// Issue #250: profile descriptions are user-controlled free text. Escape them
// BEFORE any markup expansion so embedded HTML/JS (e.g. a stored
// <details open ontoggle="eval(...)"> payload) is rendered inert, then expand
// the BBCode the form advertises ("Suport BBCode"). The md5 separator and the
// [#..] medal markers carry no HTML/BBCode-special characters, so they survive
// htmlspecialchars()/BBCode.php untouched and are still handled by medal.php.
$input = htmlspecialchars($displayarray['desc1'] ?? '', ENT_QUOTES, 'UTF-8')
. $profileSeparator
. htmlspecialchars($displayarray['desc2'] ?? '', ENT_QUOTES, 'UTF-8');
include("GameEngine/BBCode.php");
$profiel = $bbcoded;
// medal.php se ocupă de procesare (NU îi strica inputul)
require("medal.php");
@@ -253,7 +262,12 @@ if ($uid == $session->uid) {
<tr>
<td colspan="2" class="desc2">
<div class="desc2div">
<!-- Issue #250: the "messages" wrapper re-uses the existing smiley sprite
CSS (scoped to div.messages/div.forum) so BBCode smileys render here too;
its only layout rule (padding/width) is overridden inline. -->
<div class="messages" style="padding:0;width:auto">
<?php echo nl2br($profiel[0]); ?>
</div>
</div>
</td>
</tr>
@@ -264,7 +278,10 @@ if ($uid == $session->uid) {
<td class="desc1">
<div class="desc1div">
<!-- Issue #250: see the matching wrapper above (enables BBCode smileys). -->
<div class="messages" style="padding:0;width:auto">
<?php echo nl2br($profiel[1]); ?>
</div>
</div>
</td>
+2 -2
View File
@@ -95,7 +95,7 @@ maxlength="4" class="text year">
<!-- DESCRIPTION RIGHT -->
<td rowspan="<?php echo 7 + count($database->getProfileVillages($session->uid)); ?>" class="desc1">
<textarea tabindex="7" name="be1"><?= htmlspecialchars($session->userinfo['desc2'] ?? '', ENT_QUOTES, 'UTF-8') ?></textarea>
<textarea tabindex="7" name="be1" maxlength="3000"><?= htmlspecialchars($session->userinfo['desc2'] ?? '', ENT_QUOTES, 'UTF-8') ?></textarea>
</td>
</tr>
@@ -152,7 +152,7 @@ maxlength="30" class="text">
<!-- DESCRIPTION LEFT -->
<tr>
<td colspan="2" class="desc2">
<textarea tabindex="8" name="be2"><?= htmlspecialchars($session->userinfo['desc1'] ?? '', ENT_QUOTES, 'UTF-8') ?></textarea>
<textarea tabindex="8" name="be2" maxlength="3000"><?= htmlspecialchars($session->userinfo['desc1'] ?? '', ENT_QUOTES, 'UTF-8') ?></textarea>
</td>
</tr>