Secure Coding Fundamentals
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).
Competency Matrix
Section titled “Competency Matrix”| Level | Expectation |
|---|---|
| Regional | Fix basic SQL injection (parameterised queries). Identify obvious XSS. Validate user input. |
| National | Fix XSS with output encoding. Implement CSRF tokens. Secure session handling. Fix authentication bypass. |
| Squad/Team Selection | Fix deserialization vulnerabilities. Implement content security policy. Secure crypto usage. |
| International | Fix race conditions. Secure file handling. Advanced auth logic bugs. |
Technical Content
Section titled “Technical Content”Input Validation
Section titled “Input Validation”Core principle: Never trust user input. Validate on server-side.
Common validation requirements:
# Example: Python Flaskfrom flask import requestimport 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.
SQL Injection Prevention
Section titled “SQL Injection Prevention”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):
# INSECUREusername = request.form['username']query = f"SELECT * FROM users WHERE username='{username}'"cursor.execute(query)
# SECUREusername = 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.
XSS Prevention
Section titled “XSS Prevention”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:
// INSECURElet 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.
CSRF Prevention
Section titled “CSRF Prevention”Core principle: Verify requests originate from your application, not attacker site.
Implementation (CSRF tokens):
# Python Flask with Flask-WTFfrom 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 tokenPHP manual implementation:
// Generate tokensession_start();if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32));}
// In formecho '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// Validate on submissionif ($_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.
Authentication & Session Security
Section titled “Authentication & Session Security”Password storage (NEVER store plaintext):
# Python - SECURE password hashingimport hashlibimport 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 passPHP (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 loginsession_start();session_regenerate_id(true);
// Set secure cookie flagsini_set('session.cookie_httponly', 1);ini_set('session.cookie_secure', 1); // HTTPS onlyini_set('session.cookie_samesite', 'Strict');Insecure Deserialization
Section titled “Insecure Deserialization”Avoid deserializing untrusted data:
# INSECUREimport pickleuser_data = request.cookies.get('session')session = pickle.loads(base64.b64decode(user_data)) # DANGEROUS
# SECURE: Use signed tokens (JWT, Flask sessions)from flask import sessionsession['user_id'] = user_id # Flask handles serialization securelyCompetition context: If you see pickle.loads(), unserialize() (PHP), or JSON.parse() with user input, flag as vulnerability. Replace with safe alternatives.
Competition Strategy & Marking
Section titled “Competition Strategy & Marking”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.
False Flags / Wrong Paths
Section titled “False Flags / Wrong Paths”Practice Checklist
Section titled “Practice Checklist”- 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
References
Section titled “References”- Regional Roadmap: Infrastructure hardening
- National Roadmap: Infrastructure hardening
- Offensive perspective: SQL Injection, XSS
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.