v13: 安全加固 - PHP禁止自验证密码 + Java本地验证记录持久化
安全修复(严重隐患): - PHP端: verifyWebPassword()函数体改为error(),禁止PHP直接验证密码 - Java端: pollWebLoginConfirmations()增加本地验证记录检查,PHP推送的登录确认必须有Java本地验证记录才允许自动登录 - Java端: 验证成功后写入login.db的web_verified_at字段(重启不丢失) - Java端: isWebLoginVerified()增加login.db回查(内存没有时从DB恢复) - Java端: clearWebLoginVerified()同时清除DB记录 - Java端: pollWebLoginConfirmations消费后立即清除验证记录(防重放)
This commit is contained in:
@@ -72,6 +72,9 @@ public class DatabaseManager {
|
||||
"TEXT DEFAULT ''");
|
||||
safeAdd(st, "temp_pw_expire",
|
||||
"INTEGER DEFAULT 0");
|
||||
// ★ Web密码验证时间戳(Java验证成功后写入,重启不丢失)
|
||||
safeAdd(st, "web_verified_at",
|
||||
"INTEGER DEFAULT 0");
|
||||
safeAdd(st, "temp_pw_used",
|
||||
"INTEGER DEFAULT 0");
|
||||
safeAdd(st, "ip_address",
|
||||
|
||||
@@ -2894,15 +2894,33 @@ public class WebManager {
|
||||
* Java验证成功后立即记录,玩家进游戏时直接检查
|
||||
*/
|
||||
public boolean isWebLoginVerified(String playerName) {
|
||||
// ★ 优先检查内存(Java刚验证的)
|
||||
Long verifiedTime = verifiedWebLogins.get(playerName);
|
||||
if (verifiedTime == null) return false;
|
||||
|
||||
// 检查是否过期(5分钟)
|
||||
if (System.currentTimeMillis() - verifiedTime > VERIFIED_LOGIN_EXPIRE_MS) {
|
||||
verifiedWebLogins.remove(playerName);
|
||||
return false;
|
||||
if (verifiedTime != null) {
|
||||
if (System.currentTimeMillis() - verifiedTime > VERIFIED_LOGIN_EXPIRE_MS) {
|
||||
verifiedWebLogins.remove(playerName);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
// ★ 内存没有 → 检查login.db(重启后恢复)
|
||||
try {
|
||||
Long dbTime = (Long) plugin.getDb().getField(playerName, "web_verified_at");
|
||||
if (dbTime != null && dbTime > 0) {
|
||||
if (System.currentTimeMillis() - dbTime > VERIFIED_LOGIN_EXPIRE_MS) {
|
||||
// 过期,清除DB记录
|
||||
plugin.getDb().setField(playerName, "web_verified_at", 0L);
|
||||
return false;
|
||||
}
|
||||
// ★ 发现DB记录 → 同步到内存
|
||||
verifiedWebLogins.put(playerName, dbTime);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// DB查询失败,安全降级为不自动登录
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2910,6 +2928,12 @@ public class WebManager {
|
||||
*/
|
||||
public void clearWebLoginVerified(String playerName) {
|
||||
verifiedWebLogins.remove(playerName);
|
||||
// ★ 同步清除login.db记录
|
||||
try {
|
||||
plugin.getDb().setField(playerName, "web_verified_at", 0L);
|
||||
} catch (Exception e) {
|
||||
// 静默
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3783,10 +3807,17 @@ public class WebManager {
|
||||
for (String playerName : players) {
|
||||
final String name = playerName;
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
// ★★★ 安全加固:PHP推送的登录确认,必须在Java本地有验证记录 ★★★
|
||||
// 防止PHP自验证假密码 → 写入web_login_confirmations → Java盲目自动登录
|
||||
if (!isWebLoginVerified(name)) {
|
||||
plugin.getLogger().warning("[Web登录轮询] ⚠️ 玩家 " + name + " 无Java本地验证记录,拒绝自动登录(安全拦截)");
|
||||
return;
|
||||
}
|
||||
plugin.getLogger().info("[Web登录轮询] 尝试处理玩家 " + name + " 的自动登录");
|
||||
boolean ok = plugin.handleWebLoginConfirmation(name);
|
||||
if (ok) {
|
||||
plugin.getLogger().info("[Web登录轮询] 玩家 " + name + " 通过WebToken验证,已自动登录");
|
||||
clearWebLoginVerified(name); // ★ 消费后立即清除
|
||||
plugin.getLogger().info("[Web登录轮询] 玩家 " + name + " 通过Java本地验证,已自动登录");
|
||||
} else {
|
||||
plugin.getLogger().info("[Web登录轮询] 玩家 " + name + " 未在线,跳过自动登录");
|
||||
}
|
||||
@@ -3880,7 +3911,13 @@ public class WebManager {
|
||||
// ★ 验证成功后,立即记录本地登录状态(不依赖PHP)
|
||||
if ("\"success\"".equals(result)) {
|
||||
verifiedWebLogins.put(fName, System.currentTimeMillis());
|
||||
plugin.getLogger().info("[Web密码验证] ★ 玩家 " + fName + " Web登录验证成功,已设置本地登录状态");
|
||||
// ★ 持久化到login.db(重启不丢失)
|
||||
try {
|
||||
plugin.getDb().setField(fName, "web_verified_at", System.currentTimeMillis());
|
||||
} catch (Exception dbEx) {
|
||||
plugin.getLogger().warning("[Web密码验证] 写入login.db失败: " + dbEx.getMessage());
|
||||
}
|
||||
plugin.getLogger().info("[Web密码验证] ★ 玩家 " + fName + " Web登录验证成功,已设置本地登录状态(内存+DB)");
|
||||
}
|
||||
|
||||
// 异步将结果写回PHP(供Web端查询)
|
||||
|
||||
Reference in New Issue
Block a user