Saturday, 14 April 2018

Insecure Coding Habits that One Should Avoid !

Introduction

Security code review is an essential part of the software security assurance process. It helps in early identification and remediation of security flaws induced in the software lifecycle. Backdoors, Business logic flaws, Race conditions, Injection flaws, Cryptographic vulnerabilities are some of the major security vulnerabilities that can be successfully detected using static code analysis.

As a security engineer for the last 12+ years, I am regularly involved in reviewing the application code for potential security anti-patterns.  During this period, I have seen software development methodologies getting evolved from Waterfall to Agile and more recently transitioning into DevOps.  However over the course of time there are certain typical insecure coding habits religiously adopted by developers that has largely remained unchanged. These insecure coding practices were directly responsible for more than 50% software vulnerabilities identified in a given code base.
The idea of this blog post is to educate software developers in avoiding these 5 common security mistakes and help them deliver more secure code. As usual please feel free to share your valuable thoughts and feedback about this post.

1. Blindly Trusting User Input
An application or program requires user input to make logical decisions and accordingly process information. A general rule of thumb for application or program handling any form of input is to first sanitize the input prior trusting it. Failure to properly validate user input will make the application vulnerable to some of the most notorious security flaws such as Cross Site Scripting, Code Injection Flaws, Open Redirects etc.  
As an example, sometime back I reported multiple security vulnerabilities to Jive primarily caused by lack of input validation. Jive is a SaaS based on demand collaboration and knowledge management service.  If the Jive service is not available due to some reason for e.g. scheduled site maintenance the community users were redirected to a maintenance page with a generic error message.
Note: The vulnerability was promptly addressed by Jive’s security team. So kudos to them.  

The URL for the page looked like:
https://xxx.jive.com/maintenance.jspa?hostname=example.com

The query string parameter hostname value was user controlled and blindly trusted by the application. As a result this page was found vulnerable to open redirect and reflected cross site scripting.

If the hostname value was changed to an attacker controlled arbitrary domain such as evil.com, Jive redirected the user without verifying whether the domain is safe or not resulting in open redirect vulnerability.  Refer screenshot [1]

Vulnerable URL:
https://xxx.jive.com/maintenance.jspa?hostname=evil.com

1 - Open Redirect Vulnerability 
Similarly if the hostname value contained a javascript payload for e.g. ”><img src=1 onerror=alert(“evilcode”)> the application blindly trusted it. The payload was reflected back in the server response triggering javascript execution on the user browser aka reflected cross site scripting. Refer screenshot [2]

Vulnerable URL:
https://xxx.jive.com/maintenance.jspa?hostname=”><img src=1 onerror=alert(“evilcode”)>
2 - Reflected Cross Site Scripting
The real culprit for both the issues is the below single line of Java code
<a href="<%= request.getParameter("hostname")%>">  
 
The java servlet method request.getParameter(java.lang.String name) returns the value of a request parameter as a String, or null if the parameter does not exist. The developer failed to properly sanitize the value obtained from request.getParameter()and blindly utilized it in the application code leading to open redirect and reflected cross site scripting vulnerabilities.

Jive fixed the vulnerability by dropping the query string parameter hostname from the page logic meaning it was never required at all.

Similarly imagine a scenario wherein user input value obtained from a HTML form field is captured via a query string parameter and delegated to the backend query statement without performing any input sanitization. For e.g. refer to the below code snippet:

“Select name, age, weight, height, contact, address from USER_MASTER where id = “+request.getParameter(“userid”);

This will lead to SQL Injection another notorious security vulnerability.
 
2. Hard Coding of Secrets

A computer application or program is often required to communicate with other systems or services such as databases, file systems, web services or external application programming interface (APIs)  for reading, writing or processing information. For accessing such systems dedicated service accounts need to be setup for the application or program. Such accounts helps in authenticating the program prior provisioning access.

The service accounts are protected using passwords. Likewise an API requires a valid key and secret to communicate. Additionally programs are also required to use cryptographic secrets to protect sensitive information. Passwords, API secrets, service account credentials, crypto keys etc all this information constitute secrets. If the secrets gets compromised it will allow an adversary to gain unauthorized access to the application and the associated systems.

One of the most common anti-pattern associated with this use case is CWE-798: Use of Hard-coded Credentials in the application code.  Typical use cases involves, developer hard coding database connection credentials, FTP credentials, crypto keys etc in the source code.
As an example the below code snippet is from a configuration file where in you can clearly observe database connection credentials hardcoded:  
db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://db-user.aws.botuser1.com
db.default.url=${?TAGGER_DB_URL}
db.default.username=db_user
db.default.password=X@nderC@ge2018


Another example is the below code snippet from Java. The code is trying to obtain the API key value (or secret) from a centralized key management service. However in case the key is not available the developer has ensured there is fallback key (default value) that is hardcoded in the code:

private val apikey = CKMSUtils.get("key.apisecret”).getOrElse("AISyC3IPHURKH70DKYIBOM")

3. Logging Sensitive Data
 
No application is foolproof or error prone. There are times when a program behaves in an absurd manner causing outage or downtime for the parent application. This can be due to a programming flaw or a bug in the underlying software powering the application. Logical placement of logging or tracing statements in the code can greatly assist developers in quickly debugging the root cause.
 
However often developers fail to comment out or disable logging statements capturing extremely sensitive data such as Personally Identifiable Information, credit card information, application secrets from the production code. This is a very common anti-pattern which is documented in Common Weakness Enumeration dictionary as CWE-532: Information Exposure Through Log Files
 
Common Examples

Typical use cases involve developers leaving logging statements in production code capturing user passwords, authenticated session tokens, sensitive information such as fine grain location coordinates tied to a user id, unique device tracking identifiers and much more  in the application logs. Some of the real world code snippets highlighting this insecure coding practice is as shown below:

1. User ID and Password information getting logged

log.info("\n\t=== Logging in as "+this.userid+":"+this.passwd+" ===");
2. Authenticated session cookie getting logged

logger.info('Initiating with user cookie = ' + this.cookie);

3. User location information getting logged

logger.debug('For userid =' +uid+ ' lat is = ' + req.query.lat + ' and lon = ' + req.query.lon);

Debugging Enabled in Native Apps

Another common abuse pattern which I have consistently observed in native android mobile apps code is the setting android:debuggable in AndroidManifest.xml set to “true”. Please refer to the below code snippet:

<application android:debuggable="true"/>

This allows an adversary who is running the App on their device to attach a debugger to it, which may expose sensitive data and/or the inner workings of the App to the attacker.

4. Using Weak Cryptographic Algorithms

The primary purpose of using cryptography is to help:
  • Protect the confidentiality of the data
  • Preserve the integrity of the data
This can be achieved by using a secure cryptographic algorithm for the applications/systems involved in processing sensitive information such as customer secrets, credit card data, personally identifiable information and more. Failure to use a secure cryptographic algorithm may lead to information getting compromised as the algorithm in use might be vulnerable to common crypto attacks such as collision, brute force attack, replay attacks etc.  This is a very common anti-pattern as highlighted in CWE-327: Use of a Broken or Risky Cryptographic Algorithm

Some of the real world code examples describing this antipattern are as follow:

Pattern 1: Using Weak Hashing Algorithm to Generate Password Hashes

A password forms the first line of defense in providing access to any computer system, network or application. Hence it is very important to protect password data from getting compromised at the hands of an adversary. The recommended security best practice when dealing with passwords is to never store it in plain text. Instead use a secure hash function to generate a hash* for the given password. Then store this hash in a hardened database.
* A hash is a unique irreversible fixed length output for a given string.

For user authentication purpose, first generate a hash for the user provided password (captured from the login screen or form) and then compare it with the hash already available in the application database for that user. If both the password hashes match then user authentication is successful else it has failed.   

The most common anti pattern associated with this use case is choosing a weak hash function to generate password hashes. Certain hashing algorithms such as MD5, SHA-1 are vulnerable to collision attack meaning they generate same hash output for two different strings.  
As an example, consider below code snippet from Java where the developer is using MD5 to generate message digest or hash for a given password.

MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
passwd = new String(
Base64.encodeBase64(md.digest(input.getBytes())));

Another example showcasing this anti-pattern in Javascript is shown below. The developer is using SHA1 to compute a hash for a given secret.

computed = require('crypto').createHash('sha1').update(secret + raw).digest('hex');

Pattern 2: Using Weak Symmetric Key Ciphers to Encrypt Data


Symmetric key algorithm is the preferred choice while encrypting massive amount of data. As compared to Asymmetric or public key cryptography, Symmetric key algorithms are relatively fast which is a significant benefit when system performance is a key requirement.


One of the most common anti-pattern associated with this use case is developers often tend to use a weak symmetric key algorithm such as Data Encryption Standard (DES) for encrypting data in bulk. DES is considered as weak since it offers an effective key length of 56 bits which is vulnerable to brute force attack.


As an example the below code snippet from java depicts DES usage:


desCipher = Cipher.getInstance("DES");


Note: Triple DES or 3DES is also considered as deprecated. The preferred choice should be Advanced Encryption Standard or AES.

Pattern 3: Using Weak Cipher Modes

Another common anti-pattern associated with a weak cryptographic implementation is using a weak cipher mode of operation for a symmetric or asymmetric algorithm. Electronic Code Book (ECB) mode of operation is considered as weak as it generates identical ciphertext blocks from plain text blocks, making replay attacks very easy.

In the below examples in java you can clearly figure out although the crypto algorithms in use are secure i.e. AES and RSA but the cipher mode chosen is weak:  

Cipher aescipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
Cipher rsacipher = Cipher.getInstance("RSA/ECB/PKCS7Padding");

5. Cleartext Transmission
Hypertext Transfer Protocol Secure (HTTPS) is the secure version of HTTP. The protocol ensure the communication between the user's browser (client) and the website (web server) is encrypted all the time. Thus protecting the transmission from  eavesdropping or Man-in-the-Middle attack. HTTPS uses Transport Layer Security (TLS) which in turn leverages Public Key Infrastructure (PKI) to secure the communication. If the application or software is not configured to use HTTPS, this may lead to a common vulnerability as articulated in CWE-319: Cleartext Transmission of Sensitive Information

Some of the real world scenarios using this anti-pattern are:
  • Authentication flows such as Oauth, Single Sign On, Web login form pages etc configured in the application to support plain text communication
  • Native Mobile Apps using  API endpoints over HTTP to back post data
As an example, the “Oauth” endpoint configured in the deployment descriptor for the application with the below entry highlights clear text communication:
oauth.url = "http://bot.com/meeting/v1/client?dest=btool"

Another example where login page URL is configured to use http:// in java code snippet:
  
private static final String LOGIN_URL = "http://login.xtool.com/";

This example code snippet in Objective C for a native iOS App is using charles proxy for debugging purpose. However the endpoint supports cleartext transmission.

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://charlesproxy.com/getssl"] options:@{} completionHandler:nil];

Secure Coding Best Practices

In this section, I am going to talk about secure coding tips which the developers can adopt to overcome insecure coding habits as discussed earlier:

Always Sanitize User Input

The first mantra for developers is to never trust user input, always sanitize it to filter-out dangerous characters (<,>,’). Some of the best practices in this regards would be:
  • Always validate input obtained from any source such as cookies, feeds, forms, fields, files, headers, query string parameters, Urls virtually everything. 
  • For preventing cross site scripting leverage best practices as highlighted in OWASP XSS Prevention Cheat Sheet
  • Use parameterized queries to construct backend query statements to eliminate SQL injection. Also refer to the OWASP SQL Injection Prevention Cheat Sheet
  • If the application has a redirect feature ensure the target URLs are white-listed such that only redirection can happen to certain authorized domains only. If that is not possible than force all redirects to first go through a page notifying users that they are now going off from the current site, and have them click a link to confirm.

Avoid Sensitive Information Disclosure


The second best practice would be to avoid sensitive information disclosure in your application that can benefit an adversary. Some of the best practices in this regards would be:

  • Never hard-code sensitive information such as database or FTP connection credentials, cryptographic secrets, service secrets, API secrets etc in the source code. Instead use a centralized key management service for e.g. Amazon KMS to manage secrets.  
  • Ensure the application is not logging any type of sensitive information in the application logs such as personally identifiable information, authentication tokens, passwords, credit card data, bank account information etc
  • Remove debug statements or “turn off” debugging from the production code. 
  • Use a centralized error handling mechanism to handle unexpected error conditions. This will ensure that users are always redirected to a common error page without revealing too much information about the application’s underlying environment or technology stack.  

Use Secure Cryptographic Algorithms


The third piece of advice for developers would be to use secure cryptographic algorithms for the application’s encryption or hashing needs. Some of the best practices to consider here would be:


Use Secure Protocols to Communicate

This best practice is pretty straight forward. Use secure communication protocols such as HTTPS, FTPS when communicating with external web services, APIs or file servers. Essentially all the protocols internally use Transport Layer Security to secure the communication. So use OWASP TLS Protection Cheat Sheet to securely configure the protocol.