Skip to main content

Overview

Secure coding is the practice of developing software in a way that guards against accidental introduction of security vulnerabilities. Security should be considered at every phase of the Software Development Life Cycle (SDLC) to ensure it’s an integral part of development, not an afterthought.
The cost of patching vulnerabilities increases dramatically as an application moves through SDLC phases. Early security integration is critical.

Security by Design in the SDLC

PhaseSecurity Processes
Requirements Definition• Gather specific security and privacy requirements
• Vulnerability assessment
Determining Specifications• Explicit security and privacy specifications
• Risk assessment
Design• Threat modelling
• Security design review
• Security tests included in test designs
Development• Code reviews
• Static application security testing (SAST)
Integration• Risk assessment
• Code reviews
• Dynamic application security testing (DAST)
• Grey-box penetration testing
Testing & Debugging• Code reviews
• SAST and DAST
• Penetration testing
Installation• Penetration testing
• Vulnerability assessment
Maintenance• Log monitoring & reporting
• Vulnerability assessment

Input Validation

Input validation is a security control where input is checked to be valid data before storage or processing. Invalid data should be discarded with appropriate UI feedback.

Front-End Validation

1

Use HTML Form Attributes

Implement client-side validation using HTML5 attributes:
<input    
    id="email" 
    type="email" 
    name="email" 
    maxlength="32" 
    required  
    pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$"  
    placeholder="Email address: [email protected]"  
>
2

Limit Input Length

Use maxlength to prevent long strings containing malicious scripts:
<input 
    id="password" 
    type="password" 
    name="password" 
    maxlength="64" 
    required  
    pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*_=+-]).{8,16}$"  
>
3

Provide Type Hints

Use placeholder text to guide users on expected input format

Back-End Validation

Never trust client-side validation alone. Always validate on the server side as client-side checks can be bypassed.
import re
import html

def validate_and_sanitize_input(user_input: str, input_type: str) -> str:
    """
    Validates and sanitizes user input based on type
    """
    if input_type == 'email':
        if not re.fullmatch(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", user_input):
            raise ValueError("Invalid email format")
    
    elif input_type == 'name':
        if not user_input.isalpha():
            raise ValueError("Name must contain only letters")
    
    elif input_type == 'number':
        if not user_input.isdigit():
            raise ValueError("Must be a valid number")
    
    # Sanitize by making web-safe
    return html.escape(user_input)

Common Vulnerabilities & Mitigation

SQL Injection Prevention

SQL injection attacks insert malicious SQL code through input fields to manipulate database queries.
# NEVER DO THIS
cur.execute(f"SELECT * FROM users WHERE username == '{username}' AND password == '{password}'")
1

Use Parameterized Queries

Always use query parameters instead of string concatenation
2

Validate Input

Implement defensive data handling for all user input
3

Salt Table Names

Add random 5-character strings to table names to prevent predictable targets
4

Require Authentication

Never accept form input without authentication

XSS (Cross-Site Scripting) Prevention

XSS attacks inject malicious scripts into trusted websites through unsanitized user input.

Test Scripts for Penetration Testing

<!-- Test these in input boxes to check for XSS vulnerabilities -->
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<iframe src="javascript:alert(1)"></iframe>
Only test XSS vulnerabilities on your own applications or with explicit permission. Unauthorized testing is illegal.

XSS Countermeasures

1

Code Reviews

Regularly review code for external script references
2

Validate Third-Party Libraries

Only use known, secure libraries. Locally serve after review when possible
3

Implement CSP

Use Content Security Policy to block unauthorized scripts:
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; script-src 'self'">
4

Sanitize Data

Use defensive data handling practices:
import html

safe_content = html.escape(user_input)
5

Declare Language & Charset

<html lang="en">
<head>
  <meta charset="utf-8">
</head>

Code Review Process

Code review is the manual examination of source code to identify security vulnerabilities at the code level.

Code Review Checklist

Privacy Review
  • Is sensitive data stored unnecessarily?
  • Are passwords encrypted before storage?
  • Can users download and delete their data?
  • Are log files properly protected?
Authentication Review
  • Are all users authenticated?
  • What authentication factors are used?
  • Are password complexity policies enforced?
Authorization Review
  • Are role-based permissions implemented?
  • Is authorization checked on every request?
  • Are sensitive files protected from unauthorized access?
Data Validation Review
  • Is all user input validated?
  • Is validation performed on entry?
  • Are parameterized queries used for databases?
  • What validation methods are used (whitelist, regex, etc.)?
Error Handling Review
  • Are errors logged with sufficient detail?
  • What error details are shown to users?
  • Are database errors properly logged?
Session Management Review
  • How is session state managed?
  • How are session IDs generated?
  • Are previous sessions deleted on new login?
  • Are session timeouts implemented?
Logging Review
  • Is logging implemented?
  • Where are logs stored?
  • Are log messages timestamped?
  • Is sensitive data excluded from logs?
Encryption Review
  • What encryption algorithms are used?
  • Are passwords salted and hashed?
  • Are encryption libraries current and from trusted sources?

Testing Approaches

Static Application Security Testing (SAST)

SAST analyzes source code before compilation to find security vulnerabilities. This is automated white-box testing.
- Reduces manual effort
- Time efficient
- Early SDLC integration
- 100% code coverage
- Detailed reporting
Tools: GitHub Security Scan, SonarQube, Bandit (Python)

Dynamic Application Security Testing (DAST)

DAST tests running applications without knowledge of internal structure or source code. This is automated black-box testing.
- No false positives
- Discovers runtime issues
- Finds user interaction vulnerabilities
- No source code required
Tools: OWASP ZAP, Burp Suite, Acunetix

Penetration Testing

Simulated cyber-attacks to find and exploit vulnerabilities in systems.
Students MUST only perform penetration tests on their own applications or with explicit written permission. Unauthorized testing is illegal.

Types of Penetration Testing

  • White-box: Full knowledge of application, live log monitoring
  • Grey-box: Partial knowledge of application
  • Black-box: No knowledge, only front-end access

Penetration Testing Tools

1

XSS Test Scripts

Use non-destructive scripts to test input validation
2

SQL Injection Tests

Test with common injection patterns:
  • 105 OR 1=1
  • " OR ""="
  • 105; DROP TABLE users
3

Common Credentials

Test authentication with common username/password combinations
4

API Testing

Use tools like Thunder Client or Postman to test API endpoints
Testing Tools:
  • ZAPROXY - Open source penetration testing
  • CyberChef - Data analysis and decoding
  • Wireshark - Network protocol analysis
  • Postman - API testing

Secure Form Processing Example

from flask import Flask, request, jsonify
import html
import re

api = Flask(__name__)

def validate_form_data(data):
    """
    Comprehensive form validation
    """
    errors = []
    
    # Validate email
    email = data.get('email', '').strip()
    if not re.fullmatch(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email):
        errors.append('Invalid email format')
    
    # Validate password strength
    password = data.get('password', '')
    if len(password) < 8 or len(password) > 20:
        errors.append('Password must be 8-20 characters')
    if not re.search(r'[A-Z]', password):
        errors.append('Password must contain uppercase letter')
    if not re.search(r'[a-z]', password):
        errors.append('Password must contain lowercase letter')
    if not re.search(r'[0-9]', password):
        errors.append('Password must contain digit')
    if not re.search(r'[@$!%*?&]', password):
        errors.append('Password must contain special character')
    
    # Validate username
    username = data.get('username', '').strip()
    if not username.isalnum():
        errors.append('Username must be alphanumeric')
    
    return errors

@api.route('/submit_form', methods=['POST'])
def submit_form():
    try:
        data = request.get_json()
        
        # Validate input
        errors = validate_form_data(data)
        if errors:
            return jsonify({'errors': errors}), 400
        
        # Sanitize data
        safe_data = {
            'email': html.escape(data['email'].strip()),
            'username': html.escape(data['username'].strip())
        }
        
        # Process data (database operations)
        # Use parameterized queries
        # cur.execute('INSERT INTO users (email, username) VALUES (?, ?)', 
        #             (safe_data['email'], safe_data['username']))
        
        api.logger.info(f'Form submitted successfully for {safe_data["email"]}')
        return jsonify({'success': True}), 201
        
    except Exception as e:
        api.logger.error(f'Form submission error: {str(e)}')
        return jsonify({'error': 'Submission failed'}), 500

Best Practices Summary

Key Security Principles:
  1. Defense in Depth: Implement multiple layers of security
  2. Principle of Least Privilege: Grant minimum necessary permissions
  3. Fail Securely: Ensure failures don’t compromise security
  4. Never Trust User Input: Always validate and sanitize
  5. Keep Security Simple: Complex security is harder to maintain
  6. Fix Security Issues Correctly: Understand root causes
  7. Use Established Libraries: Don’t roll your own crypto/security
  8. Security by Design: Integrate security from the start
  9. Regular Updates: Keep dependencies and libraries current
  10. Comprehensive Logging: Monitor and review security events

Additional Resources

Documentation

Testing Tools

Learning Resources

Build docs developers (and LLMs) love