Security
This topic covers multiple things; privacy, the totality of server control, validation design, how to handle malicious clients, malicious servers, anti-abuse for game clients, etc.
Client IP addresses and anonymity
In order to connect, your computer must access from its IP address to the server's IP address, so a client's public IP address is visible to the server as it would be to any server.
However, clients cannot see other clients' IP addresses as part of the protocol, unlike the Fusion's old Moo objects.
The only way to mask your public IP address is by using a Virtual Private Network (VPN) instead, making the Lacewing server see the IP of your VPN server instead.
Note that using a VPN will cause you to have a higher ping – as your messages must go to your VPN server, then be passed onto the Lacewing server; and likewise messages sent back must be passed from Lacewing server to your VPN server, then back to your computer.
If you are considering using a VPN to cheat on games, or perform illegal activity, be aware that a VPN can still be blocked by its VPN server IP; and in case of prosecution many VPNs are legally required to log your activity and true IP address. In addition, if your VPN's IP is blocked, anyone on that VPN will not be able to use the game server either, not just you.
Lacewing Blue will also send the Bluewing version and the platform you are on to a Blue server; for example, a client may send "Bluewing Windows b72". No other details are sent by Relay or Blue.
You can read more about it under Blue Server's Client > Get client implementation expression.
Design Fusion events with validation
Validate user input
You should validate user input, before you send it.
If the user is typing a number into an EditBox, don't call Val(Edittext$("EditBox")) on it, and immediately send it and be done. Val() will return 0 when the user put in blank, or if the user put in letters, which might not be what you want.
Comparing Str$(Val(text)) == text is a good start. But what about if the user puts in decimal places? What about negative numbers?
Or maybe you want to accept a blank EditBox, but use blank to indicate the default value 10 should be used.
Test for things you don't want, but try to find a good balance between over-validating it and not validating at all.
Over-validating means you waste effort on edge case scenarios where the user is intending to break the program, like putting in "--0STR1CH" as a number. Do you know what Val() will return for that?
Instead, look for scenarios that might happen in normal usage, and go a little further than that, to scenarios that are uncommon usage but might happen by a typo, like a copy-paste error or mishitting a key.
Giving the user helpful error information, e.g. "The number must be between 1 and 100", is far better than a generic error like "The number is invalid". If you let the user understand exactly what they did wrong, they won't be asking you what they did wrong; you'll save time for yourself.
You can also "enforce" validation by replacing the text of an EditBox with its last valid input whenever it's invalid, but this quickly becomes more trouble than it's worth, as you now not only need a validation when submitted, but before that; and saving input; and checking after copy-paste; and racing the user before they press to submit, etc.
Validate your message content
Remember, Lacewing does not know what your messages will do on the other side, what your Fusion events will do; it only transports the messages. Like a mailman who will deliver blackmail just as quickly as they would junk mail, or legitimate pen-pal letters; the mailman doesn't know what the contents are and they're not told to check.
The mailman (server) in this scenario does not speak any language, any further than being able to deliver to addresses (clients). Any "intelligence" like scanning for swearwords must be added in Fusion events.
So, if you choose to program admin commands, such as "a text message to server will make the server kick the client whose name matches the text", then you are responsible for making sure only admins can use those messages, by also adding permission checks in the Fusion events.
It is a toss-up how quickly a game will be exploited; sometimes even free, uncommon games are hacked quickly, and sometimes even the most vulnerable games are left unexploited for years.
Be cautious that your automatic bans for a user attempting to use something they don't have permission for, don't have the risk of locking out your own access.
It's good practice to put in allowlists based on IP, like making sure LAN IPs or localhost IP (127.0.0.1) never get an auto-ban no matter what they do, so if you're forgetful enough to put in your admin password wrong too many times, you don't get locked out and have to break into your own server and unban yourself.
Alternatively, you could use no password at all and give automatic admin permissions to anyone in LAN IP range/localhost, but first consider if others at your building could be using the apps.
Public servers do not have any anti-cheat checks for games, as they host all sorts of games, all using messages in their own unique ways. Adding restrictions for each game would drop performance like a rock, as well as requiring every developer who used the server to tell the server owner how to detect their game and what limitations to add, making it a horrendous mess.
How to design secure Lacewing apps
This is the following mental checklist to design secure apps:
- Assume every client, and every message sent by a client, is malicious, until the message has been checked in every way.
Assume it again for the next message; hacks can be activated at any time, and some hackers deliberately hide their tracks by not using hacks constantly. You can choose to alternate between checking and not checking, but try to make it randomly timed, rather than consistent, or the hackers will work it out by trial and error. - Let the server do the checking of clients and their messages if you can afford a custom server. If you cannot, then test it on the client.
- Check all message variables aren't too big, too long, too small, changing too much (anti-cheating), etc.
Check they aren't invalid, check names aren't entirely spaces, etc. - Check any user-inputted data before you send, and all parts, including non-user parts, when you receive.
- Servers can be trusted, more out of necessity than design, because honestly, you won't have a way to tell if they're lying to you…
- …unless you're exposing personal details or granting extra control over the client's computer such as screen viewing, file sharing, etc, in which case go back to caution again, and assume the server's messages are malicious until they pass all client-side checks. See filename checking below.
- If a server is malicious, disconnect and don't use it again until the original server owner has fixed it.
More on handling malicious servers below. - If a client is malicious, get them disconnected and IP banned by the server; if not possible, try to avoid them.
More on handling malicious clients below.
Other security topics such as anti-eavesdropping are discussed below.
Malicious Lacewing Relay servers
The server has the final say in whether any action is permitted or works as the client expects, which grants them a lot of control, but naturally no control outside the Relay protocol, or what the Fusion events are set up to respond to. So if your Fusion events don't have "delete a file if a Lacewing message is received", the server cannot make your client delete files.
Servers cannot force clients to connect to them, or force them to remain connected longer after the client disconnects. The client never tries to disconnect, it is guaranteed to succeed.
Non-standard Lacewing Relay servers can fake any client's messages. They can impersonate client-to-client/client-to-channel messages, although this is not usual and no public code has it implemented.
Servers can choose to ignore all messages being sent from client to client, dropping them instead.
The only counter-measure a client has against a malicious server is to not use the server and disconnect instead; since the server has control even over client-to-client message delivery, a client cannot be assured a message will travel to any other clients on that server, even if they encrypt it so the server cannot understand the content.
Malicious Lacewing Relay clients
Clients have no authority or effect on the server, other than what you code into the server with Fusion events.
The server has the final say in whether any action is permitted or works as the client expects. In practice, that means a malicious client will not gain anything from ignoring the server telling them what they can no longer do.
For example, if the server kicks a client from a channel, it will send them a Channel > On Leave Channel message.
If the client is modified to ignore Leave messages, and so acts like it's still on the channel, then the client still cannot send or receive channel/peer messages via that channel, as the server will be aware the client is not in the channel, and won't transfer any channel messages to or from them.
Anti-eavesdropping
This is only relevant if you are using servers where you expect not just your program to be using them, e.g. public servers.
If you wish to avoid the possibility of some stranger joining your channel and eavesdropping on a public server, consider simply not adding your channel to the public channel listing, and using a bot with its own list of channels.
If this isn't sufficient, you can encrypt your text messages using something like AESFusion object; encrypting will add some slowdown, but prevent them from being easily read by random Lacewing clients created from scratch.
This will prevent both other clients and the server listening in; because a malicious server can read the encrypted noise, and modify the encrypted messages, but they won't decrypt properly if modified.
However, if someone gets your original app and modifies it, then the encryption will have no effect, as your original app has the decryption key.
You could also use peer messages instead of channel messages, which will only require you to trust the server… but if you're trusting Darkwire by using Blue objects and following this help file written by a Darkwire employee, you might as well trust that they're not eavesdropping on your peer messages.
…if they wanted to, they wouldn't add the above part about how to avoid eavesdropping, ay? However, if you're concerned, the before-mentioned AESFusion trick will prevent the server being able to read the decrypted content, too.
Anti-abuse
Since Lacewing is used for all sorts of apps, your Fusion events must both handle the normal and anti-abuse scenarios.
Lacewing doesn't know which users can use an admin command, because it has no built-in admin commands, and no built-in permission system.
Lacewing doesn't know that in your particular game, players can only move a max of 50px per second, because it doesn't move the objects itself, it just passes the data on.
Since clients can be modified to disable any self-checks for anti-abuse, your most secure way to prevent cheats is to create a custom server with server-side abuse checks.
You could program clients to not just check themselves, but other clients too, and start ignoring other clients that fail their checks… but the "blocked" clients' messages still must be delivered (so there will be slowdown), and new clients that join the channel won't know the client is not trustworthy until they fail the checks on the new client's side too.
And if you set up a "this client is untrustworthy" warning message system, then a hacker can simply start sending the untrustworthy warning messages himself, saying unmodified clients are not trustworthy, and effectively making your game ignore everyone.
So, if you want to program anti-abuse, then your best bet is server-side checks – as client-side checks will help take some of the load of the server, but can be disabled by modifying the client, as you might expect.
Connection restrictions
Both Lacewing Blue and Relay will drop connections that are not Lacewing, quite rapidly. Neither will trigger On Connect without a Lacewing connect message being sent, or On Disconnect.
Lacewing Blue will notify the server via an error message including the IP address of the disconnected connection, when it identifies it as malicious. Any pending messages from that connection will be dropped, not even parsed by the Lacewing message reader.
Lacewing Blue Server is quite strict with its clients, and those that fail things innocently – e.g. using a blank name – will be denied gracefully. Those that are more malicious, e.g. sending malformed messages, or attempting to set name before their connection is approved, will be disconnected immediately.
Lacewing Blue also features some DDOS protection, preventing a connection flood from one IP, or connections not being fully connected. This is covered more under Additional features.
IP bans
There are no IP ban measures in Lacewing Blue presently.
Even when being kicked for malicious activity, the IP will not be blocked, in case the "malicious" activity was not so evil; for example, trying to open your Lacewing server on a web browser.
However, you can add IP blocking easily to your server:
Don't forget to save the banned IP list on end of application, and load the list back on start of application.
You should also make sure you will never accidentally ban your own IP address (which is covered above).
File path exploits
If you are writing to or reading from files using a filename passed from the client at any point, bear in mind these two path tricks:
Relative paths
Never use filenames only, a.k.a. relative paths, when taking filenames from received messages. Only use full paths (a.k.a. absolute paths); always put in the application's folder path. (or application's data folder path, on mobile)
It's not a bad idea to do this outside of your Lacewing code, as well, as it makes your code portable.
Relative paths are paths that only have the filename or a subfolder of the current directory.
For example, "filename.ini" is a relative path, saving to "(current directory)\filename.ini".
"betsy\filename.ini" uses a relative path, saving to "(current directory)\betsy\filename.ini".
When you let a client on the other end the full choice of what path they can send, well… what if instead of the expected "filename.ini", the client sends "C:\Windows\System32\…"? Your innocent level saving mechanic now lets them overwrite critical system files.
If your app doesn't secure the filenames, it will happily accept "/" or "\" in the path, so they basically have access to write onto the whole device's filesystems, as well as LAN shared folders.
(To some extent Windows checks for user-level apps overwriting system files, by requiring administrator permissions, but these checks only apply on Windows Vista+, with UAC enabled, with the system folders' permissions being their default, and with the user running the app not running it under admin privileges (which includes being launched by an admin-level program)…
…so consider these OS checks is a last resort protection. If the OS is the only thing protecting users from your code, you have written terrible code.
There is an extra caveat for would-be exploiters; if you're taking a filename of a file from local filesystem to send over Bluewing, the exploiter able to request any filename isn't that useful to them, because they won't know what the personal documents' filenames are.)
If you simply do a check for a ":" as the second character in the path, that won't help, as there are more filename tricks:
- "\Bob" gives them access to the root of the current directory's drive.
- "/Bob" does the same.
- "\\PC-NAME\SharedFolder" gives them access to the LAN PC "PC-NAME" shared folder.
- "file:///C:/" gives them access to root of the C:\ drive. Not many OS file access features allow this sort of URL, but it's a possibility.
- In Android/iOS, all drives are accessible from a subfolder of the root; for example, the /mnt/drive folder.
Starting a filepath with a "/", like "/Something", will give access to the root of the filesystem.
With security improvements in the mobile OS world, apps are prevented from accessing other apps' files, or the system's files, so it's unlikely an exploit could damage those… but they could overwrite files that are critical to your app.
It wouldn't take many remote overwrites of app files from a hacker before your app is flooded with negative reviews from legitimate users saying their game can't boot anymore. If the same player gets it happening to them twice, they'll drop your app.
It's worth noting the current directory is not the application's folder, as you might expect. It starts out as that, but can be changed by any part of the program; File object on Windows, for example, can change it directly by an action, resulting in all relative paths to be based on that new current directory.
It's called "current directory" for a reason, not "application directory".
To get the application directory:
- On Windows, use AppPath$. It ends with a backslash, so you just need AppPath$ + "filename.ini".
- On Android, use the Android object: DataStorageDirectory$("Android object") + "/filename.ini"
- On iOS, (Clickteam haven't provided a way)
The text ".." in the path
".." in a filename means "the parent folder".
This means if someone uses a filename with ".." in your app, it will allow them to read folders you don't expect, even going all the way to the root of the drive. This applies even when using AppPath$ or other absolute paths.
For example, if your app receives a text message to save a filename, like this:
"C:\Games\MyFusionGame\levels\" + Received$("Lacewing Blue Client")
If the client sends "..\Uhoh.lvl", they will be writing the file to
"C:\Games\MyFusionGame\levels\..\Uhoh.lvl"
which becomes:
"C:\Games\MyFusionGame\Uhoh.lvl"
The parent folder symbol ".." can be repeated together to get to the parent of the parent, meaning they can quite easily do:
"C:\Games\MyFusionGame\levels\..\..\..\Windows\System32\freecell.exe"
…which becomes:
"C:\Windows\System32\freecell.exe"
And now they're overwriting Freecell. They could just as easily overwrite your Windows profile or the Windows login program, if they know the path.
How to safely use filenames
To check the filenames, you can search in them when you receive them; use Compare Two General Values and the Find expression, both of which are under the System object.
Find(text to look in, text to look for, number of letters to skip in search)
Find() returns -1 if no matches are found, and the position in the text otherwise.
To avoid those two filename exploits above, your code may look like this:
This example safely checks a filename for Windows to make sure it doesn't leave the "levels" folder.
Checking for ".." in that one extra condition is fine if you're not letting the other side decide the full path… and you shouldn't, as it's a security flaw.
To make it work on Android and iOS, use a global string, save AppPath$ or the Android/iOS equivalents to it on start of the first frame in your application; and add a slash to the end if it's not there.
Then your code can simply do a save to GlobalStringAppPath + "levels/filename.ini", after a check for "..", of course.