[FEATURE] Защита от взлома лаунчера в LauncherRequest

This commit is contained in:
Gravit 2019-09-29 15:40:26 +07:00
parent a38853180b
commit efd58d66c7
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
9 changed files with 56 additions and 11 deletions

View file

@ -4,6 +4,7 @@
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
public class StdProtectHandler extends ProtectHandler { public class StdProtectHandler extends ProtectHandler {
public boolean checkSecure = true;
@Override @Override
public String generateSecureToken(AuthResponse.AuthContext context) { public String generateSecureToken(AuthResponse.AuthContext context) {
return SecurityHelper.randomStringToken(); return SecurityHelper.randomStringToken();
@ -21,7 +22,7 @@ public boolean verifyClientSecureToken(String token, String secureKey) {
@Override @Override
public boolean allowGetAccessToken(AuthResponse.AuthContext context) { public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return (context.authType == AuthResponse.ConnectTypes.CLIENT); return (context.authType == AuthResponse.ConnectTypes.CLIENT) && (!checkSecure || context.client.isSecure);
} }
@Override @Override

View file

@ -87,6 +87,10 @@ private void setStringField(String name, String value)
public void setGuardType(String key) { public void setGuardType(String key) {
setStringField("guardType", key); setStringField("guardType", key);
} }
public void setSecureCheck(String hash, String salt) {
setStringField("secureCheckHash", hash);
setStringField("secureCheckSalt", salt);
}
private void push(final int value) { private void push(final int value) {
if (value >= -1 && value <= 5) if (value >= -1 && value <= 5)

View file

@ -8,6 +8,8 @@
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@ -133,6 +135,11 @@ public Path process(Path inputJar) throws IOException {
launcherConfigurator.setGuardType(server.config.launcher.guardType); launcherConfigurator.setGuardType(server.config.launcher.guardType);
launcherConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava); launcherConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava);
launcherConfigurator.setEnv(server.config.env); launcherConfigurator.setEnv(server.config.env);
String launcherSalt = SecurityHelper.randomStringToken();
byte[] launcherSecureHash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
server.runtime.clientCheckSecret.concat(".").concat(launcherSalt));
launcherConfigurator.setSecureCheck(Base64.getEncoder().encodeToString(launcherSecureHash), launcherSalt);
//LogHelper.debug("[checkSecure] %s: %s", launcherSalt, Arrays.toString(launcherSecureHash));
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken(); if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
launcherConfigurator.setOemUnlockKey(server.runtime.oemUnlockKey); launcherConfigurator.setOemUnlockKey(server.runtime.oemUnlockKey);
server.buildHookManager.registerAllClientModuleClass(launcherConfigurator); server.buildHookManager.registerAllClientModuleClass(launcherConfigurator);

View file

@ -7,13 +7,16 @@ public class LaunchServerRuntimeConfig {
public String clientToken; public String clientToken;
public String oemUnlockKey; public String oemUnlockKey;
public String registerApiKey; public String registerApiKey;
public String clientCheckSecret;
public void verify() { public void verify() {
if (clientToken == null) LogHelper.error("[RuntimeConfig] clientToken must not be null"); if (clientToken == null) LogHelper.error("[RuntimeConfig] clientToken must not be null");
if (clientCheckSecret == null) { LogHelper.warning("[RuntimeConfig] clientCheckSecret must not be null"); clientCheckSecret = SecurityHelper.randomStringToken(); }
} }
public void reset() { public void reset() {
clientToken = SecurityHelper.randomStringToken(); clientToken = SecurityHelper.randomStringToken();
registerApiKey = SecurityHelper.randomStringToken(); registerApiKey = SecurityHelper.randomStringToken();
clientCheckSecret = SecurityHelper.randomStringToken();
} }
} }

View file

@ -11,7 +11,6 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.hwid.HWID; import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.hwid.OshiHWID;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword; import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
@ -72,7 +71,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
AuthProviderPair pair; AuthProviderPair pair;
if (auth_id.isEmpty()) pair = server.config.getAuthProviderPair(); if (auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
else pair = server.config.getAuthProviderPair(auth_id); else pair = server.config.getAuthProviderPair(auth_id);
AuthContext context = new AuthContext(0, login, customText, client, null, ip, authType); AuthContext context = new AuthContext(clientData, login, customText, client, hwid, ip, authType);
AuthProvider provider = pair.provider; AuthProvider provider = pair.provider;
server.authHookManager.preHook.hook(context, clientData); server.authHookManager.preHook.hook(context, clientData);
provider.preAuth(login, password, customText, ip); provider.preAuth(login, password, customText, ip);
@ -131,24 +130,23 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
} }
public static class AuthContext { public static class AuthContext {
public AuthContext(long session, String login, String customText, String client, String hwid, String ip, ConnectTypes authType) { public AuthContext(Client client, String login, String customText, String profileName, HWID hwid, String ip, ConnectTypes authType) {
this.session = session; this.client = client;
this.login = login; this.login = login;
this.customText = customText; this.customText = customText;
this.client = client; this.profileName = profileName;
this.hwid = hwid; this.hwid = hwid;
this.ip = ip; this.ip = ip;
this.authType = authType; this.authType = authType;
} }
public long session;
public String login; public String login;
@Deprecated @Deprecated
public int password_length; //Use AuthProvider for get password public int password_length; //Use AuthProvider for get password
public String client; public String profileName;
public String hwid; public HWID hwid;
public String customText; public String customText;
public String ip; public String ip;
public ConnectTypes authType; public ConnectTypes authType;
public Client client;
} }
} }

View file

@ -8,6 +8,8 @@
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.utils.Version; import pro.gravit.utils.Version;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
public class LauncherResponse extends SimpleResponse { public class LauncherResponse extends SimpleResponse {
public Version version; public Version version;
@ -15,6 +17,9 @@ public class LauncherResponse extends SimpleResponse {
public byte[] digest; public byte[] digest;
public int launcher_type; public int launcher_type;
public String secureHash;
public String secureSalt;
@Override @Override
public String getType() { public String getType() {
return "launcher"; return "launcher";
@ -33,6 +38,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
if (hash == null) service.sendObjectAndClose(ctx, new LauncherRequestEvent(true, server.config.netty.launcherURL)); if (hash == null) service.sendObjectAndClose(ctx, new LauncherRequestEvent(true, server.config.netty.launcherURL));
if (Arrays.equals(bytes, hash)) { if (Arrays.equals(bytes, hash)) {
client.checkSign = true; client.checkSign = true;
client.isSecure = checkSecure(secureHash, secureSalt);
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL)); sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL));
} else { } else {
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL)); sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL));
@ -43,12 +49,21 @@ public void execute(ChannelHandlerContext ctx, Client client) {
if (hash == null) sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL)); if (hash == null) sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL));
if (Arrays.equals(bytes, hash)) { if (Arrays.equals(bytes, hash)) {
client.checkSign = true; client.checkSign = true;
client.isSecure = checkSecure(secureHash, secureSalt);
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL)); sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL));
} else { } else {
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL)); sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL));
} }
} else sendError("Request launcher type error"); } else sendError("Request launcher type error");
}
private boolean checkSecure(String hash, String salt)
{
if(hash == null || salt == null) return false;
byte[] normal_hash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
server.runtime.clientCheckSecret.concat(".").concat(salt));
byte[] launcher_hash = Base64.getDecoder().decode(hash);
//LogHelper.debug("[checkSecure] %s vs %s", Arrays.toString(normal_hash), Arrays.toString(launcher_hash));
return Arrays.equals(normal_hash, launcher_hash);
} }
} }

View file

@ -10,6 +10,8 @@ public class AutogenConfig {
public String guardLicenseName; public String guardLicenseName;
public String guardLicenseKey; public String guardLicenseKey;
public String guardLicenseEncryptKey; public String guardLicenseEncryptKey;
public String secureCheckHash;
public String secureCheckSalt;
public int env; public int env;
public boolean isWarningMissArchJava; public boolean isWarningMissArchJava;
// 0 - Dev (дебаг включен по умолчанию, все сообщения) // 0 - Dev (дебаг включен по умолчанию, все сообщения)

View file

@ -43,10 +43,15 @@ public static AutogenConfig getAutogenConfig() {
public final String guardLicenseKey; public final String guardLicenseKey;
public final String guardLicenseEncryptKey; public final String guardLicenseEncryptKey;
public final String guardType; public final String guardType;
public final String secureCheckHash;
public final String secureCheckSalt;
@LauncherAPI @LauncherAPI
public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException { public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException {
publicKey = SecurityHelper.toPublicRSAKey(input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH)); publicKey = SecurityHelper.toPublicRSAKey(input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH));
secureCheckHash = config.secureCheckHash;
secureCheckSalt = config.secureCheckSalt;
projectname = config.projectname; projectname = config.projectname;
clientPort = config.clientPort; clientPort = config.clientPort;
secretKeyClient = config.secretKeyClient; secretKeyClient = config.secretKeyClient;
@ -92,6 +97,8 @@ public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]
isWarningMissArchJava = true; isWarningMissArchJava = true;
isNettyEnabled = false; isNettyEnabled = false;
environment = LauncherEnvironment.STD; environment = LauncherEnvironment.STD;
secureCheckSalt = null;
secureCheckHash = null;
} }
@LauncherAPI @LauncherAPI
@ -108,6 +115,8 @@ public LauncherConfig(String address, RSAPublicKey publicKey, Map<String, byte[]
isWarningMissArchJava = true; isWarningMissArchJava = true;
isNettyEnabled = false; isNettyEnabled = false;
environment = LauncherEnvironment.STD; environment = LauncherEnvironment.STD;
secureCheckSalt = null;
secureCheckHash = null;
} }
@Override @Override

View file

@ -24,6 +24,10 @@ public final class LauncherRequest extends Request<LauncherRequestEvent> impleme
@LauncherNetworkAPI @LauncherNetworkAPI
public byte[] digest; public byte[] digest;
@LauncherNetworkAPI @LauncherNetworkAPI
public String secureHash;
@LauncherNetworkAPI
public String secureSalt;
@LauncherNetworkAPI
public int launcher_type = EXE_BINARY ? 2 : 1; public int launcher_type = EXE_BINARY ? 2 : 1;
@LauncherAPI @LauncherAPI
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class); public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
@ -89,6 +93,8 @@ public LauncherRequest() {
} catch (IOException e) { } catch (IOException e) {
LogHelper.error(e); LogHelper.error(e);
} }
secureHash = Launcher.getConfig().secureCheckHash;
secureSalt = Launcher.getConfig().secureCheckSalt;
} }
@Override @Override