mirror of
https://github.com/etlegacy/etlegacy-lua-scripts.git
synced 2025-02-17 01:11:41 +00:00
lua: (xp save) prevent sql injections with modified cl_guid, refs #234
This commit is contained in:
parent
ea55f531d3
commit
de5685157b
1 changed files with 67 additions and 35 deletions
|
@ -4,26 +4,36 @@
|
|||
License: MIT
|
||||
Released on 17.11.2013
|
||||
Website: http://www.etlegacy.com
|
||||
Mod: intended for Legacy, but might also work in NoQuarter
|
||||
|
||||
Description: this script saves users' experience points into
|
||||
a database and thus preserves them between connections
|
||||
]]--
|
||||
|
||||
-- load sqlite driver (or mysql..)
|
||||
luasql = require "luasql.sqlite3"
|
||||
local luasql = require "luasql.sqlite3"
|
||||
|
||||
local env -- environment object
|
||||
local con -- database connection
|
||||
local cur -- cursor
|
||||
|
||||
-- skill identifiers
|
||||
local BATTLESENSE = 0
|
||||
local ENGINEERING = 1
|
||||
local MEDIC = 2
|
||||
local FIELDOPS = 3
|
||||
local LIGHTWEAPONS = 4
|
||||
local HEAVYWEAPONS = 5
|
||||
local COVERTOPS = 6
|
||||
|
||||
local skills = {}
|
||||
skills[0] = "Battlesense"
|
||||
skills[1] = "Engineering"
|
||||
skills[2] = "Medic"
|
||||
skills[3] = "Field ops"
|
||||
skills[4] = "Light weapons"
|
||||
skills[5] = "Heavy weapons"
|
||||
skills[6] = "Covert ops"
|
||||
skills[BATTLESENSE] = "Battlesense"
|
||||
skills[ENGINEERING] = "Engineering"
|
||||
skills[MEDIC] = "Medic"
|
||||
skills[FIELDOPS] = "Field ops"
|
||||
skills[LIGHTWEAPONS] = "Light weapons"
|
||||
skills[HEAVYWEAPONS] = "Heavy weapons"
|
||||
skills[COVERTOPS] = "Covert ops"
|
||||
|
||||
-- database helper function
|
||||
-- returns database rows matching sql_statement
|
||||
|
@ -34,6 +44,20 @@ function rows(connection, sql_statement)
|
|||
end
|
||||
end -- rows
|
||||
|
||||
-- con:prepare with bind_names should be used to prevent sql injections
|
||||
-- but it doesn't work on my version of luasql
|
||||
function validateGUID(cno, guid)
|
||||
-- allow only alphanumeric characters in guid
|
||||
if(string.match(guid, "%W")) then
|
||||
-- Invalid characters detected. We should probably drop this client
|
||||
et.G_Print("^3WARNING: (XP Save) user with id " .. cno .. " has an invalid GUID: " .. guid .. "\n")
|
||||
et.trap_SendServerCommand (cno, "cpm \"" .. "Your XP won't be saved because you have an invalid cl_guid.\n\"")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- init db on game start
|
||||
function et_InitGame(levelTime, randomSeed, restart)
|
||||
-- name of this module
|
||||
|
@ -63,6 +87,15 @@ function et_InitGame(levelTime, randomSeed, restart)
|
|||
UNIQUE (guid)
|
||||
)
|
||||
]])
|
||||
|
||||
cur = assert ( con:execute("SELECT COUNT(*) FROM users") )
|
||||
|
||||
et.G_Print("There are " .. tonumber(cur:fetch(row, 'a')) .. " users in the XP save database.\n")
|
||||
|
||||
--et.G_Print ("^4List of users in XP Save database:\n")
|
||||
--for guid, date in rows (con, "SELECT * FROM users") do
|
||||
-- et.G_Print (string.format ("\tGUID %s was last seen on %s\n", guid, date))
|
||||
--end
|
||||
end -- et_InitGame
|
||||
|
||||
function et_ShutdownGame(restart)
|
||||
|
@ -74,47 +107,46 @@ end -- et_ShutdownGame
|
|||
|
||||
-- called when a client enters the game world
|
||||
function et_ClientBegin(cno)
|
||||
name = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "name" )
|
||||
guid = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "cl_guid" )
|
||||
local name = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "name" )
|
||||
local guid = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "cl_guid" )
|
||||
|
||||
if not validateGUID(cno, guid) then return end
|
||||
|
||||
cur = assert (con:execute(string.format("SELECT * FROM users WHERE guid='%s'", guid)))
|
||||
player = cur:fetch({}, 'a')
|
||||
local player = cur:fetch({}, 'a')
|
||||
|
||||
if not player then
|
||||
-- First time we see this player
|
||||
et.trap_SendServerCommand (cno, "cpm \"" .. "Welcome, " .. name .. "!\n\"")
|
||||
et.trap_SendServerCommand (cno, "cpm \"" .. "Welcome, " .. name .. "! You are playing on an XP save server.\n\"")
|
||||
cur = assert (con:execute(string.format("INSERT INTO users VALUES ('%s', '%s', 0, 0, 0, 0, 0, 0, 0)", guid, os.date("%Y-%m-%d %H:%M:%S"))))
|
||||
else
|
||||
et.trap_SendServerCommand (cno, "cpm \"" .. "Welcome back, " .. name .. "! Your last connection was on " .. player.last_seen .. "\n\"") -- in db: player.name
|
||||
|
||||
--et.G_Print ("Loading XP from database: " .. player.xp_battlesense .. " | " .. player.xp_engineering .. " | " .. player.xp_medic .. " | " .. player.xp_fieldops .. " | " .. player.xp_lightweapons .. " | " .. player.xp_heavyweapons .. " | " .. player.xp_covertops .. "\n\n")
|
||||
|
||||
et.G_XP_Set (cno, player.xp_battlesense, 0, 0)
|
||||
et.G_XP_Set (cno, player.xp_engineering, 1, 0)
|
||||
et.G_XP_Set (cno, player.xp_medic, 2, 0)
|
||||
et.G_XP_Set (cno, player.xp_fieldops, 3, 0)
|
||||
et.G_XP_Set (cno, player.xp_lightweapons, 4, 0)
|
||||
et.G_XP_Set (cno, player.xp_heavyweapons, 5, 0)
|
||||
et.G_XP_Set (cno, player.xp_covertops, 6, 0)
|
||||
|
||||
et.G_XP_Set (cno, player.xp_battlesense, BATTLESENSE, 0)
|
||||
et.G_XP_Set (cno, player.xp_engineering, ENGINEERING, 0)
|
||||
et.G_XP_Set (cno, player.xp_medic, MEDIC, 0)
|
||||
et.G_XP_Set (cno, player.xp_fieldops, FIELDOPS, 0)
|
||||
et.G_XP_Set (cno, player.xp_lightweapons, LIGHTWEAPONS, 0)
|
||||
et.G_XP_Set (cno, player.xp_heavyweapons, HEAVYWEAPONS, 0)
|
||||
et.G_XP_Set (cno, player.xp_covertops, COVERTOPS, 0)
|
||||
|
||||
et.G_Print (name .. "'s current XP levels:\n")
|
||||
for id, skill in pairs(skills) do
|
||||
et.G_Print ("\t" .. skill .. ": " .. et.gentity_get (cno, "sess.skillpoints", id) .. " XP\n")
|
||||
end
|
||||
end
|
||||
|
||||
--et.G_Print ("^4List of users in database:\n")
|
||||
--for name, guid, date in rows (con, "SELECT * FROM users") do
|
||||
-- et.G_Print (string.format ("\t%s with guid %s last seen on %s\n", name, guid, date))
|
||||
--end
|
||||
end -- et_ClientBegin
|
||||
|
||||
function et_ClientDisconnect(cno)
|
||||
name = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "name" )
|
||||
guid = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "cl_guid" )
|
||||
local name = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "name" )
|
||||
local guid = et.Info_ValueForKey( et.trap_GetUserinfo( cno ), "cl_guid" )
|
||||
|
||||
if not validateGUID(cno, guid) then return end
|
||||
|
||||
cur = assert (con:execute(string.format("SELECT * FROM users WHERE guid='%s' LIMIT 1", guid)))
|
||||
player = cur:fetch({}, 'a')
|
||||
local player = cur:fetch({}, 'a')
|
||||
|
||||
if not player then
|
||||
-- This should not happen
|
||||
|
@ -135,13 +167,13 @@ function et_ClientDisconnect(cno)
|
|||
xp_covertops='%s'
|
||||
WHERE guid='%s']],
|
||||
os.date("%Y-%m-%d %H:%M:%S"),
|
||||
et.gentity_get (cno, "sess.skillpoints", 0),
|
||||
et.gentity_get (cno, "sess.skillpoints", 1),
|
||||
et.gentity_get (cno, "sess.skillpoints", 2),
|
||||
et.gentity_get (cno, "sess.skillpoints", 3),
|
||||
et.gentity_get (cno, "sess.skillpoints", 4),
|
||||
et.gentity_get (cno, "sess.skillpoints", 5),
|
||||
et.gentity_get (cno, "sess.skillpoints", 6),
|
||||
et.gentity_get (cno, "sess.skillpoints", BATTLESENSE),
|
||||
et.gentity_get (cno, "sess.skillpoints", ENGINEERING),
|
||||
et.gentity_get (cno, "sess.skillpoints", MEDIC),
|
||||
et.gentity_get (cno, "sess.skillpoints", FIELDOPS),
|
||||
et.gentity_get (cno, "sess.skillpoints", LIGHTWEAPONS),
|
||||
et.gentity_get (cno, "sess.skillpoints", HEAVYWEAPONS),
|
||||
et.gentity_get (cno, "sess.skillpoints", COVERTOPS),
|
||||
guid
|
||||
)))
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue