mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-07-30 10:47:33 +02:00
Fixed NPE on invalid sessions + Improved security for premium logins
This commit is contained in:
@ -1,3 +1,9 @@
|
|||||||
|
######0.2.4
|
||||||
|
|
||||||
|
* Fixed NPE on invalid sessions
|
||||||
|
* Improved security by generating a randomized serverId
|
||||||
|
* Removed /premium [player] because it's safer for premium players who join without registration
|
||||||
|
|
||||||
######0.2.3
|
######0.2.3
|
||||||
|
|
||||||
* Remove useless AuthMe forcelogin code
|
* Remove useless AuthMe forcelogin code
|
||||||
|
12
README.md
12
README.md
@ -1,5 +1,7 @@
|
|||||||
# FastLogin
|
# FastLogin
|
||||||
|
|
||||||
|
[](https://travis-ci.org/games647/FastLogin)
|
||||||
|
|
||||||
Checks if a minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
|
Checks if a minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
|
||||||
So they don't need to enter passwords. This is also called auto login.
|
So they don't need to enter passwords. This is also called auto login.
|
||||||
|
|
||||||
@ -8,16 +10,15 @@ So they don't need to enter passwords. This is also called auto login.
|
|||||||
* Automatically login paid accounts (premium)
|
* Automatically login paid accounts (premium)
|
||||||
* Support various of auth plugins
|
* Support various of auth plugins
|
||||||
* Experimental Cauldron support
|
* Experimental Cauldron support
|
||||||
|
* No client modifications needed
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
###Commands:
|
###Commands:
|
||||||
* /premium Label the invoker as paid account
|
* /premium Label the invoker as paid account
|
||||||
* /premium [playername] Label specified player as a paid account
|
|
||||||
|
|
||||||
###Permissions:
|
###Permissions:
|
||||||
* fastlogin.command.premium
|
* fastlogin.command.premium
|
||||||
* fastlogin.command.premium.others
|
|
||||||
|
|
||||||
###Requirements:
|
###Requirements:
|
||||||
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/)
|
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/)
|
||||||
@ -32,7 +33,7 @@ So they don't need to enter passwords. This is also called auto login.
|
|||||||
|
|
||||||
###Downloads
|
###Downloads
|
||||||
|
|
||||||
https://github.com/games647/FastLogin/releases
|
https://www.spigotmc.org/resources/fastlogin.14153/history
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
@ -97,11 +98,14 @@ Yes, indeed. Therefore the command for toggling the fast login method exists.
|
|||||||
|
|
||||||
####Why do players have to invoke a command?
|
####Why do players have to invoke a command?
|
||||||
1. It's a secure way to make sure a person with a paid account cannot steal the account
|
1. It's a secure way to make sure a person with a paid account cannot steal the account
|
||||||
of a cracked player that has the same username.
|
of a cracked player that has the same username. The player have to proof first that it's his own account.
|
||||||
2. We only receive the username from the player on login. We could check if that username is associated
|
2. We only receive the username from the player on login. We could check if that username is associated
|
||||||
to a paid account but if we request a online mode login from a cracked player (who uses a username from
|
to a paid account but if we request a online mode login from a cracked player (who uses a username from
|
||||||
a paid account), the player will disconnect with the reason bad login. There is no way to change that message
|
a paid account), the player will disconnect with the reason bad login. There is no way to change that message
|
||||||
on the server side (without client modifications), because it's a connection between the Client and the Sessionserver.
|
on the server side (without client modifications), because it's a connection between the Client and the Sessionserver.
|
||||||
|
3. If a premium player would skip registration too, a player of a cracked account could later still register the
|
||||||
|
account and would claim and steal the account from the premium player. Because commands cannot be invoked unless the
|
||||||
|
player has a account or is logged in, protects this method also premium players
|
||||||
|
|
||||||
###What happens if a paid account joins with a used username?
|
###What happens if a paid account joins with a used username?
|
||||||
The player on the server have to activate the feature of this plugin by command. If a person buys the username
|
The player on the server have to activate the feature of this plugin by command. If a person buys the username
|
||||||
|
2
pom.xml
2
pom.xml
@ -8,7 +8,7 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>FastLogin</name>
|
<name>FastLogin</name>
|
||||||
<version>0.2.3</version>
|
<version>0.2.4</version>
|
||||||
<inceptionYear>2015</inceptionYear>
|
<inceptionYear>2015</inceptionYear>
|
||||||
<url>https://github.com/games647/FastLogin</url>
|
<url>https://github.com/games647/FastLogin</url>
|
||||||
<description>
|
<description>
|
||||||
|
@ -7,15 +7,30 @@ package com.github.games647.fastlogin;
|
|||||||
*/
|
*/
|
||||||
public class PlayerSession {
|
public class PlayerSession {
|
||||||
|
|
||||||
private final byte[] verifyToken;
|
|
||||||
private final String username;
|
private final String username;
|
||||||
|
private final String serverId;
|
||||||
|
private final byte[] verifyToken;
|
||||||
private boolean verified;
|
private boolean verified;
|
||||||
|
|
||||||
public PlayerSession(byte[] verifyToken, String username) {
|
public PlayerSession(String username, String serverId, byte[] verifyToken) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
|
this.serverId = serverId;
|
||||||
this.verifyToken = verifyToken;
|
this.verifyToken = verifyToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the random generated server id. This makes sure the request
|
||||||
|
* sent from the client is just for this server.
|
||||||
|
*
|
||||||
|
* See this for details
|
||||||
|
* http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
|
||||||
|
*
|
||||||
|
* @return random generated server id
|
||||||
|
*/
|
||||||
|
public String getServerId() {
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the verify token the server sent to the client.
|
* Gets the verify token the server sent to the client.
|
||||||
*
|
*
|
||||||
|
@ -34,17 +34,6 @@ public class PremiumCommand implements CommandExecutor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sender.hasPermission(plugin.getName() + ".command." + command.getName() + ".others")) {
|
|
||||||
String playerName = args[0];
|
|
||||||
//todo check if valid username
|
|
||||||
plugin.getEnabledPremium().add(playerName);
|
|
||||||
sender.sendMessage(ChatColor.DARK_GREEN + "Added "
|
|
||||||
+ ChatColor.DARK_BLUE + ChatColor.BOLD + playerName
|
|
||||||
+ ChatColor.RESET + ChatColor.DARK_GREEN + " to the list of premium players");
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Not enough permissions");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,9 +100,13 @@ public class EncryptionPacketListener extends PacketAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//this makes sure the request from the client is for us
|
||||||
|
//this might be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
|
||||||
|
String generatedId = session.getServerId();
|
||||||
|
|
||||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L193
|
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L193
|
||||||
//generate the server id based on client and server data
|
//generate the server id based on client and server data
|
||||||
byte[] serverIdHash = EncryptionUtil.getServerIdHash("", plugin.getServerKey().getPublic(), loginKey);
|
byte[] serverIdHash = EncryptionUtil.getServerIdHash(generatedId, plugin.getServerKey().getPublic(), loginKey);
|
||||||
String serverId = (new BigInteger(serverIdHash)).toString(16);
|
String serverId = (new BigInteger(serverIdHash)).toString(16);
|
||||||
|
|
||||||
String username = session.getUsername();
|
String username = session.getUsername();
|
||||||
@ -206,7 +210,7 @@ public class EncryptionPacketListener extends PacketAdapter {
|
|||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
if (!"null".equals(line)) {
|
if (line != null && !line.equals("null")) {
|
||||||
//validate parsing
|
//validate parsing
|
||||||
//http://wiki.vg/Protocol_Encryption#Server
|
//http://wiki.vg/Protocol_Encryption#Server
|
||||||
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
|
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
|
||||||
@ -222,7 +226,7 @@ public class EncryptionPacketListener extends PacketAdapter {
|
|||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
//catch not only ioexceptions also parse and NPE on unexpected json format
|
//catch not only ioexceptions also parse and NPE on unexpected json format
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to verify if session is valid", ex);
|
plugin.getLogger().log(Level.WARNING, "Failed to verify session", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this connection doesn't need to be closed. So can make use of keep alive in java
|
//this connection doesn't need to be closed. So can make use of keep alive in java
|
||||||
|
@ -119,6 +119,10 @@ public class StartPacketListener extends PacketAdapter {
|
|||||||
*/
|
*/
|
||||||
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN, true);
|
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN, true);
|
||||||
|
|
||||||
|
//randomized server id to make sure the request is for our server
|
||||||
|
//this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
|
||||||
|
String serverId = Long.toString(random.nextLong(), 16);
|
||||||
|
newPacket.getStrings().write(0, serverId);
|
||||||
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
|
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
|
||||||
//generate a random token which should be the same when we receive it from the client
|
//generate a random token which should be the same when we receive it from the client
|
||||||
byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH];
|
byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH];
|
||||||
@ -129,7 +133,7 @@ public class StartPacketListener extends PacketAdapter {
|
|||||||
protocolManager.sendServerPacket(player, newPacket);
|
protocolManager.sendServerPacket(player, newPacket);
|
||||||
|
|
||||||
//cancel only if the player has a paid account otherwise login as normal offline player
|
//cancel only if the player has a paid account otherwise login as normal offline player
|
||||||
plugin.getSessions().put(sessionKey, new PlayerSession(verifyToken, username));
|
plugin.getSessions().put(sessionKey, new PlayerSession(username, serverId, verifyToken));
|
||||||
packetEvent.setCancelled(true);
|
packetEvent.setCancelled(true);
|
||||||
} catch (InvocationTargetException ex) {
|
} catch (InvocationTargetException ex) {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex);
|
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex);
|
||||||
|
@ -30,6 +30,4 @@ commands:
|
|||||||
permissions:
|
permissions:
|
||||||
${project.artifactId}.command.premium:
|
${project.artifactId}.command.premium:
|
||||||
description: 'Label themselves as premium using a command'
|
description: 'Label themselves as premium using a command'
|
||||||
default: true
|
default: true
|
||||||
${project.artifactId}.command.premium.others:
|
|
||||||
description: 'Label other people as premium'
|
|
Reference in New Issue
Block a user