MCCodes - Chapter 9: Security Functions
Welcome to Chapter 9! In Chapter 8: Staff Panel & Permissions, we explored how game administrators manage the game using special tools and how access is controlled. Running a game isn't just about managing content and users; it's also crucially about keeping things secure. How do we protect user accounts from being misused? How do we handle sensitive information like passwords? How do we stop automated bots from abusing the system?
This chapter covers the essential Security Functions in MCCodes, focusing on protecting forms, handling passwords safely, and validating user input. Think of these functions as the game's security guards, ID checkers, and form inspectors.
The Problem: Keeping Things Safe and Sound
Imagine you're logged into your bank website in one browser tab. Then, you visit a malicious website in another tab. Could that malicious site somehow trick your browser into sending a request to your bank, like "transfer $1000 to BadGuy", without you realizing it? This is a real type of attack called Cross-Site Request Forgery (CSRF).
Also, how should the game store your password? If it's stored as plain text, anyone who gets access to the database can see it! Even if it's scrambled slightly, hackers have ways to crack common passwords.
And what about automated bots? If they can create thousands of accounts automatically or submit forms repeatedly, they can ruin the game for everyone.
We need mechanisms to:
- Prevent CSRF: Make sure form submissions are intentional and initiated by the user on the game's site.
- Handle Passwords Securely: Store passwords in a way that even if the database is compromised, the actual passwords are very hard to recover.
- Validate Input & Stop Bots: Ensure data entered by users is valid (like email addresses) and block automated scripts from interacting with the game.
The Solution: A Toolkit of Security Guards
MCCodes provides several functions and scripts, mostly found in global_func.php
and related files, to address these security concerns:
- CSRF Protection: Uses special hidden codes (tokens) in forms.
- Password Handling: Uses "salting" and "hashing" to securely store passwords.
- Validation & Anti-Bot: Uses server-side checks (often via AJAX for instant feedback) and CAPTCHA challenges.
Let's look at each area.
1. CSRF Protection: Foiling the Tricksters
The Problem: How to stop malicious websites from forcing your browser to submit forms on MCCodes without your consent?
The Analogy: Imagine every time you want to perform an important action (like changing your password or donating to a gang), the game gives you a unique, secret handshake code valid only for that specific request. An external attacker won't know the secret handshake for your current session.
The Solution: MCCodes uses CSRF tokens.
Generating a Token (
request_csrf_code
): When a sensitive form is displayed (like the password change form), the game generates a random, unpredictable string (the token) and stores it in your server-side session ($_SESSION
). It also puts this token into a hidden field within the HTML form. The functionrequest_csrf_code('form_identifier')
generates this token, andrequest_csrf_html('form_identifier')
generates the full hidden input HTML tag.// Inside preferences.php (Password Change Form) $code = request_csrf_code('prefs_passchange'); // Generate token & store in session echo " <form action='preferences.php?action=passchange2' method='post'> Current Password: <input type='password' name='oldpw' /><br /> New Password: <input type='password' name='newpw' /><br /> Confirm: <input type='password' name='newpw2' /><br /> <input type='hidden' name='verf' value='{$code}' /> <!-- Put token in form --> <input type='submit' value='Change PW' /> </form> ";
Verifying the Token (
verify_csrf_code
): When the form is submitted, the script that processes it (e.g.,preferences.php
withaction=passchange2
, orauthenticate.php
for login) takes the token submitted in the hidden field ($_POST['verf']
). It then callsverify_csrf_code('form_identifier', submitted_token)
. This function compares the submitted token with the one stored in the session for that form identifier. It also checks if the token has expired (usually after 15 minutes).// Inside preferences.php (Processing Password Change) function do_pass_change(): void { global $db, $ir; // *** CSRF Check *** if (!isset($_POST['verf']) || !verify_csrf_code('prefs_passchange', // Same identifier as form stripslashes($_POST['verf']))) { csrf_error('passchange'); // Function defined in preferences.php to show error } // --- If CSRF check passes, continue processing --- $oldpw = stripslashes($_POST['oldpw']); // ... rest of password change logic ... }
How it Helps: An external attacker trying to force your browser to submit the form won't know the secret token stored in your session. When the forged request arrives without the correct token (or with an expired/invalid one), verify_csrf_code
returns false
, and the script stops processing, preventing the unwanted action. You'll see this check at the beginning of many form processing scripts like authenticate.php
, register.php
(after username check), preferences.php
actions, macro2.php
, yourgang.php
actions, etc.
2. Password Security: Salting and Hashing
The Problem: How to store passwords so they are secure even if the database is exposed?
The Analogy: Instead of writing your password "secret123" on a note, you: 1. Add a unique random string (your "salt", like "aB3d") to it: "aB3dsecret123". 2. Put the combined string through a one-way scrambler (a "hash function", like MD5 in this case) that turns it into gibberish (e.g., "f4a8..."). Storing the salt ("aB3d") and the gibberish ("f4a8...") is much safer. Even if someone steals the note, they can't easily figure out the original password. They'd have to try scrambling every possible password with your specific salt to find a match.
The Solution: MCCodes uses salting and hashing via functions in global_func.php
.
-
generate_pass_salt()
: Creates a unique, random 8-character string for each user when they register. This is stored in thepass_salt
column in theusers
table. -
encode_password($password, $salt)
: Takes the user's plain-text password and their unique salt. It scrambles them together using the MD5 hashing algorithm. (Specifically, it calculatesmd5(salt + md5(password))
). The resulting hash is stored in theuserpass
column in theusers
table. -
verify_user_password($input_password, $stored_salt, $stored_hash)
: When a user tries to log in or change their password, this function takes their input password, retrieves their stored salt and stored hash from the database. It then runs the input password through the sameencode_password
process using the stored salt. If the resulting hash matches the stored hash, the password is correct.
Usage Examples:
Registration (
register.php
):php // Inside register.php (after password validation) $salt = generate_pass_salt(); // Create a unique salt $e_salt = $db->escape($salt); $encpsw = encode_password($base_pw, $salt); // Hash password with salt $e_encpsw = $db->escape($encpsw); $db->query( "INSERT INTO `users` (... `userpass`, `pass_salt` ...) VALUES(... '{$e_encpsw}', '{$e_salt}' ...)"); // Store hash and salt
Login (
authenticate.php
):php // Inside authenticate.php $uq = $db->query("SELECT `userid`, `userpass`, `pass_salt` FROM `users` WHERE `login_name` = ?"); // ... fetch user data into $mem ... $login_failed = !(verify_user_password($raw_password, $mem['pass_salt'], $mem['userpass'])); // Check input against stored hash/salt if ($login_failed) { die("Invalid username or password!"); } // ... login successful ...
Important Note: While salting and hashing significantly improve security over plain text or simple hashing, the MD5 algorithm used in MCCodes is considered outdated and insecure by modern standards. Modern systems use much stronger hashing algorithms like bcrypt or Argon2. If you were building a new system, you would not use MD5 for passwords. However, this is how MCCodes implements it.
3. Input Validation & Anti-Bot Measures
The Problem: How to ensure user input is reasonable (e.g., a valid email format, username not taken) and how to stop automated bots from registering or performing actions?
The Analogy: * Validation: Like a form inspector checking if you filled out all required fields correctly and if your chosen username isn't already painted on someone else's mailbox. * Anti-Bot: Like a doorman asking you to read a wavy line of text that only humans can easily decipher, to prove you're not a robot.
The Solution: MCCodes uses AJAX checks for instant feedback and CAPTCHA challenges.
AJAX Validation Scripts (
checkun.php
,checkem.php
,check.php
):- During registration, as you type your desired username or email, JavaScript code (
js/register.js
) sends your input silently (using AJAX) to these PHP scripts. -
checkun.php
: Checks if the username meets length/character rules and if it already exists in theusers
table. -
checkem.php
: Checks if the email has a valid format and if it's already in use in theusers
table. -
check.php
: Checks the strength of the entered password based on length and character types (numbers, lowercase, uppercase, symbols). - These scripts query the database or apply rules and send back a simple message ("Valid", "Invalid - Taken", "Weak", "Good", etc.) which the JavaScript then displays next to the input field, giving instant feedback without reloading the page.
// Inside checkun.php (Simplified) require_once('globals_nonauth.php'); // DB connection $username = isset($_POST['username']) ? stripslashes($_POST['username']) : ''; // ... (Check length/format) ... $e_username = $db->escape($username); $q = $db->query("SELECT COUNT(`userid`) FROM users WHERE login_name = '{$e_username}' OR username = '{$e_username}'"); if ($db->fetch_single($q)) { echo "<font color='red'>Invalid - Taken</font>"; // Send message back to JS } else { echo "<font color='green'>Valid</font>"; // Send message back to JS } $db->free_result($q);
- During registration, as you type your desired username or email, JavaScript code (
CAPTCHA (
macro1.php
,macro2.php
,captcha_verify.php
):- If account validation is enabled (
$set['validate_on']
), users might be redirected tomacro1.php
before they can proceed. -
macro1.php
generates a random 6-character string, stores it in the session ($_SESSION['captcha']
), and displays an image. - The image itself is generated by
captcha_verify.php
. This script reads the code from the session and uses PHP's GD graphics library to draw the characters with distortion, lines, and random dots, making it hard for bots to read automatically. It also includes a CSRF token in the form. - The user types the characters they see into a text box and submits the form to
macro2.php
. -
macro2.php
verifies both the submitted CAPTCHA code against the one stored in the session AND the submitted CSRF token. If both match, it marks the user as verified (UPDATE users SET verified = 1
) and redirects them back to the page they were trying to access ($_POST['refer']
). If not, it sends them back tomacro1.php
with an error.
// Inside macro1.php (Displaying CAPTCHA) $_SESSION['captcha'] = ''; // Generate random string and store here $valid_csrf = request_csrf_code('validation'); echo "... <img src='captcha_verify.php' /> ..."; // Image generated on the fly echo "<form action='macro2.php' method='post'> Text: <input type='text' name='captcha' /> <input type='hidden' name='verf' value='{$valid_csrf}' /> <input type='hidden' name='refer' value='...' /> <input type='submit' value='Verify' /> </form>";
// Inside macro2.php (Verifying CAPTCHA) // *** CSRF Check *** if (!isset($_POST['verf']) || !verify_csrf_code('validation', stripslashes($_POST['verf']))) { // Redirect back to macro1 with error } // *** CAPTCHA Check *** if (!isset($_SESSION['captcha']) || $_SESSION['captcha'] != stripslashes($_POST['captcha'])) { // Redirect back to macro1 with error } // --- If checks pass --- unset($_SESSION['captcha']); $db->query("UPDATE `users` SET `verified` = 1 WHERE `userid` = {$userid}"); header("Location: {$_POST['refer']}"); // Redirect user back
- If account validation is enabled (
These validation and anti-bot measures help maintain data integrity and prevent automated abuse.
Under the Hood: How CSRF Protection Works
Let's visualize the CSRF token flow when you change your password.
sequenceDiagram participant B as Browser participant PF as preferences.php (Form Display) participant PP as preferences.php (Processing) participant Func as global_func.php participant Sess as PHP Session B->>PF: Request preferences.php?action=passchange PF->>Func: request_csrf_code('prefs_passchange') Func->>Func: Generate unique token (e.g., 'abc123') Func->>Sess: Store token: $_SESSION['csrf_prefs_passchange'] = {token: 'abc123', issued: time()} Func-->>PF: Return token 'abc123' PF-->>B: Send HTML form with B->>PP: Submit form (action=passchange2, POST includes verf='abc123') PP->>Func: verify_csrf_code('prefs_passchange', 'abc123') Func->>Sess: Get stored token for 'prefs_passchange' ({token: 'abc123', ...}) Func->>Func: Compare 'abc123' (submitted) == 'abc123' (stored)? (Yes) Func->>Func: Check expiry time (Assume OK) Func->>Sess: Unset $_SESSION['csrf_prefs_passchange'] Func-->>PP: Return true PP->>PP: Proceed with password change logic... PP-->>B: Display "Password changed!" message
This shows how the token is generated, stored in the session, placed in the form, sent back, and verified against the session value before the action is allowed.
Key Files and Functions
- CSRF:
-
global_func.php
: Containsrequest_csrf_code()
,request_csrf_html()
,verify_csrf_code()
. - Used in almost all files that display forms (
preferences.php
,register.php
,shops.php
actions,yourgang.php
actions,macro1.php
, etc.). - Verification happens in the corresponding processing scripts/actions (
authenticate.php
,preferences.php
do_* actions,itembuy.php
,itemsell.php
,macro2.php
, etc.).
-
- Password Handling:
-
global_func.php
: Containsgenerate_pass_salt()
,encode_password()
,verify_user_password()
. - Used during registration (
register.php
), login (authenticate.php
), and password changes (preferences.php
).
-
- Validation & Anti-Bot:
-
checkun.php
: Checks username availability/validity via AJAX. -
checkem.php
: Checks email availability/validity via AJAX. -
check.php
: Checks password strength via AJAX. -
js/register.js
: Contains the JavaScript that calls the AJAX check scripts. -
macro1.php
: Displays the CAPTCHA challenge page. -
captcha_verify.php
: Generates the CAPTCHA image dynamically. -
macro2.php
: Verifies the user's CAPTCHA input and CSRF token. -
users
table: Stores theverified
flag.
-
Conclusion
You've now learned about the essential security functions built into MCCodes! These are vital for protecting the game and its players. We covered:
- CSRF Protection: Using unique, expiring tokens stored in sessions and hidden form fields (
request_csrf_code
,verify_csrf_code
) to prevent unwanted form submissions. - Password Security: Using unique salts (
generate_pass_salt
) and hashing (encode_password
,verify_user_password
) to protect stored passwords (though the MD5 algorithm is outdated). - Input Validation: Checking user input like usernames and emails for validity and uniqueness, often using AJAX (
checkun.php
,checkem.php
) for instant feedback. - Anti-Bot Measures: Using CAPTCHA challenges (
macro1.php
,macro2.php
,captcha_verify.php
) to distinguish humans from automated scripts.
These functions work together to create a more secure environment for players. Security often relies heavily on correct and safe interactions with the database. Let's explore how MCCodes handles that next.
Next Up: Chapter 10: Database Interaction
Previously: Chapter 8: Staff Panel & Permissions
First published April 21, 2025
Tags: MCCodes Walkthrough Tutorial