Disrupting the Codenames Game: A Developer's Tale

Dev by Day, Mythology Explorer by Night 🌌 | 📚 Greek & Stoic Philosophy Buff | 🏺 Greek & Indian Mythology Fanatic | 🍳 Chef in the Coding Kitchen | Turning Bytes into Bites! 🔍✨ #CodeAndCookery #PhilosophyGeek #MythologyExplorer
Medium | Github | Twitter | Linkedin | Facebook
During one of our team’s enjoyable Friday meetings while we were working remotely, we decided to engage in a game of Codenames. The game uses a 5x5 grid with 8 blue, 8 red, 8 beige, and 1 black word. Blue and red are team colors, beige is neutral, and black is the game-ending color, but only Spymasters know this. Spymasters give single-word clues to guide their team to their color words. The goal is for a team to guess all their words except black, which causes instant loss.
Now normally the game depends on Spymasters’ ability, but not if you’re a Developer. So here’s how I spoofed the network calls to break the game.
During one of their network calls, likely before establishing a WebSocket connection, they transmit comprehensive game information to each player. This data encompasses player details, game state information, and more. However, a potential issue arises in their game information sharing, as they not only provide words for all the grid spaces but also reveal color information for each grid.
Below are the images of boards that were visible to operatives and spymaster.

Board visible to operatives

Board visible to spymaster
Link to the reponse from codenames server, if someone is interested.
Now, let’s dive into unraveling the JSON data. I began by searching for the first word on the board TRIANGLE, and discovered a JSON object of type wordCount that holds grid data for that word, including its x and y positions on the grid. Upon closer inspection of the id key, I noticed a structured format with an integer value following wordCount/. Interestingly, a similar structure can be observed for the coverCard type. What’s intriguing is that they both represent the same grid on the board. This revelation allowed me to exploit the game, as someone seemingly thought it would be convenient to share the entire game information across the gaming room.
{
"id":"wordCard/0",
"type":"wordCard",
"data":{
"word":"TRIANGLE",
"revealed":false,
"selected":false,
"tips":[]
},
"location":{
"name":"board",
"x":0,
"y":0
}
}
{
"id":"coverCard/blue/0",
"type":"coverCard",
"data":{
"color":"blue",
"img":9
},
"location":{
"name":"blueDeck"
}
}
Alright, let’s talk security, my fellow coders. Here’s how we could’ve locked this down, as per my knowledge:
Listen up, folks! It’s time to level up your servers. You absolutely should NOT be dishing out the same data to everyone. Let’s get smart and segregate that data based on user type. Operatives? They couldn’t care less about that
coverCardstuff, but Spymasters? They’re all about it. Sure, this might mean a little extra work for your server, but here’s the hack: Cache that user info, my friends. Not only will it give your server a breather, but it’ll also save you some sweet data transfer bandwidth. Why? Because guess what? Operatives are like 80% of your traffic, and sending less data to them essentially decreases your data bandwidth and also your server expenses hopefully. Cause, When it comes to computer servers, shelling out for data transfer usually outpaces processing expenses, fam. 🚀💻On the flip side, think about this: why not encrypt that raw JSON data for
coverCardtypes and only give the decryption key to our trusty Spymasters? I mean, it sounds pretty cool, right? But here’s the kicker: you’d end up encrypting and decrypting stuff, which adds complexity. Plus, you’d still be sending that encrypted data to, like, 80% of our clients who don’t need it. Oh, and don’t forget, you’d have to do extra processing to figure out who’s who before encrypting. Honestly, not the most server-friendly move, if you ask me.
