8.5 Define and apply secure coding guidelines and standards

  • Security weaknesses and vulnerabilities at the source-code level
  • Security of Application Programming Interfaces (API)
  • Secure coding practices
  • Software-defined security

How do we make sure that our software code is secure?  How do we find weaknesses in our source code?  We might use a database of common vulnerabilities.

Common Vulnerabilities and Exposures or CVE is a free database that keeps track of cybersecurity vulnerabilities.

Each time a vulnerability is discovered in a commercial device, it is listed in this database.  The scan can (and should) check for vulnerabilities that are present in this database.  These vulnerabilities are well researched and usually have patches.

It is available at https://cve.mitre.org/

Another database is the National Vulnerability Database, which is available at https://nvd.nist.gov/

These databases only track public vulnerabilities.  When a company discovers a vulnerability, it might keep it a secret until it has time to release a patch.  This way, hackers won’t read about the vulnerability and take advantage of unpatched systems.

The Common Vulnerability Scoring System gives each vulnerability a score.  If we don’t have the time or money to fix every vulnerability right away, we can use the score to determine whether a vulnerability is a low, medium, or high risk.  Then we can manage them accordingly.  The score is calculated from three areas

  • Base Findings

    • Attack Vector (AV) – how does the attack enter the system?  Does the hacker need to be physically present or can the attack happen remotely?

      • Network – can happen over the internet

      • Adjacent – must be over the same protocol, but not necessarily the same physical location.  Attack can happen over VPN, MPLS, Bluetooth, etc.

      • Local – must have physical access via SSH, console, etc. or can manipulate another user to execute the attack

      • Physical – must have direct physical access to physically manipulate the device such as in a cold boot attack or connecting a malicious peripheral

    • Attack Complexity (AC) – how complicated is the attack? 

      • Low – no special conditions are required in order to perform the attack

      • High – the hacker must have special knowledge about the device being attacked, or must prepare the device in order to make it vulnerable

    • Privileges Required (PR) – what privileges are required to perform the attack?

      • None – no privileges are required

      • Low – the hacker requires basic user privileges

      • High – the hacker requires administrator access

    • User Interaction (UI) – is another user’s participation required in order to execute the attack?

      • None

      • Required

    • Scope (S) – does a vulnerability in one component impact resources in other components?

      • Unchanged

      • Changed

    • Confidentiality (C) – does the vulnerability cause a loss of confidentiality?

      • High – a direct and serious breach of confidentiality

      • Low – information loss is limited

      • None – no information is lost

    • Integrity (I) – does the vulnerability cause a loss of data integrity?

      • High – a direct and serious breach of integrity

      • Low – limited data is modified

      • None – no information is modified

    • Availability (A) – does the vulnerability cause a loss of data availability?

      • High – a complete loss of availability

      • Low – interruptions to the data is experienced, but complete loss does not take place

      • None – information availability is not affected

  • Temporal

    • Exploit Code Maturity (E) – how likely is it that the attack will take place?

      • Not Defined – we don’t know

      • High – we know for a fact that an exploit will happen.  The exploit is the target of well-known viruses.  Hackers have created automated tools that exploit the vulnerability.

      • Functional – there is functional code that can exploit the vulnerability

      • Proof-of-Concept – an attack is not practical, but the proof-of-concept exists

      • Unproven – we haven’t seen an attack and haven’t been able to create a code that uses the exploit

    • Remediation Level (RL) – can we fix it?

      • Not Defined – we don’t know

      • Unavailable – there is no way to fix the vulnerability

      • Workaround – there is a way to fix the vulnerability, but it is an unofficial solution.  The vendor does not have a fix.  Users may have created their own patch.

      • Temporary Fix – there is an official fix created by the vendor, but it is temporary

      • Official Fix – the vendor has created an official patch or a new version of the software that does not include the vulnerability

    • Report Confidence – how confident are we that this vulnerability exists?

      • Not Defined – we don’t know

      • Confirmed – the vendor has confirmed that the vulnerability exists, or independent reputable researchers have confirmed that the vulnerability

      • Reasonable – we aren’t 100% sure because we don’t have access to the source code but there are many details that indicate the vulnerability exists

      • Unknown – we don’t know.  There are some details showing that the vulnerability exists, but we aren’t confident enough

  • Environmental

    • Security Requirements (CR, IR, AR) – does this affect Confidentiality, Integrity, and/or Availability, and what is the impact on the organization

      • Not Defined – we don’t know

      • High – catastrophic effect on the business

      • Medium – serious effect on the business

      • Low – minimal effect on the business

    • Modified Base Metrics – we can modify our score based on the specific circumstances of our user environment

Common Weakness Enumeration or CWE is another database maintained by the MITRE corporation.  The Common Weakness Scoring System gives each weakness a score.  We score each weakness based on the following

  • Base Findings

    • Technical Impact (TI) – what can the weakness do?  What can it damage?

    • Acquired Privilege (AP) – what privileges can a hacker obtain when exploiting the weakness?

    • Acquired Privilege Layer (AL) – which layer does the hacker gain access to?

    • Internal Control Effectiveness (IC) – how effective are internal controls at mitigating the effects of the weakness?

    • Finding Confidence (FC) – how confident are we in our evaluation of the weakness?

  • Attack Surface

    • Required Privilege (RP) – the privilege that the hacker needs before executing the attack

    • Required Privilege Layer (RL) – the layer that the hacker needs to be in to exploit the weakness

    • Access Vector (AV) – the channel that a hacker uses to exploit the weakness

    • Authentication Strength (AS) – authentication strength that protects the system

    • Level of Interaction (IN) – how much interaction a victim needs in order for the hacker to successfully exploit the system

    • Deployment Scope (SC) – is the weakness present in all versions of the software or only some?

  • Environmental

    • Business Impact (BI) – impact on the business if the weakness is exploited

    • Likelihood of Discovery (DI) – how likely is it that an attacker will discover that this weakness exists

    • Likelihood of Exploit (EX) – if discovered, how likely is it that the attacker will use it to exploit something

    • External Control Effectiveness (EC) – are there controls that can mitigate an attack, and how effective are they?

    • Prevalence (P) – how common is the weakness?

The worst hardware weaknesses at the time of writing (January 2022)

  • Improper Isolation of Shared Resources on a System-on-a-Chip

  • On-Chip Debug and Test Interface With Improper Access Control

  • Improper Prevention of Lock Bit Modification

  • Security-Sensitive Hardware Controls with Missing Lock Bit Protection

  • Use of a Cryptographic Primitive with a Risky Implementation

  • Internal Asset Exposed to Unsafe Debug Access Level or State

  • Improper Restriction of Software Interfaces to Hardware Features

  • Improper Handling of Overlap Between Protected Memory Ranges

  • Sensitive Information Uncleared Before Debug/Power State Transition

  • Improper Access Control for Volatile Memory Containing Boot Code

  • Firmware Not Updateable

  • Improper Protection of Physical Side Channels

The worst software weaknesses are

  • Out-of-bounds Write

  • Improper Neutralization of Input During Web Page Generation

  • Out-of-bounds Read

  • Improper Input Validation

  • OS Command Injection

  • SQL Injection

  • Use After Free

  • Improper Limitation of a Pathname to a Restricted Directory

  • Cross-Site Request Forgery

  • Unrestricted Upload of File with Dangerous Type

  • Missing Authentication for Critical Function

  • Integer Overflow or Wraparound

  • Deserialization of Untrusted Data

  • Improper Authentication

  • NULL Pointer Dereference

  • Use of Hard-Coded Credentials

  • Improper Restriction of Operations within the Bounds of a Memory Buffer

  • Missing Authorization

  • Incorrect Default Permissions

  • Exposure of Sensitive Information to an Unauthorized Actor

  • Insufficiently Protected Credential

  • Incorrect Permissions Assignment for Critical Resource

  • Improper Restriction of XML External Entity Reference

  • Server-Side Request Forgery

  • Improper Neutralization of Special Elements used in a Command

Some of the worst things you can do

  • Store a password in plain text

  • Store an empty password in a configuration file

  • Use a hard-coded password

  • Store a password in a configuration file

  • Weak encoding of a password

  • Least privilege violation

  • Use insufficiently random values

  • Exposure of private personal information

  • Race Condition

  • Time-of-check Time-of-use

  • Insecure temporary files

  • Session Fixation

  • Use of System.exit()

  • Direct use of threads

  • Unrestricted externally accessible lock

  • Unchecked error condition

  • Declaration of catch for generic exception

  • Declaration of throws for generic exception

  • Improper input validation

  • Improper neutralization of special elements used in a command

  • Improper neutralization of input during web page generation

  • Improper neuralization of special elements used in an SQL Command

  • Improper control of resource identifiers

  • Use of an inherently dangerous function

  • Creation of chroot jail without changing working directory

  • Improper clearing of heap memory before release

  • Direct management of connections

  • Direct use of sockets

  • Uncaught exception

  • Execution with unnecessary privileges

  • Unchecked return value

  • Use of getlogin() in multithreaded application

  • Missing release of memory

  • Improper resource shutdown or release

  • Use after free

  • Double free

  • Use of uninitialized variable

  • Use of obsolete function

  • Comparison of classes by name

  • Active debug code

  • Exposure of data to wrong session

  • Public cloneable method without final

  • Use of inner class containing sensitive data

  • Critical public variable without final modifier

  • Private data structure returned from a public method

  • Public data assigned to private array-typed field

  • Trust boundary violation

  • Compiler removal of code to clear buffers

  • Creating debug binary

  • Missing custom error page

The Open Web Application Security Project or OWASP is an online community that shares best practices for creating secure web applications. 

The OWASP Security Knowledge Framework is an open-source application that allows developers to understand common security principles.  It is a framework for integrating security into a web software application.

According to OWASP, the top ten risks are

  • Broken Access Control

  • Cryptographic Failures

  • Injection

  • Insecure Design

  • Security Misconfiguration

  • Vulnerable and Outdated Components

  • Identification and Authentication Failures

  • Software and Data Integrity Failures

  • Security Logging and Monitoring Failures

  • Server-Side Request Forgery

API Security

An API or Application Programming Interface is an interface that allows other programs to authenticate with ours.  How do we authenticate each user who connects to our API? 

  • If our API is delivering public information, we may not need to perform any authentication.  We don’t know who is accessing what.

  • Typically, an API user must provide the API with a secret key (known only to the user and the API).  The key is generated symmetrically.  The key system allows us to identify each user and encrypt the data.

    The API

  • We might accept a username and password combination.  This is a bad idea because the password if it is sent in plain text.  The username/password combination allows us to identify each user but does not allow us to encrypt the data.

  • Certificate-based authentication.  A certificate identifies the user and provides encryption.  It also allows the user to verify the identity of the API.

  • Single Sign On or federation allows us to outsource access and permissions to a third party or to another system.

We need to test our API regularly to ensure that it is functioning correctly

  • Does the API deliver the data requested?

  • Does the API deliver the data quickly?

  • Does the API leak data that should not be disclosed?

  • Does the API authenticate each user properly and verify that they are authorized to access each function?  A user should not be able to access functions that they are not entitled to.

We should test our API when we test our software.  A reasonableness check tells us whether the software is delivering a reasonable value for the inputs that it was provided.  The test should be performed by a person who is different from the person who wrote the software.

According to OWASP, the top 10 API issues are

  • Broken Object Level Authorization – we don’t take a granular approach to security.  Every time a user accesses an object, we should verify that he has authorization.

  • Broken User Authentication – we don’t verify the user’s credentials

  • Excessive Data Exposure – we expose all properties and objects to the client.  We don’t filter objects or search results based on the user’s permissions.

  • Lack of Resources – we don’t have enough resources to accommodate all of the requests.  We don’t set limits on the number of queries made.  This leads to Denial of Service.

  • Broken Function Level Authorization – we don’t have a clear system for determining whether a user has permission to run a function.  Users have access to functions that they shouldn’t have.

  • Mass Assignment – we don’t properly filter objects.  If the API code is sloppy, it might guess the properties of objects.

  • Security Misconfiguration – we use a default configuration, no security, no permissions, or other misconfigurations

  • Injection – a hacker can inject malicious data as part of their query

  • Improper Asset Management – we have a larger attack surface than necessary

  • Insufficient Logging & Monitoring – we do not log queries or do not keep logs long enough

Some ways we can prevent injection attacks

  • Use of Prepared Statements

  • Use of Stored Procedures

  • Allow-List Input Validation

  • Escape All User-Supplied Input

  • Use of Least Privilege in the Database Users

Some ways we can validate inputs

  • Use of an allow list to reject characters that are not on the list

  • Use Canonical Encoding Rules to encode the entire set of text.  This ensures that the code is encoded uniformly.

  • Use regular expressions to validate inputs such as e-mail addresses, zip codes, and phone numbers.

  • Validate inputs on the client side and on the server side

  • Validate uploaded ZIP files to ensure that they do not contain viruses or bombs before unzipping them.  A malicious ZIP file can be designed so that it will occupy all the free space on a hard disk when it is decompressed.

  • Validate uploaded images.  Remove metadata and malicious content from images.

  • Rename all uploaded files to standard formats.

  • The server should determine the directory where uploaded files are stored.

Secure Coding Practices

What are some secure coding practices?

  • Input Validation

    • Validate all data on the server (not just at the client side)

    • Classify all data as trusted or untrusted

    • Provide proper encoding for all inputs

    • Reject any inputs with validation failures

    • Validate data after UTF-8 decoding, if the system provides UTF-8 character sets

    • Validate all data before processing

    • Use only ASCII characters in headers

    • Validate redirect data

    • Validate data for expected type, range, and length

    • Use a whitelist of allowed characters

    • Escape hazardous characters such as <> “’%{}&+\;

  • Output Validation

    • Encode all output data on the server

    • Encode all characters unless they are known to be safe to the end user

    • Sanitize all output of data sent to SQL queries

    • Sanitize all output of data sent to the operating system

  • Authentication and Password Management

    • Use authentication for all pages and resources

    • Validate authentication on a server

    • Use standard authentication services where possible

    • Centralize authentication where possible

    • Separate authentication controls from the resource that it protects

    • Ensure that authentication fails securely

    • Ensure that administrative and account management functions are at least as secure as the authentication method

    • When storing credentials, store passwords via one-way salted hashes.  Use a cryptographically strong one-way hash.

    • Protect the hashed password table from modification by users.  This prevents a user from substituting a hash with a hash of a known password.

    • Hash passwords on the server

    • When the authentication fails, the error response must not tell the user whether the wrong password was entered, or the wrong username was entered.  This prevents a hacker from determining whether he has guessed a valid username.

    • Use authentication when connecting to external systems

    • Do not store passwords in source code

    • Use HTTP POST to transmit credentials, not HTTP GET

    • Send non-temporary passwords via encrypted methods.  Only temporary passwords may be sent by unencrypted e-mail

    • Enforce password complexity and length requirements

    • Prevent users from reusing their passwords

    • Obscure password entry boxes

    • Disable accounts after several invalid logins

    • Ensure that password reset and change operations have the same level of security as authentication

    • Use password reset questions that support random answers

    • When resetting a password, send the temporary password or password reset link to a preregistered address

    • Force temporary passwords to expire quickly

    • Force users to change temporary passwords upon the first use

    • Notify users when their passwords have been reset.  Notify users via more than one method if possible (e-mail and SMS)

    • Do not allow users to change passwords until they are at least one day old.  This prevents attacks on password reuse.

    • Enforce password changes

    • Disable the ability for a password field to remember the password

    • When a user logs in, they should be notified about the last time their account was logged into or attempted to be logged into.  The user should be able to report a login that is suspicious.

    • Monitor the attack surface for attacks against multiple accounts using the same password

    • Force users to reauthenticate when performing critical functions such as resetting their password or viewing financial information

    • Change all default passwords and usernames, especially vendor-supplied accounts

    • Use Multi Factor Authentication

  • Session Management

    • Use the server’s session controls

    • Sessions must be created by the server

    • Session IDs must be generated randomly

    • The domain and path for session cookies should be restricted to the site

    • When logging out, the logout function should terminate the session

    • The logout function should be available on every application page

    • Force sessions to time out after a short period of inactivity

    • Disable persistent logins

    • Enforce periodic session terminations, even in active sessions

    • Terminate sessions created before login

    • Generate a new session ID after the user logs in

    • Do not allow multiple concurrent logins with the same username

    • Do not expose session IDs in URLs, logs, or error messages

    • Protect session data on the server

    • Generate a new session ID when the connection changes from HTTP to HTTPS

    • Set cookies with HTTPOnly

  • Access Control

    • Use trusted system objects to make access decisions

    • Use a single site-wide system to make access decisions

    • Ensure that access controls fail securely

    • Deny access when the application is unable to access the authentication system

    • Verify authorization on every request

    • Restrict access to files to authorized users

    • Restrict access to protected URLs, functions, object references, services, application data, policy information, and configuration

    • Use encryption and integrity checking

    • Limit the number of transactions a user can perform in a period of time

    • Disable accounts and terminate active sessions when authorization ceases

    • Periodically revalidate a user’s authorization even when a session is active

    • Provide service accounts and external accounts with the least privilege possible

    • Create an access control policy

  • Cryptographic Practices

    • Implement cryptographic processes only on the server

    • Ensure that cryptographic processes fail securely

    • Generate random numbers via a random number generator that is approved by FIPS 140-2

    • Create a policy for managing cryptographic keys

  • Error Handling

    • Do not include sensitive information in error messages.  Information includes session IDs, account information, and debugging

    • Use custom error pages

    • Have the application handle application errors

    • Free allocated memory during an error

    • Log successful and unsuccessful security events

    • Perform logging on the server

    • Restrict access to logs

    • Do not log sensitive information

    • Log all input validation failures, authentication attempts, access control failures, unexpected changes to data, tampering, exceptions, attempted use of invalid tokens, attempted use of expired tokens, administrative functions, backend connections, and use of cryptographic module failures

    • Use cryptographic hashes to validate log integrity

  • Data Protection

    • Use least privilege to restrict users to the specific functions and data that they require

    • Protect temporary copies of sensitive data and purge them when no longer required

    • Encrypt sensitive data

    • Protect server-side source code so that it cannot be downloaded

    • Do not store passwords, strings, or other information

    • Remove comments in production code if the comments are accessible by users

    • Remove unnecessary documentation

    • Disable auto complete

    • Disable client-side caching

  • Communication Security

    • Encrypt sensitive information

    • Validate TLS certificates to ensure that they have valid domain names, expiry dates, and intermediate certificates

    • Do not allow failed TLS connections to fail over to insecure connections

    • Use TLS for all communication

    • Use TLS for connections to external systems

    • Use a standard TLS system for all connections

    • Specify character encoding for all connections

  • System Configuration

    • Use the latest version of all server software and frameworks

    • Apply all patches to server software and frameworks

    • Disable directory listings

    • Restrict service accounts to the least privileges

    • Remove or disable unnecessary functionality, files, and services

    • Remove test code

    • Use the robots.txt file to prevent disclosure of the directory

    • Define HTTP methods that are supported

    • Disable unnecessary HTTP methods

    • Remove unnecessary information from HTTP response headers

    • Separate development and production environments

    • Use a software change control system

  • Database Security

    • Use strong type parameterized queries

    • Validate all inputs and outputs

    • Use strongly typed variables

    • Use the lowest level of privileges when connecting to the database

    • Do not hardcode connection strings.  Store connection strings in a separate configuration file and encrypt the file.

    • Use stored procedures to access data

    • Close the database connection when it is not required

    • Change all default usernames and passwords

    • Use strong passwords and multi factor authentication

    • Disable unnecessary database functions

    • Remove default content

    • Use multiple account types – one for each privilege level – when connecting to the database

  • File Management

    • Do not pass user supplied data to an include function

    • Do not allow unauthenticated users to upload files

    • Restrict the types of files that can be uploaded

    • Verify that the header of the file type being uploaded matches its extension

    • Do not save files in the same directory that the application is hosted in

    • Do not allow files to be uploaded if they can be interpreted by the server

    • Disable execution privileges in the directory where the uploaded files are stored

    • Use safe uploading in UNIX by mounting the file directory as a separate logical drive

    • Use a white list of allowed file names and types

    • Do not pass user supplied data, directory, or file paths

  • Memory Management

    • Use input and output control for untrusted data

    • Verify the buffer sizes

    • When copying data using a function, verify that the destination buffer is the same size as that of the source buffer

    • Check buffer boundaries to ensure that you don’t write past the allocated space

    • Truncate input strings to a reasonable length

    • Avoid the use of vulnerable functions

  • General Coding Practice

    • Use tested managed code for common tasks

    • Use task-specific APIs for operating system tasks

    • Do not allow the application to send commands directly to the operating system

    • Do not allow the use of application-initiated command shells

    • Use checksums to verify integrity of code, libraries, executables, and configuration files

    • Use locking to prevent race conditions

    • Protect shared variables from concurrent access

    • Explicitly initialize all variables

    • Raise elevated privileges as late as possible, and terminate them as soon as possible

    • Understand the programming language’s numeric calculation methods

    • Prohibit users from altering the code

    • Review all third-party code and libraries to ensure that they are needed and that they are safe

    • Cryptographically sign all code and updates

    • Ensure that users verify the cryptographic signature of any update

Software Defined Security

In the past, everything ran on dedicated hardware.  We could understand the data pathway by tracing the physical connections in the hardware and implementing appropriate technology.  For example, we knew that we could install a firewall and DLP between the internet connection and the router.

Now we have many new technologies, including virtual servers, virtual private clouds, SD-WANs, VxLANs, elastic computing, software defined networking, and software as a service.  We no longer have physical access to much of the infrastructure.  So, we need a new way to secure our data.  We call this new idea Software Defined Security or SDS

Like other Software Defined services, in SDS we create some rules for how we want the system to behave, and the software writes the necessary configuration to modify the various components.  The key advantages

  • We don’t have to know what the physical infrastructure looks like or how it is connected

  • When there are many different types of devices created by many different vendors, an administrator must normally have knowledge to configure each product.  With SDS, we don’t have to know how to configure any specific server operating system, hardware, or software. 

  • We can create specific rules that use the context to determine whether to allow or block access.  We don’t have to create broad general rules that are insecure or that block legitimate requests.

  • We can manage our entire security system from a single interface (a single pane of glass).

We can integrate our security system with an AI system or a SOAR to make decisions in response to changing conditions.  We can detect attacks as they happen and block further access to additional systems.