Application vulnerability is caused when a developer fails to sanitize the input from user and blindly uses it as an input for further data processing. One of the major parts of an application development is to validate user input data and pass it through proper sanitization and escaping.
What is Defense-in-Depth?
Defense-in-Depth is a security implementation which has layers of security implemented to protect an asset from unauthorized access or modification.
For example, a top secret document is stored in a high security building which has electronic fences on the perimeter. Motion sensors are on the ground and ID card entry is followed by biometric authentication. Key based entry and user-name password are required for accessing the document.
Every layer of security in the above scenario makes up defense in depth. If any of the layers fails to protect, then the next layer is in place to provide protection.
Implementing a single layer of input sanitization and validation will be a point of failure in case it gets bypassed or its core implementation is flawed. It is always a better approach to implement layered input validation and sanitization protection layers. This will be very helpful if a certain layer of protection is bypassed or exploited, then the further layers of protection can protect the underlying sensitive information and other details from being stolen or modified.
It should not be assumed from the above discussion that client-side validations are of no use. It is an important layer of protection and should always be implemented. This layer of protection can help in stopping some script kiddies from trying to exploit your application. But this layer of protection should be followed by some layer of protection at application level, which includes server side.
This is how our application protection structure looks like right now:
Server side protection includes server side languages like JavaEE, PHP, etc being used to validate user input and passing it through more validation and sanitization processes. This is the second layer of protection which ensures that the user input is not malicious. In case some alert is generated because the data contains malicious content, then it should be stopped from further processing and should be immediately discarded. The user should be informed about the request being dropped with an error message. The error message should not contain internal details like what malicious or attack vector was detected. It should be a simple error message like “Sorry, your request could not be processed”.
Not giving detailed error messages helps in giving an attacker the least information, which will be of no use to him/her. And it does not allow more application enumeration to be done.
Server-side protections at application level can be implemented using built-in security modules or data validation functions of the programming languages or by using third party libraries and frameworks. One such third party library is OWAS ESAPI, which is a library to implement an application interface to protect from application security vulnerabilities.
Built-in functions and security modules include those functions and modules that have been provided by the programming language itself for building a secure application along with ability to validate and sanitize data.
For example: Java allows user input to be converted into integers using Integer.parseInt(USER_INPUT). Assume that the user input had to be a number like 179. If this method returns an exception, then it proves that the user input was not a number, and further data processing can be stopped.
Another example is to use Prepared Statements or Parameterized SQL Queries when handling database related information like login, edit profile, etc. It is the safest method to handle user input and protect from vulnerabilities like SQL Injection.
SQL Injection is one of the most exploited vulnerabilities in web applications. It comes under the topmost vulnerability (A1-Injection) in the OWASP Top 10 vulnerabilities . This vulnerability allows an attacker to exploit SQL vulnerability caused by faulty input validation and data sanitization in the application at server side. This vulnerability can lead to database theft or system compromise.
It is very important to implement server side validation, data sanitization and protections before using it further for data processing.
After implementing server-side protections, this is what our application security structure looks like:
Now that we have implemented two layers of protections, we can move further with handling user input at the database level. One of the things that developers fail to implement is the proper structure of a database.
Imagine that a database has to be created which will contain user’s information. This information will contain:
|Field Name||Field Description|
|Username||String of length less than 20|
|Password||SHA256 hash of length 64|
|Security Question||String of length less than 30|
|Security Answer||String of length less than 30|
|Date-Time||Date-Time of user registration|
Now while creating this database structure, most developers for the sake of ease will make each field as of string type of any allowed length. Which is a big mistake at the developer’s end.
Imagine for some reason client-side and server-side protections are bypassed and an attacker can input any data to the application for being stored in the database. If the database structure allows all fields to be of string type, then this type of data is allowed for storing.
|Username||Asdba8wg8932&^(&||Username with not allowed special characters|
|Password||blank||Blank password instead of SHA256 hash|
|Security Question||My very big question is to store more than thirty characters in this field||String of length larger than 30 characters.|
|Security Answer||My very big question is to store more than thirty characters in this field||String of length larger than 30 characters.|
|Date-Time||gskdn*&^*&sd||String instead of a date-time.|
An attacker was able to store this data into the database just because the structure of database did not implement proper security to validate the user input.
The same database, if built with this structure would have been more robust and secure:
|Field Name||Field Description|
|Username||String of length less than 20 with only A-Z, a-z, 0-9 allowed and NULL value not allowed|
|Password||SHA256 hash of length 64 only and NULL value not allowed|
|Security Question||String of length less than 30 and NULL value not allowed|
|Security Answer||String of length less than 30 and NULL value not allowed|
|Date-Time||Date-Time table column type and NULL value not allowed|
So after implementing another layer of protection which is at the database level, this is what our application security structure looks like:
Another major concern is data security. While developing an application, it is assumed that all information gathered from the user should be stored in database. But it should not be the case. Only the information that is required and is important for future data processing should be stored. For data storage, it should be a major concern that sensitive user information is stored in encrypted or salted-hash format. This is so because if at any point of time database is compromised for one or the other reason, then user information remains partially safe because of encryption or hashed format of the data.
For example, storing password of a user’s account should never be in anything but a salted-hash format. The hashing algorithm should also be a strong algorithm. Many developers still use the MD5 hashing algorithm but it is no longer a strong hashing technique because of the MD5 Collision Attack . Instead of the MD5 hashing algorithm, SHA256 is a better hashing algorithm.
In the next part, we will see how we can implement a defense-in-depth approach on a live application with real world code used in enterprises.
Defense-in-Depth is a very important part of secure implementations. It allows many layers of protections implemented to protect an asset from attack and unauthorized access or modifications. It is a common misconception that implementing a very secure module is implementing many secure implementations. This is because in the former case if it failed for some reason, there was no way to protect the asset. Whereas in the latter case, if one layer of protection is broken, the next layer is there to protect the asset. Together, all layers of security prove to be a better security implementation and create more obstacles for an attacker to gain unauthorized access.