GPF Docs

Guide to Security

Guide to Security

[FromClient]

By default, ServerObject Handlers will not receive messages from Clients (so they can’t be hijacked). To allow a Handler to receive messages from the Client apply the [FromClient] attribute to that handler. Sanitize all input from clients. If all messages from [FromClient] handlers are properly sanitized, you can trust any other messages from other SO's.

[Syncable]

By default, ServerObject state can not be accessed by a client; however, you can give clients access to state with the [Syncable] attribute.

The ServerObject below is an example of [Syncable] and provides a few examples of [FromClient]:

[DataStorePath("sync.coin_players")]
[Syncable]
[Register("coin_player")]
public class CoinPlayerSO : ServerObject
{
public const int FLIP_SECONDS = 1;
public override SOID ID { get; set; }
public enum CoinState
{
HEADS,
TAILS,
FLIPPING
}
public CoinState state;
public int currentStreak = 0;
public int longestStreak = 0;
public string username = "Anonymous";
public Tuple<string,int>[] leaderboard;
public class SetUsername : ServerObjectMessage
{
public string username;
}
[FromClient] // <- [FromClient] allows a user to send this type of message
void Handler(SetUsername message)
{
username = message.username;
Send(
Registry.GetId<CoinLeaderboardSO>("0"),
new CoinLeaderboardSO.SendScore {
score = longestStreak,
username = username }
);
}
public class Flip : ServerObjectMessage {}
[FromClient] // <- [FromClient] allows a user to send this type of message
void Handler(Flip message)
{
if (state != CoinState.FLIPPING)
{
state = CoinState.FLIPPING;
Random rand = new Random();
var isHeads = rand.Next(2) == 0;
Send(ID, new FlipResult { isHeads = isHeads }, FLIP_SECONDS);
}
}
public class GetTopScores : ServerObjectMessage { }
[FromClient] // <- [FromClient] allows a user to send this type of message
void Handler(GetTopScores message)
{
Send(
Registry.GetId<CoinLeaderboardSO>("0"),
new CoinLeaderboardSO.GetTopScores()
);
}
public class FlipResult : ServerObjectMessage
{
public bool isHeads;
}
// This handler does not have [FromClient] so only the server can send it
void Handler(FlipResult message)
{
if (message.isHeads)
{
currentStreak++;
state = CoinState.HEADS;
}
else
{
currentStreak = 0;
state = CoinState.TAILS;
}
if (currentStreak > longestStreak)
{
longestStreak = currentStreak;
Send(
Registry.GetId<CoinLeaderboardSO>("0"),
new CoinLeaderboardSO.SendScore {
score = longestStreak,
username = username }
);
}
}
// This handler does not have [FromClient] so only the server can send it
void Handler(CoinLeaderboardSO.TopScores message)
{
leaderboard = message.scores;
}
public override string ToString()
{
string s = $"CoinPlayer: {username} currentStreak: {currentStreak} currentStreak: {longestStreak}";
return s;
}
}

Encrypted IDs.

Using encrypted IDs, Client's are prevented from accessing an SO (either syncing to it or messaging it) unless the Client have a direct knowledge of that SO's ID. For example, if a client recieves an authenticated ID for it's PlayerSO, it can access that SO, but if it learns of a PlayerSO's ID from a LeaderboardSO or LobbySO, it will not be able to use that ID to access the playerSO.

OAuth

While EncryptedIDs provide an out of the box minimal layer of security, it is highly recommended that you leverage GPF's OAuth integration.

© 2023 Launch It Labs INC