Skip to content

What this is: Identifying and fixing common vulnerabilities in application code during secure code reviews.

Competition relevance: Infrastructure hardening (secure coding and code review). Code review tasks in PHP, Python, JavaScript, or C#. Focus on OWASP Top 10 vulnerabilities.

Why it matters: Secure coding prevents the vulnerabilities exploited in offensive operations. Both perspectives are tested.

WSOS 2026 alignment: This topic directly supports Section 5: Secure systems protection and defence (preventing vulnerabilities through secure coding) and Section 3: Secure systems design and creation (identifying and fixing security defects in code). You demonstrate competence by auditing code, identifying vulnerability classes, and applying correct mitigations.

Competition area: Core to the infrastructure hardening area (secure coding), where you review web application code for security vulnerabilities and implement fixes that eliminate exploit vectors.

Offline competence: Use language-specific offline documentation (man php, Python’s /usr/share/doc/python3, or pre-downloaded Zeal docsets). OWASP Top 10 and CWE lists should be downloaded offline. Many secure coding patterns are language-standard (e.g., prepared statements in PDO/MySQLi are documented in PHP manual).


LevelExpectation
RegionalFix basic SQL injection (parameterised queries). Identify obvious XSS. Validate user input.
NationalFix XSS with output encoding. Implement CSRF tokens. Secure session handling. Fix authentication bypass.
Squad/Team SelectionFix deserialization vulnerabilities. Implement content security policy. Secure crypto usage.
InternationalFix race conditions. Secure file handling. Advanced auth logic bugs.

Core principle: Never trust user input. Validate on server-side.

Common validation requirements:

# Example: Python Flask
from flask import request
import re
@app.route('/user/<user_id>')
def get_user(user_id):
# INSECURE: No validation
# query = f"SELECT * FROM users WHERE id={user_id}"
# SECURE: Validate input
if not user_id.isdigit():
return "Invalid user ID", 400
if not (1 <= int(user_id) <= 999999):
return "User ID out of range", 400
# Now safe to use (but still use parameterised queries!)
query = "SELECT * FROM users WHERE id=?"
result = db.execute(query, (user_id,))

Validation checklist:

  • Check data type (integer, string, email, etc.)
  • Check length (min/max)
  • Whitelist allowed characters (regex)
  • Range validation for numbers
  • Reject unexpected input (fail securely)

Competition context: Input validation alone isn’t enough for SQLi - must use parameterised queries. But validation prevents other issues.


INSECURE (String concatenation):

// PHP - VULNERABLE
$username = $_POST['username'];
$query = "SELECT * FROM users WHERE username='$username'";
$result = mysqli_query($conn, $query);

SECURE (Parameterised queries / Prepared statements):

// PHP - SECURE
$username = $_POST['username'];
$stmt = $conn->prepare("SELECT * FROM users WHERE username=?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();

Python (SQLite):

# INSECURE
username = request.form['username']
query = f"SELECT * FROM users WHERE username='{username}'"
cursor.execute(query)
# SECURE
username = request.form['username']
query = "SELECT * FROM users WHERE username=?"
cursor.execute(query, (username,))

Competition context: Find all SQL queries in code. Replace string concatenation with parameterised queries. Test that app still works.


Core principle: Encode all user-controlled data before rendering in HTML.

INSECURE (No encoding):

// PHP - VULNERABLE to XSS
$username = $_GET['username'];
echo "Welcome, $username!";
// User input: <script>alert('XSS')</script>

SECURE (Output encoding):

// PHP - SECURE
$username = $_GET['username'];
echo "Welcome, " . htmlspecialchars($username, ENT_QUOTES, 'UTF-8') . "!";

JavaScript context:

// INSECURE
let username = userInput;
document.getElementById('welcome').innerHTML = "Welcome, " + username;
// SECURE (use textContent, not innerHTML)
document.getElementById('welcome').textContent = "Welcome, " + username;

Python (Jinja2 templates):

<!-- Jinja2 auto-escapes by default -->
<p>Welcome, {{ username }}</p>
<!-- To disable escaping (dangerous unless you know data is safe):
<p>Welcome, {{ username|safe }}</p>
-->

Competition context: Find all places where user input is echoed to browser. Apply appropriate encoding function. Test with <script>alert(1)</script> payload.


Core principle: Verify requests originate from your application, not attacker site.

Implementation (CSRF tokens):

# Python Flask with Flask-WTF
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'supersecretkey'
csrf = CSRFProtect(app)
# In template:
# <form method="POST">
# {{ csrf_token() }}
# <input name="email">
# <button>Submit</button>
# </form>
# Flask-WTF automatically validates token

PHP manual implementation:

// Generate token
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// In form
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// Validate on submission
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF token mismatch");
}

Competition context: Add CSRF protection to state-changing forms (login, profile update, etc.). Ensure token is validated server-side.


Password storage (NEVER store plaintext):

# Python - SECURE password hashing
import hashlib
import os
# Hashing (registration)
salt = os.urandom(32)
password = request.form['password']
key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000)
# Store salt and key in database
# Verification (login)
stored_salt = user['salt']
stored_key = user['key']
password = request.form['password']
new_key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), stored_salt, 100000)
if new_key == stored_key:
# Password correct
pass

PHP (use password_hash):

// Hashing
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_DEFAULT);
// Store $hash in database
// Verification
$password = $_POST['password'];
$stored_hash = $user['password_hash'];
if (password_verify($password, $stored_hash)) {
// Password correct
}

Session security:

// Regenerate session ID after login
session_start();
session_regenerate_id(true);
// Set secure cookie flags
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // HTTPS only
ini_set('session.cookie_samesite', 'Strict');

Avoid deserializing untrusted data:

# INSECURE
import pickle
user_data = request.cookies.get('session')
session = pickle.loads(base64.b64decode(user_data)) # DANGEROUS
# SECURE: Use signed tokens (JWT, Flask sessions)
from flask import session
session['user_id'] = user_id # Flask handles serialization securely

Competition context: If you see pickle.loads(), unserialize() (PHP), or JSON.parse() with user input, flag as vulnerability. Replace with safe alternatives.


What gets marked:

  • Vulnerable code identified correctly with CWE/OWASP classification
  • Fix applied correctly (parameterised queries, output encoding, CSRF tokens)
  • Application still functional after fixes
  • Comments explaining what was fixed and why

Evidence and reporting requirements:

  • Before/after code diff showing exact changes (unified diff format preferred)
  • Line numbers of all changes with file paths
  • Vulnerability classification (e.g., “CWE-89: SQL Injection” or “OWASP A03: Injection”)
  • Test evidence showing vulnerability is fixed (screenshot of SQLi payload failing, XSS payload escaped)
  • Test evidence showing functionality preserved (legitimate input still works)
  • Brief write-up explaining the vulnerability and mitigation strategy
  • For web apps: screenshots of browser DevTools showing secure headers or escaped output
  • Code comments in-line explaining security changes
  • Clear naming for evidence files: app_sqli_fix_users_php_line42.png

Time allocation:

  • Find vulnerabilities: 10 minutes
  • Fix SQL injection: 10-15 minutes
  • Fix XSS: 10 minutes
  • Fix CSRF/auth issues: 15-20 minutes

Time-boxed drill suggestion: 60-minute secure code review sprint: Given a vulnerable PHP/Python web app with 3-5 OWASP Top 10 vulnerabilities (SQLi, XSS, CSRF, weak authentication), identify all vulnerabilities, fix them with appropriate mitigations (prepared statements, output encoding, CSRF tokens, password hashing), test that exploits no longer work, verify functionality is preserved, and document all changes with before/after code and test screenshots.



  • Can identify SQL injection vulnerabilities in code
  • Can fix SQLi with parameterised queries in PHP/Python
  • Can identify XSS vulnerabilities (reflected, stored, DOM)
  • Can fix XSS with appropriate output encoding
  • Can implement CSRF tokens manually
  • Understand password hashing (bcrypt, PBKDF2, Argon2)
  • Can identify insecure session handling
  • Can identify authentication bypass logic errors
  • Can test fixes to verify vulnerability is patched

Unofficial study guide, not endorsed by WorldSkills International or WorldSkills UK. Topics are example areas to prepare for, not the content of any test project: competition tasks are confidential, competitions are closed book, and formats change between competitions. More about this site.