Player: Improved XUID verification

we check if an existing player is online with a matching XUID first; if there isn't, we don't bother loading the playerdata, since that other player couldn't have joined unless they had a match or were allowed to bypass.
This commit is contained in:
Dylan K. Taylor 2021-03-07 19:53:19 +00:00
parent 3d90625020
commit ed84252942
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D

View File

@ -2073,32 +2073,40 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
* @return void * @return void
*/ */
protected function processLogin(){ protected function processLogin(){
$this->namedtag = $this->server->getOfflinePlayerData($this->username); $checkXUID = (bool) $this->server->getProperty("player.verify-xuid", true);
if((bool) $this->server->getProperty("player.verify-xuid", true)){ $kickForXUIDMismatch = function(string $xuid) use ($checkXUID) : bool{
$recordedXUID = $this->namedtag->getTag("LastKnownXUID"); if($checkXUID && $this->xuid !== $xuid){
if(!($recordedXUID instanceof StringTag)){
$this->server->getLogger()->debug("No previous XUID recorded for " . $this->getName() . ", no choice but to trust this player");
}elseif($this->xuid !== $recordedXUID->getValue()){
if($this->kick("XUID does not match (possible impersonation attempt)", false)){ if($this->kick("XUID does not match (possible impersonation attempt)", false)){
//TODO: Longer term, we should be identifying playerdata using something more reliable, like XUID or UUID. //TODO: Longer term, we should be identifying playerdata using something more reliable, like XUID or UUID.
//However, that would be a very disruptive change, so this will serve as a stopgap for now. //However, that would be a very disruptive change, so this will serve as a stopgap for now.
//Side note: this will also prevent offline players hijacking XBL playerdata on online servers, since their //Side note: this will also prevent offline players hijacking XBL playerdata on online servers, since their
//XUID will always be empty. //XUID will always be empty.
return; return true;
} }
$this->server->getLogger()->debug("XUID mismatch for " . $this->getName() . ", but plugin cancelled event allowing them to join anyway"); $this->server->getLogger()->debug("XUID mismatch for " . $this->getName() . ", but plugin cancelled event allowing them to join anyway");
}else{
$this->server->getLogger()->debug("XUID match for " . $this->getName());
} }
} return false;
};
foreach($this->server->getLoggedInPlayers() as $p){ foreach($this->server->getLoggedInPlayers() as $p){
if($p !== $this and ($p->iusername === $this->iusername or $this->getUniqueId()->equals($p->getUniqueId()))){ if($p !== $this and ($p->iusername === $this->iusername or $this->getUniqueId()->equals($p->getUniqueId()))){
if(!$p->kick("logged in from another location")){ if($kickForXUIDMismatch($p->getXuid())){
$this->close($this->getLeaveMessage(), "Logged in from another location");
return; return;
} }
if(!$p->kick("logged in from another location")){
$this->close($this->getLeaveMessage(), "Logged in from another location");
return;
}
}
}
$this->namedtag = $this->server->getOfflinePlayerData($this->username);
if($checkXUID){
$recordedXUID = $this->namedtag->getTag("LastKnownXUID");
if(!($recordedXUID instanceof StringTag)){
$this->server->getLogger()->debug("No previous XUID recorded for " . $this->getName() . ", no choice but to trust this player");
}elseif(!$kickForXUIDMismatch($recordedXUID->getValue())){
$this->server->getLogger()->debug("XUID match for " . $this->getName());
} }
} }