1. Introduction
This document summarizes the "Secure Coding Guidelines" that should be followed by WSO2 engineers
while engineering WSO2 products, as well as applications used within the organization.
The purpose of this document is to increase the security awareness and make sure the products and
the applications developed by WSO2 are inherently secure, by making sure security best practices
are followed throughout the Software Development Life Cycle.
1.1 Organization of the Document
"OWASP Top 10 - 2017 Prevention" section of the document categorizes different
attacks or security threats that engineers must focus on while engineering a product or an
application. Prevention techniques are discussed in generic form, and there are sections that
discuss programming language specific prevention techniques.
“OWASP Top 10 - 2013 Prevention” section of the document categorizes OWASP
Top 10 2013 list of the most critical application security risks. But OWASP has updated its top 10
list in 2017. Please refer to OWASP Top 10 2017 for updated list.
“OWASP Mobile Top 10 Prevention” section of the document categorizes
different attacks or security threats that engineers must focus on while engineering mobile
applications. Prevention techniques are discussed in generic form, and there are sections that
discuss mobile platform specific prevention techniques.
"General Recommendations for Secure Coding" section of the document discusses any security
threats that are not captured within OWASP Top 10 and several generic recommendations that
summarize prevention mechanisms which address multiple security threats. For example, the
"Cryptographic Algorithms" section discusses general recommendations on selecting cryptographic
algorithms, and sections such as "Security Related HTTP Headers" and "Securing Cookies", summarize
prevention techniques used across preventing multiple attacks.
"Tooling Recommendations for Secure Coding" section brings together all the documentations
relevant to security related tooling used within WSO2, and recommendations relevant to usage of
such tools in the engineering process.
1.2 Formatting of the Document
Formatting for "example incorrect usage":
Example Incorrect Usage:
Code block goes here. Any highlight should use this formatting.
Formatting for "example correct usage":
Example Correct Usage:
Code block goes here. Any highlight should use this formatting.
Formatting for documenting items that require Platform Security Team's approval:
Alert - Approval Required: If any component requires that,
[example security control should be skipped], the
use-case, as well as controls in place to provide required protection, must be reviewed and
approved by Platform Security Team, before proceeding with the release of such component.
Formatting for documenting sample code blocks or sample code outputs used in explanations (not
incorrect or correct usage samples, but general code or output used for explanations):
Code block or sample result of a program used in explanations.
Formatting for documenting example scenarios used in explanations:
Example: User input is the API description. The user might have
to add some special characters in the description. However, the application is not expecting
any HTML syntax in the description.
In the example scenario, output encoding will convert special characters to respective HTML
character entity references.
Formatting for referencing to other documents owned by WSO2.
WSO2 Document Reference: Further information on required
changes and recommended configuration for WSO2 products as well as production deployments are
available at "Example Document"[ref].
1.3 Revision History
Version
|
Release Date
|
Contributors / Authors
|
Summary of Changes
|
1.0
|
Jan 2016
|
Kasun Balasuriya, Dulanja
Liyanage, Prakhash
Sivakumar, Milinda Wickramasinghe, Tharindu Edirisinghe, Malithi Edirisinghe, Rasika Perera, Ayoma Wijethunga
|
Initial version of Secure Coding Guidelines
|
1.8
|
May 28, 2017
|
Ayoma
Wijethunga, Tharindu Edirisinghe
|
Initial version of Secure Coding Guidelines - 2nd Edition
|
1.8.1
|
Nov 13, 2017
|
Ayoma
Wijethunga
|
Minor modification to XML External Entity (XXE) prevention sample.
|
1.9
|
Nov 16, 2017
|
Kasun
Dharmadasa, Ayoma
Wijethunga, Charitha Goonetilleke
|
Adding initial version of Secure Coding Guidelines for Mobile Applications
|
2.0
|
Sep 4, 2018
|
Mathuriga
Thavarajah, Ayoma
Wijethunga
|
Updating OWASP Top 10 list to OWASP Top 10 Application Security Risks - 2017 list
|
2.1
|
Oct 22, 2018
|
Kasun
Dharmadasa
|
Adding the Zip Slip Vulnerability prevention
|
2. OWASP Top 10 - 2017 Prevention
2.1 A1 - Injection
2.1.1 SQL Injection
A SQL injection attack consists of insertion or "injection" of a SQL query via the input data from
the client to the application. A successful SQL injection exploit can read sensitive data from the
database, modify database data (Insert/Update/Delete), execute administration operations on the
database (such as shutdown the DBMS), recover the content of a given file present on the DBMS file
system and in some cases issue commands to the operating system. SQL injection
attacks are a type of injection attack, in which SQL commands are injected into data-plane input in
order to effect the execution of predefined SQL commands 1.
2.1.1.1 Prevention Techniques
Use secure language constructs (Example: PreparedStatement in Java) when executing SQL statements.
Use language specific best practices when using secure language constructs to avoid any possible
misuses.
Restructure the methods so that the application does not accept table names, column names, ordering
information, offset details or any other value that cannot be parameterized using language specific
best practices.
Example: In Java context, ORDER BY column name of below query
can not be provided as a PreparedStatement parameter.
SELECT ID,NAME FROM EXAMPLE_TABLE WHERE AGE > ? ORDER BY DYNAMIC_COLUMN_NAME;
If user input must be used in sections that cannot be parameterized using secure language
constructs, perform allowlisting against the user input.
Example: A allowlist should be maintained which includes an
acceptable list of inputs for "DYNAMIC_COLUMN_NAME". User input should be rejected if provided
input is not within expected set of values.
2.1.1.2 Java Specific Recommendations
Use PreparedStatements to prevent SQL injections. The statement will be compiled and the user
variables will be assigned to the query parameters in the runtime. Since user variables are being
set to a precompiled SQL statement, this approach avoids possibility of SQL injections.
Example Incorrect Usage:
String query = "SELECT account_balance FROM
user_data WHERE user_name = " + request.getParameter("customerName");
try {
Statement statement = connection.createStatement(
... );
ResultSet results = statement.executeQuery(
query );
}
//Catch block...
Example Correct Usage:
String customerName = request.getParameter("customerName");
//Validations. Etc...
String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
PreparedStatement preparedStatement = connection.prepareStatement(
query );
preparedStatement.setString( 1, customerName );
ResultSet results = preparedStatement.executeQuery();
}
//Catch block...
When processing dynamic query segments that cannot be set as PreparedStatement parameters (table
names, column names, ordering information, offset details), validate user input against a
allowlist. This approach avoids the risk of providing end user the ability to append anything
uncontrolled to the SQL query.
Example 1:
Example Incorrect Usage:
String customerType = request.getParameter("customerType");
String order = request.getParameter("order");
//Validations. Etc...
String query = "SELECT user_name, account_balance FROM user_data WHERE
user_type = ? ORDER BY " + order;
try {
PreparedStatement preparedStatement = connection.prepareStatement(
query );
preparedStatement.setString( 1, customerType );
ResultSet results = preparedStatement.executeQuery();
}
//Catch block...
Example Correct Usage:
//Class level static map maintaining "user input to database column"
mapping
private static final Map<String, String> validOrderColumns
= new HashMap<String, String>();
validOrderColumns.add("accountBalance", "account_balance");
validOrderColumns.add("userName", "user_name");
//Method definition...
String customerType = request.getParameter("customerType");
String orderColumn = request.getParameter("orderColumn");
String orderDirection = request.getParameter("orderDirection");
if (orderDirection.equalsIgnoreCase("DESC")) {
orderDirection = "DESC";
} else {
orderDirection = "ASC";
}
orderColumn = validOrderColumns.get(orderColumn);
//Validations. Etc...
String query = "SELECT user_name, account_balance FROM user_data WHERE
user_type = ? ORDER BY " + orderColumn + " " +
orderDirection;
try {
PreparedStatement preparedStatement = connection.prepareStatement(
query );
preparedStatement.setString( 1, customerType
);
ResultSet results = preparedStatement.executeQuery();
}
//Catch block...
Example 2:
Example Incorrect Usage:
//...
query = "SELECT * FROM " + columnFamily + " WHERE
" +
APIUsageStatisticsClientConstants.API_PUBLISHER
+ " = ?";
if (selectRowsByColumnName != null)
{
query = query + " AND " +
selectRowsByColumnName + " = ? ";
}
try {
PreparedStatement preparedStatement = connection.prepareStatement( query
);
preparedStatement.setString( 1, publisher );
if (selectRowsByColumnName != null) {
preparedStatement.setString( 2,
selectRowsByColumnValue );
}
ResultSet results = preparedStatement.executeQuery();
}
//Catch block
Example Correct Usage:
//Class level static map maintaining user input to column mapping
private static final Map<String, String> validSelectColumns
= new HashMap<String, String>();
validSelectColumns.add("apiName", "api_name");
validSelectColumns.add("apiId", "api_id");
private static final Map<String, String>
validColumnFamilies = new HashMap<String, String>();
validSelectColumns.add("commonApis", "COMMON_APIS");
validSelectColumns.add("corporateApis",
"CORPORATE_APIS");
//Method definition...
String columnFamily = validSelectColumns.get(columnFamilyUserInput)
if(columnFamily == null) {
//Throw required exceptions or errors.
return;
}
query = "SELECT * FROM " + columnFamily + " WHERE
" +
APIUsageStatisticsClientConstants.API_PUBLISHER
+ " = ?";
if (selectRowsByColumnName != null)
{
String columnName = validSelectColumns.get(selectRowsByColumnName);
if(columnName != null)
{
query = query + " AND " + columnName + " = ? ";
} else {
selectRowsByColumnName = null;
}
}
try {
PreparedStatement preparedStatement = connection.prepareStatement(
query );
preparedStatement.setString( 1, publisher );
if (selectRowsByColumnName != null) {
preparedStatement.setString( 2,
selectRowsByColumnValue );
}
ResultSet results = preparedStatement.executeQuery();
}
//Catch block...
2.1.1.3 PHP Specific Recommendations
Use PHP Data Objects (PDO) Extension or MySQL Improved Extension when executing queries that
include user inputs. Make sure to use parameterizing capabilities of the extensions.
Example Incorrect Usage:
<?php
$query = "SELECT id, name, inserted, size FROM
products WHERE size ='$size'";
$result = odbc_exec($conn,
$query);
?>
Example Incorrect Usage:
<?php
$query = "SELECT id, name, inserted, size FROM
products WHERE size = '$size'";
$result = pg_query($conn,
$query);
?>
Example Incorrect Usage:
<?php
$query = "SELECT id, name, inserted, size FROM
products WHERE size = '$size'";
$result = mssql_query($conn,
$query);
?>
Using PHP Data Objects (PDO) Extension2,3
Example Correct Usage:
<?php
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute(array('name' => $name));
foreach ($stmt as
$row) {
// do something with $row
}
?>
Using MySQL Improved Extension 4,3
Example Correct Usage:
<?php
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s',
$name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// do something with $row
}
?>
Example with query segments that cannot be parameterized with PDO or MySQL Improved Extension5:
Example Incorrect Usage:
<?php
$offset = $argv[0]; //
beware, no input validation!
$query = "SELECT id, name FROM products ORDER
BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
?>
Example Correct Usage:
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products ORDER
BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string, using %s would be
meaningless
$query = sprintf("SELECT
id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",$offset);
$result = pg_query($conn, $query);
?>
2.1.2 LDAP Injection
LDAP Injection is an attack used to exploit web based applications that construct LDAP statements
based on user input. When an application fails to properly sanitize user input,
it's possible to modify LDAP statements using a local proxy. This could result in the execution of
arbitrary commands such as granting permissions to unauthorized queries, and content modification
inside the LDAP tree. The same advanced exploitation techniques available in SQL Injection can be
similarly applied in LDAP Injection 6,7.
All user inputs that are getting directly appended to any LDAP queries should be filtered through
an encoding function that does proper encoding for LDAP8.
Example Incorrect Usage:
String grpSearchFilter = searchFilter.replace("?",role);
groupResults = searchInGroupBase(grpSearchFilter,...)
Example Correct Usage:
String grpSearchFilter = searchFilter.replace("?",
escapeSpecialCharactersForFilter(role));
groupResults = searchInGroupBase(grpSearchFilter,...)
2.1.3 OS Command Injection
Command injection is an attack in which the goal is execution of arbitrary commands on the host
operating system via a vulnerable application. Command injection attacks are possible when an
application passes unsafe user supplied data (forms, cookies, HTTP headers etc.) to a system shell.
In this attack, the attacker-supplied operating system commands are usually executed with the
privileges of the vulnerable application. Command injection attacks are possible largely due to
insufficient input validation9.
2.1.3.1 Prevention Techniques
Products or applications should not use user inputs in constructing OS commands.
Alert - Approval Required:
If any component requires that, user input to be appended to an OS command or to be interpreted
as OS command, the use-case, as well as controls in place to provide required protection, must
be reviewed and approved by Platform Security Team, before proceeding with the release of such
component.
2.1.3.2 Java Specific Recommendations
WSO2 Products should not use java.lang.Runtime.getRuntime(),
java.lang.ProcessBuilder or any
other method to execute OS commands, constructed based on user input.
Example Incorrect Usage:
java.lang.Runtime.getRuntime().exec(userInput);
Example Incorrect Usage:
java.lang.Runtime.getRuntime().exec("curl -v -k " + userInput);
Example Incorrect Usage:
java.lang.ProcessBuilder processBuilder =
new java.lang.ProcessBuilder("curl", "-v", "-k",
userInput);
2.1.3.3 PHP Specific Recommendations
PHP applications should not use "exec" function10, "WScript.Shell"11 or any other method to execute OS commands, constructed based on
user input.
Example Incorrect Usage:
<?php
echo exec(userInput);
?>
Example Incorrect Usage:
<?php
echo exec("curl
-k " . userInput);
?>
Example Incorrect Usage:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd " . userInput, 7, false);
?>
2.1.4 HTTP Response Splitting (CRLF Injection)
Including unvalidated data in an HTTP header allows an attacker to specify the entirety of the HTTP
response rendered by the browser. When an HTTP request contains unexpected CR (carriage return,
also given by %0d or \r) and LF (line feed, also given by %0a or \n) characters, the server may
respond with an output stream that is interpreted as two different HTTP responses (instead of one).
An attacker can control the second response and mount attacks such as cross-site scripting and
cache poisoning attacks16.
For example, if below snippet was used to set cookie value, malicious value "Wiley
Hacker\r\nContent-Length:45\r\n\r\nHTTP/1.1 200 OK\r\n...." can result in generating two
HTTP responses17.
String author = request.getParameter(AUTHOR_PARAM);
...
Cookie cookie = new Cookie("author",
author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);
HTTP/1.1 200 OK
Set-Cookie: author=Wiley Hacker
Content-Length:45
HTTP/1.1 200 OK
...
2.1.4.1 Prevention Techniques
With regards to WSO2 Carbon 4 based products, Apache Tomcat will handle this internally, by
disallowing "carriage return" and "line feed" characters from the header names or values18,19.
If HTTP response generation is done in any other component, or any other HTTP transport
implementation, it should be reviewed and confirmed to have a mechanism similar to Tomcat 618 and Tomcat 719 that performs
necessary filtering. If such mechanism is not present in transport implementation, a central filter
should be used to read all the headers and do the necessary sanitization before passing the
response to transport.
Sample filter implementation is available in WSO2 Carbon 4.4.x branch20.
However, this filter is not in use, since Tomcat provides the necessary protection.
Alert - Approval Required: If any transport implementation
or component that generates HTTP responses directly require usage of a custom written filter
that does the "carriage return" and "line feed" (CRLF) filtering, the logic performing
filtering should be reviewed and approved by Platform Security Team. Platform Security Team
should be informed and approval should be obtained before releasing such component or a
transport implementation.
2.1.5 Log Injection / Log Forging (CRLF Injection)
Including unvalidated data in log files allows an attacker to forge log entries or inject malicious
content into logs. When a log entry contains unexpected CR (carriage return, also given by %0d or
\r) and LF (line feed, also given by %0a or \n) characters, the server may record them in the log
files/monitoring systems as different events. This can be used to forge log entries and might
result in business level implications, if logs are used for further actions, such as reconciliation21.
For example, if below snippet was used to print a log entry, malicious input "example1\r\n$100
payment made to author: example2" can result in generating two log lines.
String author = request.getParameter(AUTHOR_PARAM);
...
log.info("$100 payment made to author: " + author);
$100 payment made to author: example1
$100 payment made to author: example2
2.1.5.1 Prevention Techniques
In WSO2 Carbon 4 (4.4.3 onwards), log appenders can be configured to append a UUID to each log
entry. UUID will be randomly generated during server startup and the UUID regeneration time can be
configured if such behavior is required. Therefore, even if a log line was forged, it would be
possible to isolate the forged entry, since it will not contain the relevant valid UUID.
UUID in logs can be enabled by adding %K in log4j pattern and relevant configuration is further
documented in Administration Guide22.
2.2 A2 - Broken Authentication
2.2.1 Session Hijacking
The Session Hijacking attack consists of the exploitation of the web session control mechanism,
which is normally managed with a session token.
Because HTTP communication uses different TCP connections, the web server needs a method to
recognize every user's connections. The most useful method depends on a token that the Web Server
sends to the client browser after a successful client authentication. A session token is normally
composed of a string of variable width and it could be used in different ways, like in the URL, in
the header of the HTTP requisition as a cookie, in other parts of the header of the HTTP request,
or yet in the body of the HTTP request. (In WSO2 products, session ID is maintained as a cookie and
will be communicated between browser and the server through HTTP headers).
The Session Hijacking attack compromises the session token by stealing or predicting a valid
session token to gain unauthorized access to the Web Server23.
The session token could be compromised in different ways; the most common methods controllable by
application developer are:
- Session Sniffing
- Eavesdropping attack
- Predictable session token
- Client-side attacks (XSS, malicious JavaScript Codes, Trojans, etc)
2.2.1.1 Prevention Techniques
Preventing session sniffing and eavesdropping attacks
HTTP Strict-Transport-Security Header (HSTS)
HTTP Strict Transport Security (HSTS) is an opt-in security enhancement that is specified by a web
application through the use of a special response header. Once a supported browser receives this
header, that browser will prevent any communications from being sent over HTTP, to the specified
domain and will instead send all communications over HTTPS24.
HSTS header prevents sensitive information exposure over unencrypted channels by avoiding SSLStrip
attacks25 and by preventing a victim from using HTTP links that
correspond to HTTPS-protected site, which could lead to session cookie exposure, if not configured
properly.
HTTP Public-Key-Pins Header (HPKP)
To ensure the authenticity of a server's public key used in TLS sessions, this public key is
wrapped into an X.509 certificate which is usually signed by a certificate authority (CA). Web
clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary
domain names. If an attacker is able to compromise a single CA, they can perform MITM attacks on
various TLS connections. HPKP can circumvent this threat for the HTTPS protocol by telling the
client which public key belongs to a certain web server26.
HPKP is a Trust on First Use (TOFU) technique. The first time a web server tells a client via a
special HTTP header which public keys belong to it, the client stores this information for a given
period of time. When the client visits the server again, it expects at least one certificate in the
certificate chain to contain a public key whose fingerprint is already known via HPKP. If the
server delivers an unknown public key, the client should present a warning to the user26.
In addition, HPKP header prevents attack patterns such as SSLSplit27
attack.
Avoiding predictable session token
Please refer to documentation section "Session Prediction".
Preventing client-side attacks
Please refer to documentation section "Cross-Site Scripting (XSS)".
Cookie security
Cookie attributes should be set properly, in order to prevent session related cookies from getting
exposed over unencrypted channels. Please refer to documentation section "Securing
Cookie".
2.2.2 Session Fixation
Session Fixation is an attack that permits an attacker to hijack a valid user session (a type of a
Session Hijacking attack). The attack explores a limitation in the way the web application manages
the session ID, more specifically the vulnerable web application. When authenticating a user, it
doesn't assign a new session ID, making it possible to use an existing session ID. The attack
consists of obtaining a valid session ID (e.g. by connecting to the application), inducing a user
to authenticate himself with that session ID, and then hijacking the user-validated session by the
knowledge of the used session ID. The attacker has to provide a legitimate Web application session
ID and try to make the victim's browser use it28.
2.2.2.1 Prevention Techniques
User Login Flow
When a user is successfully authenticated, a new session should be initiated and the guest session
that was maintained to access the login sequence should be invalidated. This approach makes sure
that an attacker is unable to use a compromised guest session token to gain access to an active
user session.
User Logout Flow
When a user logs out of the system, any existing session should be invalidated so that an attacker
is unable to login back into the system by re-submitting the previous session token.
2.2.2.2 Java Specific Recommendations
User Login Flow
Invalidate the HttpSession and create a new session after authentication is complete, and before
setting any values to the user session.
Example Incorrect Usage:
boolean validLogin = login(username, password);
if(validLogin) {
session.setAttribute(Constants.IS_AUTHENTICATED,
Constants.AUTHENTICATED);
session.setAttribute(Constants.AUTHENTICATED_USER,
username);
}
Example Correct Usage:
booleanspan validLogin = login(username, password);
if(validLogin) {
session.invalidate();
session = request.getSession();
session.setAttribute(Constants.IS_AUTHENTICATED,
Constants.AUTHENTICATED);
session.setAttribute(Constants.AUTHENTICATED_USER,
username);
}
User Logout Flow
Invalidate the HttpSession, rather than just removing user specific attributes.
Example Incorrect Usage:
//... logout sequence
session.removeAttribute(Constants.IS_AUTHENTICATED);
session.removeAttribute(Constants.AUTHENTICATED_USER);
Example Correct Usage:
//... logout sequence
session.invalidate();
Other Best Practices
Do not use HttpServletResponse#encodeRedirectURL() or HttpServletResponse#encodeURL, in any
operation, since it will append the session ID to the resulting URL. Since WSO2 products are
dependent on cookies, there is no functional impact of not running URLs through these methods.
Example Incorrect Usage:
String redirectUrl = response.encodeRedirectURL(request.getContextPath()
+
Constant.STORE_FRONT_URL);
response.sendRedirect(redirectUrl);
Example Correct Usage:
String redirectUrl = request.getContextPath() + Constant.STORE_FRONT_URL;
response.sendRedirect(redirectUrl);
2.2.2.3 PHP Specific Recommendations
User Login Flow
Use session_regenerate_id function to renew the session after successful authentication.
Example Incorrect Usage:
<?php
$validLogin = login($username, $password);
if(validLogin) {
$_SESSION[Constants::IS_AUTHENTICATE]
= Constants::AUTHENTICATED;
$_SESSION[Constants::AUTHENTICATED_USER]
= $username;
}
?>
Example Correct Usage:
<?php
boolean validLogin = login($username, $password);
if(validLogin) {
if(session_regenerate_id(true)) {
$_SESSION[Constants::IS_AUTHENTICATE]
= Constants::AUTHENTICATED;
$_SESSION[Constants::AUTHENTICATED_USER]
= $username;
} else {
//...
}
}
?>
User Logout Flow
Destroy current session using session_destroy function, rather than just removing user specific
attributes.
Example Incorrect Usage:
<?php
//... logout sequence
unset($_SESSION[Constants::IS_AUTHENTICATED]);
unset($_SESSION[Constants::AUTHENTICATED_USER]);
?>
Example Correct Usage:
<?php
//... logout sequence
session_destroy();
?>
2.2.3 Session Prediction
Session prediction attack focuses on predicting session ID values that permit an attacker to bypass
the authentication schema of an application. By analyzing and understanding the session ID
generation process, an attacker can predict a valid session ID value and get access to the
application.
In the first step, the attacker needs to collect some valid session ID values that are used to
identify authenticated users. Then, they must understand the structure of session ID, the information
that is used to create it, and the encryption or hash algorithm used by application to protect it.
Some bad implementations use sessions IDs composed by username or other predictable information,
like timestamp or client IP address. In the worst case, this information is used in the clear text
or coded using some weak algorithm like base64 encoding.
In addition, the attacker can implement a brute force technique to generate and test different
values of session ID until they successfully gets access to the application35.
2.2.3.1 Prevention Techniques
Using a longer random number or string as the session key will reduce the risk that an attacker
could simply guess a valid session key through trial and error or brute force attacks. Session
identifiers should be at least 128 bits long to prevent brute-force session guessing attacks,
according to OWASP recommendations30.
2.2.3.2 Java Specific Recommendations
WSO2 Carbon 4 based products can use the Apache Tomcat context.xml file to configure the session ID
generation. Default session ID length is 16 bytes, which is exactly 128 bit. Therefore, WSO2 Carbon
4 based products match with current recommendation. However, if the recommendations change and if
any product needs to increase the session ID length, it is possible to use "SessionIdGenerator"
element of context.xml file to make required changes as documented in Tomcat 7 "SessionIdGenerator
Component" documentation31.
2.2.3.3 PHP Specific Recommendations
Below minimum session configuration should be used with PHP applications32
:
session.hash_function = 1
session.hash_bits_per_character = 6
Full set of recommended session configuration is:
session.auto_start = Off
#session.save_path = /path/PHP-session/
#session.name = CUSTOMSESSID
session.hash_function = 1
session.hash_bits_per_character = 6
session.use_trans_sid = 0
session.cookie_domain = full.qualified.domain.name
#session.cookie_path = /application/path/
session.cookie_lifetime = 0
session.cookie_secure = On
session.cookie_httponly = 1
session.use_only_cookies = 1
session.cache_expire = 30
2.3 A3 - Sensitive Data Exposure
2.3.1 Heap Inspection Attacks
When sensitive data such as a password or an encryption key is not removed from memory, it could be
exposed to an attacker using a "heap inspection" attack that reads the sensitive data using memory
dumps or other methods45.
2.3.1.1 Prevention Techniques
It is recommended to store sensitive information such as user passwords in mutable data types or
data structures. Once usage is complete, it is essential to clear the memory space and make sure
none of the store data is present in the memory.
2.3.1.2 Java Specific Recommendations
It would seem logical to collect and store the password in an object of type java.lang.String.
However, here's the caveat: Objects of type String are immutable, i.e., there are no methods
defined that allow you to change (overwrite) or zero out the contents of a String after usage.
Even if String object is no longer referenced and available for garbage collection, the object can
stay in the memory for multiple garbage collection cycles, allowing a heap inspection attack to
capture relevant values.
This feature makes String objects unsuitable for storing security-sensitive information such as
user passwords. It is essential to always collect and store security-sensitive information in a
char array or a mutable data type instead46.
Once operations that require password are executed, it is required to clear the values from the
array elements, before moving the variable out of scope.
Example Incorrect Usage:
String userPassword = getUserPassword(request);
Example Incorrect Usage:
//...
char[] userPassword = getUserPassword(request);
boolean valid = validateUser(username, userPassword);
if(valid) {
//Do valid actions
}
return valid;
//...
Example Correct Usage:
private static final List
SENSITIVE_PARAMETER_NAME_LIST = new ArrayList();
//...
Map<String, Object> parameterMap = SecurityUtil.getSensitiveDataMap(request,
SENSITIVE_PARAMETER_NAME_LIST);
char[] userPassword = (char[])
parameterMap.get("password");
String otherNonSensitiveData = (String) parameterMap.get("otherNonSensitiveData");
boolean valid = validateUser(username,
userPassword);
if(valid) {
//Do valid actions
}
SecurityUtil.clearSensitiveDataMap(userPassword);
return valid;
//...
public static void
clear(char[] arr) {
for(int
i = 0; i < arr.length; i++) {
arr[0] = '';
}
}
2.3.2 Privacy Violation - Password Auto Complete
Browsers will sometimes ask a user if they wish to remember the password that they just entered.
The browser will then store the password, and automatically enter it whenever the same
authentication form is visited. This is a convenience for the user47.
Having the browser store passwords is not only a convenience for end-users, but also for an
attacker. If an attacker can gain access to the victim's browser (e.g. through a Cross Site
Scripting attack, or through a shared computer), then they can retrieve the stored passwords. It is
not uncommon for browsers to store these passwords in an easily retrievable manner, but even if the
browsers were to store the passwords encrypted and only retrievable through the use of a master
password, an attacker could retrieve the password by visiting the target web application's
authentication form, entering the victim's username, and letting the browser to enter the password47.
Due to associated security risks relevant to password auto complete, it is recommended to turn
autocomplete off, for sensitive text fields. This includes password inputs.
Example Incorrect Usage:
<input type="password" name="password" />
Example Correct Usage: 42
<input type="password" name="password" autocomplete="off"/>
In order to support password managers and increase usability features of browsers, some modern
browsers do not honor autocomplete attribute. However, regardless of browser's behavior, it is
advised to turn autocomplete off, to address security compliance related issues.
2.4 A4 - XML External Entity (XXE)
An XML External Entity attack is a type of attack against an application that parses XML input. This
attack occurs when XML input containing a reference to an external entity is processed by a weakly
configured XML parser. This attack may lead to the disclosure of confidential data, denial of
service, server-side request forgery, port scanning from the perspective of the machine where the
parser is located, and other system impacts12.
2.4.1.1 Java Specific Recommendations
In order to resolve this issue, it is required to configure the XML parser correctly performing
following actions.
- Enable the DocumentBuilderFactory namespace awareness
- Disable the DocumentBuilderFactory entity reference expansion
- Enable the DocumentBuilderFactory secure processing feature
- Disable the DocumentBuilderFactory "http://xml.org/sax/features/external-general-entities"
feature
- Use a SecurityManager for the DocumentBuilderFactory
- Use a custom EntityResolver for the DocumentBuilder created using above DocumentBuilderFactory
Alert - Approval Required: If any component requires, any
of the recommended security flags not to be set in DocumentBuilderFactory, the use-case, as
well as controls in place to provide required protection, must be reviewed and approved by
Platform Security Team, before proceeding with the release of such component.
DocumentBuilderFactory (DOM Parser)
Example Incorrect Usage:
DocumentBuilder builder;
ByteArrayInputStream inputStream;
Element root = null;
// xmlConfig is the XML content
inputStream = new ByteArrayInputStream(xmlConfig.getBytes());
DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
try {
Document doc = builder.newDocumentBuilder().parse(inputStream);
root = doc.getDocumentElement();
//doc is used for further processing from here
}
Example Correct Usage:
import org.apache.xerces.impl.Constants;
private static final int
ENTITY_EXPANSION_LIMIT = 0;
private static final DocumentBuilderFactory
documentBuilderFactory =
DocumentBuilderFactory.newInstance();
static {
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
try {
documentBuilderFactory.setFeature(Constants.SAX_FEATURE_PREFIX +
Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE,
false);
} catch (ParserConfigurationException
e) {
logger.error("Failed to
load XML Processor Feature " +
Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE);
}
try {
documentBuilderFactory.setFeature(Constants.SAX_FEATURE_PREFIX +
Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE,
false);
} catch (ParserConfigurationException
e) {
logger.error("Failed to
load XML Processor Feature " +
Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE);
}
try {
documentBuilderFactory.setFeature(Constants.XERCES_FEATURE_PREFIX +
Constants.LOAD_EXTERNAL_DTD_FEATURE, false);
} catch (ParserConfigurationException
e) {
logger.error("Failed to
load XML Processor Feature " +
Constants.LOAD_EXTERNAL_DTD_FEATURE);
}
SecurityManager securityManager = new
SecurityManager();
securityManager.setEntityExpansionLimit(ENTITY_EXPANSION_LIMIT);
documentBuilderFactory.setAttribute(Constants.XERCES_PROPERTY_PREFIX +
Constants.SECURITY_MANAGER_PROPERTY,
securityManager);
}
private DocumentBuilderFactory getSecuredDocumentBuilderFactory()
{
return documentBuilderFactory;
}
XMLInputFactory (Stax Parser)
Example Incorrect Usage:
DocumentBuilder builder;
ByteArrayInputStream inputStream;
Element root = null;
// xmlConfig is the XML content
inputStream = new ByteArrayInputStream(xmlConfig.getBytes());
XMLInputFactory factory = XMLInputFactory.newInstance();
try {
XMLEventReader eventReader = factory.createXMLEventReader(inputStream);
while(eventReader.hasNext()) {
//doc is used for further processing from here
}
}
Example Correct Usage:
import org.apache.xerces.impl.Constants;
private static final int
ENTITY_EXPANSION_LIMIT = 0;
private static final XMLInputFactory xmlInputFactory;
static {
xmlInputFactory = XMLInputFactory.newInstance();
try {
xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
} catch (IllegalArgumentException e) {
log.error("Failed to load
XML Processor Feature XMLInputFactory.IS_NAMESPACE_AWARE",
e);
}
try {
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
} catch (IllegalArgumentException e) {
log.error("Failed to load
XML Processor Feature XMLInputFactory.SUPPORT_DTD", e);
}
try {
xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
} catch (IllegalArgumentException e) {
log.error("Failed to load
XML Processor Feature
XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES",
e);
}
//If Woodstox StAX parser 5+ is used, set following
property
//to disable entity expansion
//try {
// factory.setProperty("com.ctc.wstx.maxEntityDepth", 1);
//} catch (IllegalArgumentException e) {
// log.error("Failed to load XML Processor Feature
com.ctc.wstx.maxEntityDept", e);
//}
}
private XMLInputFactory getSecuredXMLInputFactory()
{
return factory;
}
2.4.1.2 PHP Specific Recommendations
If libxml is used in XML processing, libxml_disable_entity_loader function13
must be used to protection against XXE attacks14,15.
Alert - Approval Required: If any component requires, any
of the recommended security flags not to be set in libxml, the use-case, as well as controls in
place to provide required protection, must be reviewed and approved by Platform Security Team,
before proceeding with the release of such component.
Example Incorrect Usage:
<?php
//User provided XML content here (Hard-coded only as an example).
$xml = <<<EOD
<?xml version="1.0"?>
<!DOCTYPE root
[
<!ENTITY foo SYSTEM "file://$dir/content.txt">
]>
<test><testing>&foo;</testing></test>
EOD;
$doc = simplexml_load_string($xml);
?>
Example Correct Usage:
<?php
//User provided XML content here (Hard-coded only as an example).
$xml = <<<EOD
<?xml version="1.0"?>
<!DOCTYPE root
[
<!ENTITY foo SYSTEM "file://$dir/content.txt">
]>
<test><testing>&foo;</testing></test>
EOD;
$oldValue = libxml_disable_entity_loader(true);
$doc = simplexml_load_string($xml);
libxml_disable_entity_loader($oldValue);
?>
Example Correct Usage:
<?php
//User provided XML content here (Hard-coded only as an example).
$xml = <<<EOD
<?xml version="1.0"?>
<!DOCTYPE root
[
<!ENTITY foo SYSTEM "file://$dir/content.txt">
]>
<test<testing>&foo;</testing></test>
EOD;
$oldValue = libxml_disable_entity_loader(true);
$dom = new DOMDocument;
$dom->loadXML($xml);
libxml_disable_entity_loader($oldValue);
?>
2.5 A5 - 2017 – Broken Access Control
2.5.1 Insecure Direct Object References
2.5.1.1 Path Traversal
A path traversal attack (also known as directory traversal) aims to access files and directories
that are stored outside the web root folder. By manipulating variables that reference files with
"dot-dot-slash (../)" sequences and its variations or by using absolute file paths, it may be
possible to access arbitrary files and directories stored on file system including application
source code or configuration and critical system files40.
Prevention Techniques
Absolute paths should not be accepted from the end-user during any operation, apart from
administrative configurations.
Alert - Approval Required: If any component requires that
an absolute path must be
accepted from the end-user (not in administrative configuration), the use-case, as well as
recommended security manager rules that are in place
to provide required protection, must be reviewed and approved by Platform Security Team, before
proceeding with the release of such component.
The general recommendation is to avoid accepting paths or path fragments (used to build absolute or
relative paths) from the end-user.
However, if any such requirement arises, it is necessary to follow language specific
recommendations for validating the accepted path fragment.
Java Specific Recommendations
Normalize41 the final path constructed and check if the normalized
path is within the expected boundary. The purpose of this validation is to check if any malicious
user has used ".." or other traversal techniques to move out of the expected base directory the
action should be performed on.
Example Incorrect Usage:
String userDirectory = request.getParameter("userDirectory");
File file = new File(Constants.USER_HOME_BASE
+ File.separator + userDirectory +
File.separator + Constants.LOG_FILE_NAME);
Example Correct Usage: 42
//...
String userDirectory = request.getParameter("userDirectory");
Path resolvedPath = null;
try {
resolvedPath = SecurityUtil.resolvePath(Constants.USER_HOME_BASE,
userDirectory + File.separator + Constants.LOG_FILE_NAME);
} catch (IllegalArgumentException e) {
//Exception handling and informing end-user in the errors
return;
}
File file = new File(resolvedPath.toString());
//...
public class SecurityUtil {
//...
/**
* Resolves an untrusted user-specified path against the API's base directory.
* Paths that try to escape the base directory are rejected.
*
* @param baseDirPath the absolute path of the base directory that all
user-specified paths should be within
* @param userPath the untrusted path provided by the API user, expected to be
relative to {@code baseDirPath}
*/
public static Path resolvePath(final Path baseDirPath, final
Path userPath) {
if (!baseDirPath.isAbsolute())
{
throw new IllegalArgumentException("Base path must be absolute")
}
if (userPath.isAbsolute())
throw new IllegalArgumentException("User path must be relative");
}
// Join the two paths together, then normalize so that any
".." elements
// in the userPath can remove parts of baseDirPath.
// (e.g. "/foo/bar/baz" + "../attack" -> "/foo/bar/attack")
final Path resolvedPath = baseDirPath.resolve(userPath).normalize();
// Make sure the resulting path is still within the
required directory.
// (In the example above, "/foo/bar/attack" is not.)
if (!resolvedPath.startsWith(baseDirPath))
{
throw new IllegalArgumentException("User path escapes the base path");
}
return resolvedPath;
}
}
PHP Specific Recommendations
Normalize the final path constructed and check if the normalized path is within the expected
boundary. Purpose of this validation is to check if any malicious user has used ".." or other
traversal techniques to move out of the expected base directory the action should be performed on.
Example Correct Usage: 43
<?php
$basepath = USER_HOME_BASE;
$realBase = realpath($basepath);
$userDirectory = $basepath .
DIRECTORY_SEPARATOR . $_GET['userDirectory'];
$userDirectoryRealPath = realpath($userDirectory);
if ($userDirectoryRealPath === false || strpos($userDirectoryRealPath,
$realBase) !== 0) {
//Directory Traversal. Informing end-user in the errors.
} else {
//No Directory Traversal. Proceed with usual operations
based on userDirectoryRealPath.
}
?>
2.5.2 Missing Function Level Access Control
If the authentication check in sensitive request handlers is insufficient or non-existent, the
vulnerability can be categorized as Missing Function Level Access Control48.
2.5.2.1 Prevention Techniques
- Make sure authentication checks are present for any restricted URLs.
- Make sure user authorization (role-permission) is checked before allowing a user to execute any
function. It is essential to make sure, authorization checks are done, before processing any
state-changing operation.
- "login" permission should not be granted for any user that does not require access to Carbon
Management Console (Carbon 4). (Example: API Manager - Store - Self-registered user).
- The enforcement mechanism(s) should deny all access by default, requiring explicit grants to
specific roles for access to every function.
- If the function is involved in a workflow, check to make sure the conditions are in the proper
state to allow access.
- Make sure audit logs are maintained properly and authentication and authorization related
changes are logged in audit logs.
2.6 A6 - 2017 - Security Misconfiguration
This section is not within the scope of "Secure Coding Guidelines". However, necessary prevention
techniques are summarized for information.
If a component is susceptible to attack due to an insecure configuration it would classify as
security misconfiguration44.
2.6.1.1 Prevention Techniques
Product Documentation
Product documentation should explain or reference to steps to be executed in order to perform
required security hardening and production deployment guidelines should include security-related
instructions.
All the default credentials, default certificates and security sensitive information such as ports
being opened should be properly documented.
Deployments
All WSO2 deployments should follow security hardening guidelines and production deployment
guidelines in security sensitive environments.
Production and staging environments should be configured identically. Configuration, as
well as artifact deployment between these environments, should be automated and should
follow a proper change management process to make sure no configuration changes are done in
production environments without proper security evaluation.
External components of the deployment such as the operation system and runtime environment
should be updated regularly and must be updated when relevant "security updates" are
released.
Default credentials, default certificates, and any security sensitive default values should
not be used in production environments (as recommended in security hardening guidelines and
production deployment guidelines).
2.7 A7 - 2017 - Cross-Site Scripting (XSS)
Cross-Site Scripting allows an attacker to execute malicious code (scripts) against the web
browser of the user. By leveraging this attack, an attacker could attempt to carry-out
other forms of attacks such as stealing session cookies to perform Session
Hijacking or stealing token values to conduct CSRF Attacks,
launch a phishing attack etc33.
2.7.1.1 Prevention Techniques
There are three recommended best practices that need to be followed in order to prevent XSS
threats:
Input validation
User inputs should be validated whenever possible and only characters that are relevant to
the particular field should be accepted. Input validation should be done on the server-side
to prevent any bypass attempts, even though additional front-end validation can be done to
increase user experience.
However, in WSO2 Carbon 4 based products, input validation is given a lower priority and is
only done in essential screens. Therefore, it is a must to use proper output encoding and
output sanitization techniques.
Output Encoding
Proper output encoding should be applied, where output consists of user input that is not
expected to include HTML content.
Example:
User input is the API description. The user might have to add some special characters
in the description. However, the application is not expecting any HTML syntax in the
description.
In the example scenario, output encoding will convert special characters to respective
HTML character entity references34.
Output Sanitization
Proper output sanitization should be applied, where output consists of user input that is
expected to include HTML content.
Example:
User input is the contents of API documentation, which is expected to have some basic
HTML syntax used to in formatting.
In the example scenario, output sanitization will disallow the user from adding HTML
tags that could introduce a potential threat such as <script> and from using HTML
event related attributes.
Browser Level Protection
Modern browsers have built-in XSS prevention mechanisms. However, certain browsers require
explicitly enabling these mechanisms using special response headers. "X-XSS-Protection: 1;
mode=block" header should be set in HTTP responses to make sure browser level protection is
enabled in all supported browsers.
For additional details on HTTP security related headers, refer to "Security
Related HTTP Headers" section.
However, this is not a permanent or justifiable solution for XSS. Instead, browser level
protection should only be considered as an additional protection mechanism.
2.7.1.2 Java Specific Recommendations
Output Encoding
OWASP Java Encoder35 should be used to perform output
encoding. OWASP Java Encoder is capable of performing contextual encoding. Following are
the contexts and relevant examples that should be used:
Encode within HTML content
Example Incorrect Usage:
<tr>
<td width="30%"><fmt:message key='workflow.impl.name'/></td>
<td><%=variableWithDynamicText%></td>
</tr>
Example Correct Usage:
<tr>
<td width="30%"><fmt:message key='workflow.impl.name'/></td>
<td>
<%=Encode.forHtml(variableWithDynamicText)%></td>
</tr>
Encode Attributes used in JavaScript
Example Incorrect Usage:
<a title="Title" onclick="jsMethod('
<%=variableWithDynamicText%>')">Link Text</a>
Example Correct Usage:
<a title="Title" onclick="jsMethod('<%=Encode.forJavaScript(variableWithDynamicText)%>')">Link Text</a>
Encode Variables in Javascript Blocks
Example Incorrect Usage:
<script type="text/javascript">
//Some JS logic here
vat x = <%=
variableWithDynamicText %> ;
//Some JS logic here
</script>
Example Correct Usage:
<script type="text/javascript">
//Some JS logic here
vat x = <%=
Encode.forJavaScript(variableWithDynamicText)%> ;
//Some JS logic here
</script>
Encode with URLs
Example Incorrect Usage:
<a title="Title" href="<%=variableWithDynamicText%>">Link
Text</a>
Example Correct Usage:
<a title="Title" href="<%=Encode.forUri(variableWithDynamicText)%>">Link
Text</a>
Output Sanitization
OWASP Java HTML Sanitizer36 should be used to perform output
sanitization. OWASP Java HTML Sanitizer can be used to allow only certain set of HTML
elements in a user input. In addition, it is possible to define allowed attributes and
properties of attributes, relevant to an enabled element.
It is recommended to limit to pre-packaged FORMATTING sanitizer policy37.
However, if user should be allowed to use images, tables or links, other pre-packaged
sanitizer policies can be used.
Alert - Approval Required: If a custom policy or a
custom rule is defined other than the pre-packaged sanitizer policies, custom policy
should be reviewed and approved by Platform Security Team. Therefore, before the
release of component with custom sanitizer policies or rules, Platform Security Team
should be notified, and use case, as well as rules, should be reviewed.
Example Incorrect Usage:
<tr>
<td width="30%"><fmt:message key='workflow.impl.name'/></td>
<td><%=variableWithDynamicHtmlContent%></td>
</tr>
Example Correct Usage:
<tr>
<td width="30%"><fmt:message
key='workflow.impl.name'/></td>
<%
PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS); %>
<td><%=policy.sanitize(variableWithDynamicHtmlContent)%></td>
</tr>
Browser Level Protection
"org.apache.catalina.filters.HttpHeaderSecurityFilter" Servlet Filter should be used to add
X-XSS-Protection header to the HTTP response.
2.7.1.3 JavaScript Specific Recommendations
Output Encoding
When adding content into HTML document using client-side script (such as JavaScript), avoid
inserting HTML content directly into the document. Avoid .html(), .innerHTML, and other
related functions.
Example Incorrect Usage:
document.getElementById('example-div').innerHtml
= dynamicText;
Example Incorrect Usage:
jQuery( '#example-div' ).html( dynamicText );
jQuery( '#example-div' ).before( dynamicText );
jQuery( '#example-div' ).after( dynamicText );
jQuery( '#example-div' ).append( dynamicText );
jQuery( '#example-div' ).prepend( dynamicText );
jQuery( dynamicText ).appendTo( '#example-div' );
jQuery( dynamicText ).prependTo( '#example-div' );
Use methods such as .text(), .textContent, for adding dynamic text content into DOM. When
adding HTML content is required, programmatically create DOM nodes and append content to
the newly created DOM and append the new DOM node into HTML document.
Example Correct Usage:
document.getElementById('example-div').textContent
= dynamicText;
Example Correct Usage:
jQuery( '.some-div' ).text( dynamicText );
Example Correct Usage:
var text = jQuery('<div />').text(
dynamicText );
jQuery( '#example-div' ).html( text.html() );
If dynamic input is used to construct a HTML attribute double quote and single quote should
be additionally escaped.
Example Incorrect Usage:
var text = jQuery('<div />').text(
dynamicText );
jQuery( '#example-div' ).attr("href", text.html());
Example Correct Usage:
var text = jQuery('<div />').text(
dynamicText );
jQuery( '#example-div' ).attr("href",
text.html().replace(/"/g, """).replace(/'/g, ''') );
2.7.1.4 PHP Specific Recommendations
Output Encoding
"htmlspecialchars" function38 should be used whenever user
input is printed back into a response. Mentioned function converts special characters in
the input to HTML entities.
Use ENT_QUOTES flag to enable encoding both single and double quotes.
Example Incorrect Usage:
<a title="Title" href="<?php
echo variableWithDynamicText ?>">Link Text</a>
Example Incorrect Usage:
<a title="Title" href="<?php
echo htmlspecialchars(variableWithDynamicText)?>">Link Text</a>
Example Correct Usage:
<a title="Title" href="<?php echo
htmlspecialchars(variableWithDynamicText, ENT_QUOTES)?>">Link Text</a>
Output Sanitization
HTML Purifier39 should be used to sanitize HTML content.
Default HTML Purifier rules set is recommended.
Alert - Approval Required: If a custom policy or a
custom rule is defined, other than the default sanitizer policy, custom policy should
be reviewed and approved by Platform Security Team. Therefore, before the release of
component with custom sanitizer policies or rules, Platform Security Team should be
notified, and use case, as well as rules, should be reviewed.
Example Incorrect Usage:
<a title="Title" href="<?php
echo htmlspecialchars(variableWithDynamicText)?>">Link Text</a>
Example Correct Usage:
<?php
$config = HTMLPurifier_Config::createDefault();
$purifier = new
HTMLPurifier($config);
?>
<a title="Title" href="<?php echo
$purifier->purify(variableWithDynamicText,
ENT_QUOTES)?>">Link Text</a>
Browser Level Protection
2.8 A8 - 2017 - Insecure Deserialization
Deserialization is the process of creating an object from binary data or text data. It is an
opposite process of serialization. When serialized data is in control of an attacker,
insecure deserialization flaws can enable an attacker to cause remote code execution upon
deserialization or create a malicious deserialized objects that can cause remote code
execution and date tampering upon usage119.
2.8.1.1 Prevention Techniques
Use language specific guidelines
Use language-specific guidelines to enumerate safe methodologies for deserializing data that
can't be trusted.
Additional prevention techniques120
- Implementing integrity checks such as digital signatures on any serialized objects to
prevent hostile object creation or data tampering.
- Log deserialization exceptions and failures, such as where the incoming type is not the
expected type, or the deserialization throws exceptions.
2.8.1.2 PHP Specific Recommendations
Check the use of 'unserialize()' and review how the external parameters are accepted.Use a
safe, standard data interchange format such as JSON (via json_decode() and json_encode())
if you need to pass serialized data to the user. If you need to deserialize
externally-stored data, consider using hash hmac() for data validation. Make sure data is
not modified by anyone but you121.
2.8.1.3 JAVA Specific Recommendations
Secure java.io.ObjectInputStream
The technique is overriding the ObjectInputStream#resolveClass() method to prevent arbitrary
classes from being deserialized. This safe behavior can be wrapped by using SerialKiller.
SerialKiller inspects Java classes during naming resolution and allows a combination of
denylisting or allowlisting to secure the application120.
Example Usual Usage:
ObjectInputStream ois = new ObjectInputStream(untrustedInputStream);
String message = (String)ois.readObject();
In order to detect malicious payloads or allow trusted classes only, SerialKiller should be
used instead of the standard java.io.ObjectInputStream122.
Example Recommended Usage:
ObjectInputStream ois = new SerialKiller(is, “/etc/serialkiller.conf”);
String message = (String) ois.readObject();
The second argument is the location of SerialKiller's configuration file.
InvalidClassException exceptions should be used to gracefully handle insecure object
deserialization.
Prevent Data Leakage
If there are data members of an object that should never be controlled by end users during
deserialization or exposed to users during serialization, they should be declared as
"transient".
For a class that is defined as Serializable, the sensitive variables should be declared as
'private transient'. For example, the variable 'name' and 'age' of the class Foo were
declared as transient to avoid being serialized120.
Example Recommended Usage:
public class Foo implements Serializable
{
private transient String name;
private transient String age;
……….
……...
Prevent Deserialization of Domain Objects
If a Class is must implement Serializable only due to their hierarchy, in order to
guarantee that the objects can’t be deserialized, readObject method should be declare with
the final modifier120.
Example Recommended Usage:
private final void readObject(ObjectInputStream in) throws java.io.IOException{
throw new java.io.IOException(“Cannot be deserialized”);
}
Harden own java.io.ObjectInputStream
Override ObjectInputStream.html#resolveClass()
to restrict which classes are allowed to be deserialized120.
A example code ensures the SampleObjectInputStream class is guaranteed not to deserialize
any type other than the Foo class.
Example Recommended Usage:
public class SampleObjectInputStream extends ObjectInputStream{
Public SampleObjectInputStream(InputStream inpuStream) throws
IOException{
super(inputStream);
}
@Overide
protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException,
ClassNotFoundException{
if(!osc.getName().equals(Foo.class.getName())){
throw new InvalidClassException(“Unauthorized deserialization
attempt”,
osc.getName());
}
return super.resolveClass(osc);
}
}
2.9 A9 - 2017 – Using Known Vulnerable Components
Outdated components may have known vulnerabilities that are exploitable. Public databases
such as NVD53 and ExploitDB54
offer information on known vulnerabilities and exploits available.
2.9.1 Introducing New External Dependencies
When introducing new external dependency components, use the most up-to-date version of the
component.
Selected most up-to-date version should be scanned by using OWASP Dependency Check for
known vulnerabilities by following "Engineering Guidelines - External
Dependency Analysis using OWASP Dependency Check".
External dependency, as well as all transitive dependencies getting added should be scanned
using instructions from "OWASP Dependency Check CLI" section of the document.
Generated report should be attached with the usual "Approval Request" and the approval
request should be copied to Security Leads Group email.
If most up-to-date version contains a known vulnerability, and if there is no active
development happening, other alternatives should be considered, rather than using the
vulnerable library.
Alert - Approval Required: Any external dependency
component approval request should include OWASP Dependency Check report (or else
Platform Security Team team will down vote the approval request).
Any external component with known vulnerability should not be introduced as a
dependency. If there is any exceptional situation, Platform Security Team should be
informed and review should be conducted on the use-case, source code, known
vulnerabilities and controls in place for mitigating the impact of the vulnerability in
usage path. Platform Security Team's approval is required before introducing such
dependency.
2.9.2 Vulnerabilities in Current Dependencies
OWASP Dependency Check maven plugin should be integrated into the build process of latest
product builds, by following "Engineering Guidelines - External Dependency
Analysis using OWASP Dependency Check".
Engineers can execute Dependency Check by calling the " dependency-check:check" maven goal,
in development environment. However, Jenkins will execute this task by default during the
scheduled builds, to identify if any latest product build contains external dependencies
with known security vulnerabilities.
When it is identified that a security vulnerability has been identified for a particular
external dependency, it is recommended to do following:
- Initiate relevant discussion at Security Leads Group mailing list with subject
"Dependency Vulnerability - [DependencyName] - [DependencyVersion] - [CVE]".
- Analyze the impact on the usage of WSO2, relevant to the particular dependency and
identify if usage of WSO2 makes any product vulnerable.
- If yes, take necessary corrective actions to migrate to a higher version of the
dependency with no known security vulnerabilities.
- If yes, and no new version of the dependency exists, take necessary actions to
nullify the impact of the vulnerability on usage of WSO2, by introducing
additional validations or security checks.
Alert - Approval Required:
Platform Security Team should review any validations, additional security checks or
additional security constraints added in order to nullify the impact of a known
vulnerability in an external dependency when no newer version is available and no
active development is happening.
- If no, update the mail thread with the reasoning and request approval of Platform
Security Team to add relevant mitigation information into OWASP Dependency Check,
suppression file relevant to the component.
Alert - Approval Required:
Platform Security Team should review and merge pull-requests, adding any entries to a
particular component's OWASP Dependency Check suppression file. During the review,
mitigated reason, dependency source, and usage path will be reviewed. This is further
explained in "Engineering Guidelines - OWASP Dependency Check".
2.10 A10 - 2017 - Insufficient logging and Monitoring
Insufficient logging and ineffective integration with security incident response systems
allow attackers to pivot to other systems and maintain persistent threats for weeks or
months before being detected. This leads attacker to tamper, extract or system data123.
2.10.1.1 Prevention Techniques
As per the risk of the data stored or processed by the WSO2 products124.
- Ensure all login, access control failures, and server-side input validation failures
can be logged with sufficient user context to identify suspicious or malicious
accounts, and held for sufficient time to allow delayed forensic analysis.
- Ensure that logs are generated in a format that can be easily consumed by a centralized
log management solutions.
- Ensure high-value transactions have an audit trail with integrity controls to prevent
tampering or deletion, such as append-only database tables or similar.
3. OWASP Top 10 - 2013 Prevention
This section discusses OWASP Top 10 2013 list of the most critical application security
risks.
Note : OWASP has updated its top 10 list of the most critical application security risks in
2017. Please refer to OWASP top 10 2017 for updated list.
3.1 A1 – Injection
Please refer to OWASP Top 10 2017 A1 - Injection
Please refer to OWASP Top 10 2017 A4 - XML External Entity (XXE)
3.2 A2 – Broken Authentication and Session Management
Please refer to OWASP Top 10 2017 A2 - Broken Authentication
3.3 A3 – Cross-Site Scripting (XSS)
Please refer to OWASP Top 10 2017 A7 - Cross-Site Scripting (XSS)
3.4 A4 – Insecure Direct Object References
Please refer to OWASP Top 10 2017 A5 - Broken Access Control
3.5 A5 – Security Misconfiguration
Please refer to OWASP Top 10 2017 A6 - Security Misconfiguration
3.6 A6 – Sensitive Data Exposure
Please refer to OWASP Top 10 2017 A3 - Sensitive Data Exposure
3.7 A7 – Missing Function Level Access Control
Please refer to OWASP Top 10 2017 A5 - Broken Access Control
3.8 A8 - Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted
actions on a web application in which they're currently authenticated. CSRF attacks
specifically target state-changing requests, not theft of data, since the attacker has no
way to see the response to the forged request. With a little help of social engineering
(such as sending a link via email or chat), an attacker may trick the users of a web
application into executing actions of the attacker's choosing. If the victim is a normal
user, a successful CSRF attack can force the user to perform state-changing requests like
transferring funds, changing their email address, and so forth. If the victim is an
administrative account, CSRF can compromise the entire web application49.
3.8.1.1 Prevention Techniques
There are multiple techniques usable for preventing CSRF attacks which are further
documented at Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet50.
Synchronizer Token Pattern
With Synchronizer Token Pattern, any state-changing operation requires a secure random token
which is generally known as CSRF Token. CSRF Token is a unique large random string
generated per user session, using a secure pseudorandom number generator.
Once user session is created, per-session CSRF Token should be added to the server session
as well. Basically, the storage of CSRF Token happens in server session.
Generated CSRF Token should be added to all the forms, generating requests for
state-changing operations, as a hidden input. The server should do additional validation
and reject the request if the CSRF Token sent in the request does not match with the token
available in the user session. In addition to forms, AJAX requests for state-changing
operations should also include the CSRF Token.
Ultimately, the attacker will not be able to make a form submission to protected
application, since attacker will need to know the per-session CSRF Token value in advance,
to craft the attack vector.
This pattern is further discussed in "Core J2EE Patterns"51.
Example of an Incorrectly Designed HTML Form:
<form action="create_user" method="POST">
<div>Username : <input type="text"
name="username"></div>
<div>Password : <input type="password"
name="password"></div>
</form>
Example of a Correctly Designed HTML Form:
<form action="create_user" method="POST">
<div>Username : <input type="text"
name="username"></div>
<div>Password : <input type="password"
name="password"></div>
<input type="hidden" name="CSRFToken"
value="MJpTVsF0EKZGnqbYxvizCupE9AOxhKWmKdD3E6uMVean36CEENAF7Q3swB8TQT5zAERQvvt3lgq2">
</form>
Double Submit Cookie
The difference between "Double Submit Cookie" and "Synchronize Token Pattern" is that,
"Synchronize Token Pattern" uses server session for storing CSRF Token, whereas "Double
Submit Cookie" approach uses cookies to store the CSRF Token.
Upon user login, per-session CSRF token value should be generated and it should be sent to
the browser as a cookie. The server may decide not to store the CSRF token value.
When rendering HTML pages with forms, client side JavaScript should read the CSRF Token
cookie value and inject the value all forms. In addition, AJAX requests should be modified
to send the same value with AJAX requests.
When an application sends an HTTP request for a state changing operation (over HTTP method
other than GET), the server should compare the CSRF Token received over the request
cookies, with the CSRF Token value received in request payload (query parameter/post data).
If values are not matching, request can be identified as a possible CSRF attack.
If an attacker creates a forged HTTP request with the intention of performing a CSRF
attack, correct CSRF Token value will still be sent in the cookie, but they will not have
required cross domain access to read the token value and inject same into the HTML forms.
3.8.1.2 Java Specific Recommendations
- WSO2 products based on WSO2 Carbon Kernel versions prior to 4.4.6 use "Referer Header"
based CSRF prevention, which is no longer recommended.
- WSO2 products based on WSO2 Carbon Kernel 4 (4.4.6+) should use "Synchronizer Token
Pattern" based CSRF prevention, using OWASP CSRFGuard52.
- WSO2 Carbon Kernel 5+ based products and any new applications should use "Double Submit
Cookie" approach in CSRF prevention.
Synchronizer Token Pattern (WSO2 Carbon Kernel 4 (4.4.6+))
OWASP CSRFGuard is used to implement Synchronizer Token Pattern in WSO2 Carbon Kernel 4
(4.4.6+) products. OWASP CSRFGuard provides required classes to generate per-session token
and to do necessary validation on state-changing operations. Furthermore, it provides a
JavaScript which is capable of dynamically adding CSRF Token as a hidden input and override
XMLHttpRequest to include CSRF Token in AJAX requests.
In summary, when integrating OWASP CSRFGuard with a product, it is required to do following
changes:
- Make sure all the state-changing operations are performed using HTTP methods other than
GET. GET requests must not be used for state-changing operations.
- In web.xml or jaggery.conf file, add CsrfGuardServletContextListener.
- This class is responsible for loading CSRFGuard configuration and doing the
initialization of component.
- In web.xml or jaggery.conf file, add CsrfGuardHttpSessionListener.
- This class is responsible for generating and storing per-session CSRF Token.
- In web.xml or jaggery.conf file, add CsrfGuardFilter.
- This class is responsible for comparing the submitted CSRF Token with the token
in user session, and generating error response if an attack was detected.
- In web.xml or jaggery.conf file, add JavaScriptServlet.
- This class is responsible for serving the JavaScript file that is dynamically
adding the CSRF Token to forms and AJAX requests.
- Include "JavaScriptServlet" in the HTML template of the application, so that <head>
element of all pages that need to be protected, should have JavaScriptServlet as the
first JavaScript inclusion.
- Prepare and store per-application CSRF configuration file according to
"repository/conf/security/Owasp.CsrfGuard.Carbon.properties" or reuse the Carbon CSRF
configuration.
- Do thorough testing on CSRF protected application to verify that there is no
functional impact.
Note : CSRF is dropped in OWASP top 10 list of the most critical application security risks
in 2017.
3.9 A9 – Using Known Vulnerable Components
Please refer to OWASP Top 10 2017 A9 - Using Known Vulnerable Components
3.10 A10 - Unvalidated Redirects and Forwards
Unvalidated redirects and forwards are possible when a web application accepts untrusted
input that could cause the web application to redirect the request to a URL contained
within untrusted input. By modifying untrusted URL input to a malicious site, an attacker
may successfully launch a phishing scam and steal user credentials. Because the server name
in the modified link is identical to the original site, phishing attempts may have a more
trustworthy appearance. Unvalidated redirect and forward attacks can also be used to
maliciously craft a URL that would pass the application's access control check and then
forward the attacker to privileged functions that they would normally not be able to access55.
3.10.1.1 Prevention Techniques
Absolute forward URLs or fragments of forward URLs should not be accepted from the end-user
during any operation. This is advised since an attacker can use a less restricted resource
with a malicious forward, to circumvent URL based security control in place and send
requests to a restricted resource.
Alert - Approval Required:
If any component requires that, an absolute forward URL
must be accepted from the end-user by any means (as demonstrated
in OWASP Cheat Sheet56)>, the use-case, as
well as controls in place to provide required protection, must be reviewed and approved
by Platform Security Team, before proceeding with the release of such component.
Absolute redirect URLs should not be accepted from the end-user during any operation, apart
from administrative configurations.
Alert - Approval Required:
If any component requires that, an absolute redirect URL
must be accepted from the end-user (not in administrative configuration), the use-case,
as well as controls in place to provide required protection, must be reviewed and
approved by Platform Security Team, before proceeding with the release of such
component.
The general recommendation is to avoid accepting redirect or forward URL fragments (used to
build absolute or relative redirect/forward URLs) from the end-user.
However, if there is such requirement, it is necessary to follow language specific
recommendations for validating the accepted URL fragment.
3.10.1.2 Java Specific Recommendations
If an absolute URL is accepted from the end-user, it should be validated against a list of
allowed redirect/forward URLs. However, since this the condition explained in Approval
Required blocks above, Platform Security Team should be informed on the use case
and approval is required.
Example Incorrect Usage:
response.sendRedirect(request.getParameter("url"));
Example Correct Usage:
String url = request.getParameter("url");
boolean allowed =
SecurityUtil.validateRedirectUrl(listOfAllowedRedirectUrls, url);
if(!allowed) {
//Required logic to show the relevant error to the
end-user
return;
}
response.sendRedirect(request.getParameter("url"));
When a portion of the redirect or forward URL is expected from the user, it is a must to
validate if the appended fragment contains only the expected type of a value.
Example Incorrect Usage:
response.sendRedirect(Constant.BASE_URL + "/info/" + request.getParameter("index"));
Example Correct Usage:
String index = request.getParameter("index");
if(isInteger(index)) {
response.sendRedirect(Constant.BASE_URL + "/info/" + index);
} else {
//Inform end-user about the error
}
Note: Unvalidated Redirects and Forwards is dropped in OWASP top 10 list of the most
critical application security risks in 2017.
4. OWASP Mobile Top 10 Prevention
4.1 Introduction
This section discusses OWASP Mobile Top 10 prevention techniques that should be followed by
WSO2 engineers while engineering mobile applications.
4.2 M1 - Improper Platform Usage
This category covers misuse of a platform feature or failure to use platform security
controls. It might include Android intents, platform permissions, misuse of TouchID, the
Keychain, or some other security control that is part of the mobile operating system57.
4.2.1 Prevention Techniques
The best method of preventing improper platform usage is to follow the guidelines and best
practices published by the respective platforms, on secure development of the mobile
applications. These guidelines include the proper way to implement the relevant features
and how to maintain them. Also, make sure to check whether the application behaves as it
was intended without misusing platform features.
4.2.1.1 Android Specific Recommendations
Using Intents
Intents are used to request an action such as starting an activity, starting a service or
delivering a broadcast from another app component. Explicit intents specify the component
by the fully qualified class name. Usually, these are used to start a component within the
app. Implicit intents declare a general action to be handled by another application.
Do not use an implicit intent to start a service as implicit intents allow any other app
component to respond to the intent and start the Service transparently to the user. Use an
explicit intent where the component can be specified using the fully qualified class name.
Note: From Android 5.0 (API level 21) onwards, the system will throw an exception if an
implicit intent is used to start a service.
Set the android:exported attribute to false for the specific activity, service or receiver
to limit the exposure to other components.
Example: A Service can be declared in the App Manifest
with the android:exported attribute set to false as follows
<service
android:exported="false"
>
. . .
</service>
Alert - Approval Required:
If any component requires that, android:exported attribute to be set to true, the
use-case, as well as controls in place to provide required protection, must be reviewed
and approved by Platform Security Team, before proceeding with the release of such
component.
Data received by public components should be treated as untrusted data and needs to be
properly validated and sanitized before usage.
Example: An email address received from a public
component must be verified against the email address specifications defined by RFC282258.
4.3 M2 - Insecure Data Storage
This category covers insecure data storage and unintended data leakage57.
4.3.1 Prevention Techniques
4.3.1.1 Client-side data storage
Data stored in the mobile device have a risk of being exposed to an outside side attacker
through a malware or a lost/stolen device. To reduce the impact of an attack, limit the
amount of data stored in a mobile device and use a strong encryption algorithm to encrypt
the stored data.
Android Specific Recommendations
Using internal storage
Do not create files with permissions MODE_WORLD_WRITEABLE59
or MODE_WORLD_READABLE60 as all applications will have
permission to read or write to the file.Use a content provider61
to allow other applications to securely access and modify data.
Note: Constants MODE_WORLD_WRITEABLE and MODE_WORLD_READABLE were deprecated in API level
17
Using external storage
Files stored in the external storage has global read and write permissions. Therefore
external storage should not be used to store any sensitive data. Also, input validations
should be applied when using data from the external storage.
It is not recommended to store executable files in the external storage. If the application
requires loading executable files from external storage, make sure to sign and
cryptographically verify the content before dynamic loading.
Database Related Recommendations
Using a third party encryption can withstand a threat to the native protection provided by
the OS. The master key for the encryption should be randomly generated and encrypted using
a passphrase from the user by the time the data is processed. Unencrypted master key or the
passphrase should not be stored on the device.
Example: SQLCipher is an open source extension to SQLite
that provides transparent 256-bit AES encryption of database files62.
4.3.1.2 HTTP response caching
HTTP responses can have sensitive information. Caching them can increase the risk of data
leakage. Therefore response caching should be disabled for sensitive data.
Example: API responses can contain sensitive information
such as bank account details that should not be cached.
Android Specific Recommendations
Do not use HttpResponseCache63 to cache sensitive data.
4.3.1.3 Keyboard press caching
Mobile devices cache the keyboard input to be used in auto-suggesting words to the user.
This feature can be a vulnerability when sensitive information of one user is cached and
later suggests to a different user. Therefore auto suggest feature must be disabled to
avoid keyboard press caching for sensitive information.
Android Specific Recommendations
The user dictionary in android saves the words entered by the user to be used for
auto-correction. Since this dictionary is available to other applications without
permission, sensitive information can get leaked to other apps. To prevent this, use
android:inputType= “textNoSuggestions” for sensitive data fields or create custom keyboards
with auto-suggest disabled.
4.3.1.4 Copy/Paste buffer caching
When using copy and paste, the data is initially copied into a clipboard. Malicious
applications have the ability to access this clipboard cache to extract sensitive data.
Disable the copy/paste functionality for sensitive data fields to prevent these type of
copy/paste buffer caching.
Example: Application should not allow users to
copy/paste credit card details.
4.3.1.5 Logging
Debug logs are used to identify flaws in the application. However, information provided by
debug logs can be useful for an attacker to gather knowledge about the application.
Therefore debug logs should be disabled in the production environment.
Android Specific Recommendations
Logs are a shared resource for Android. Any application with READ_LOGS permission can view
them. Production applications should limit the logging by using debug flags and configure
logging levels by defining custom log classes.
4.3.1.6 HTML5 local storage
HTML5 local storage can be used to store data within the browser between HTTP requests.
Since this storage is accessible by JavaScript, cross-site scripting attacks can be used to
steal the data. Therefore sensitive data should not be stored in HTML5 local storage.
4.3.1.7 Browser Cookie objects
Cookies are used by servers to store data in the browser. Often session related data are
stored inside cookies. Use Secure flag to indicate that only HTTPS requests are allowed to
transfer cookies and HTTP Only flag to make cookies inaccessible to JavaScript's
Document.cookie API. Make sure that both Secure and HTTP Only flags are set to cookies
containing sensitive data.
4.3.1.8 Analytics data sent to 3rd parties
Mobile applications may need to have access to user’s personal information for
functionality purposes. However, the application should make sure that these sensitive
pieces of information are not being sent to third parties violating the privacy of the
user.
Example: Mobile applications send data to Google
Analytics and Facebook Graph API for analytical purposes.
4.4 M3 - Insecure Communication
This section covers poor handshaking, incorrect SSL versions, weak negotiation, cleartext
communication of sensitive assets, etc57.
4.4.1 Prevention Techniques
4.4.1.1 SSL/TLS
Use TLS to serve all sensitive and nonsensitive traffic. This will prevent from having
mixed SSL sessions where the user’s session ID might get exposed.
Android Specific Recommendations
The application can choose to avoid unencrypted HTTP traffic by using
cleartextTrafficPermitted="false" in the network security config file64.
Example: To enforce HTTPS on all connections to wso2.com
use the following code in the network_security_config.xml
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">wso2.com</domain>
</domain-config>
</network-security-config>
4.4.1.2 Self Signed Certificates
Usually, self-signed certificates are used in the development stage for the easiness of the
developer. However, do not accept self-signed certificates in the production application as
they allow an attacker to easily intercept the communication using their own self-signed
certificate.
4.4.1.3 Certificate Pinning
The application accepts all the certificates signed by trusted Certificate
Authorities(CAs). If one of these trusted CA gets compromised and started issuing
fraudulent certificates, they will be accepted by the application. Certificate pinning can
be used store the certificate locally along with the domain name. This allows detecting
fraudulent certificates as the application can compare previously stored certificate with
the new one.
Android Specific Recommendations
Example: To use certificate pinning on wso2.com use the
following code in the network_security_config.xml. Here, base64 encoded Subject Public
Key Information of the wso2 certificate should be used as the pin.
<network-security-config>
<domain-config>
<domain includeSubdomains="true">wso2.com</domain>
<pin-set expiration="2020-01-01">
<pin digest="SHA-256">2pCcYrG90hDFxwOCsVya7wpbQjqhBy3OPsFyyT+7108=</pin>
</pin-set>
</domain-config>
</network-security-config>
Note: Always include a backup pin to use in an event of a certificate change.
Note: To protect from compromised CAs android has the ability to denylist certain
certificates or even whole CAs. While this list was historically built into the operating
system, starting in Android 4.2 this list can be remotely updated to deal with future
compromises65.
4.4.1.4 Hostname Verification
Hostname verification is verifying whether the hostname of the server that the application
is trying to connect is specified in the certificate presented by the server. It is
important to have this verification as this will make sure that the server has presented
the right certificate.
Android Specific Recommendations
SSLSocket66 does not perform hostname verification. The
application has to verify the hostname of the certificate by calling the method
getDefaultHostnameVerifier() with the expected hostname.
Using org.apache.http.conn.ssl.AllowAllHostnameVerifier or
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER allows the application to accept all
certificates. Make sure that these are not being used in the production code.
Note: org.apache.http.conn.ssl.AllowAllHostnameVerifier and
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER were deprecated in API level 22.
4.4.1.5 SMS
SMS is a protocol that has been designed for user to user communication. It is unencrypted
and not properly authenticated. Therefore, do not use SMS for sensitive data transfer.
Android Specific Recommendations
In Android, SMS messages are transmitted as broadcast intents. Anyone with the READ_SMS
permission can read the SMS messages on the device. Therefore, do not use SMS for data
transfer.
4.5 M4 - Insecure Authentication
This category captures notions of authenticating the end user or bad session
management.This can include:
-
Failing to identify the user at all when that should be required
-
Failure to maintain the user's identity when it is required
-
Weaknesses in session management57
4.5.1 Prevention Techniques
4.5.1.1 Local Authentication
Local Authentication can be bypassed if the attacker has the ability to tamper the
application. Therefore all the authentication checks should be performed by a backend
server.
Alert - Approval Required: If any component
requires implementing local authentication, the use-case, as well as controls in place
to provide required protection, must be reviewed and approved by Platform Security
Team, before proceeding with the release of such component.
4.5.1.2 Password Policy
Ensure that a strong password policy is enforced by the authentication server. Use a
similar strong password policy if the authentication is performed locally with the approval
mentioned in the Local Authentication section.
4.5.1.3 Device Identifiers
Do not use device-specific identifiers for authentication. In an event of change of
ownership of the phone, these IDs can expose previous owner’s data to the new owner.
4.5.1.4 Geo-Location
Geo-location should not be used as an authentication mechanism as attackers can easily
spoof the geo-location. If an application requires the use of geo-location, ensure that the
application has implemented proper geo-location spoof detection mechanism to detect
location anomalies.
4.5.1.5 Token Revocation
In the event of a lost or stolen device, the application should have the ability to
invalidate the authenticated session on the lost/stolen device by revoking a
device-specific token.
4.6 M5 - Insufficient Cryptography
The code applies cryptography to a sensitive information asset. However, the cryptography
is insufficient in some way. Note that anything and everything related to TLS or SSL goes
in M3. Also, if the app fails to use cryptography at all when it should, that probably
belongs in M2. This category is for issues where cryptography was attempted, but it wasn't
done correctly57.
4.6.1 Prevention Techniques
4.6.1.1 Key Generation
Use a secure random number generator to generate strong keys that can withstand brute force
attacks.
Android Specific Recommendations
Use the secure random number generator, SecureRandom67 to
initialize any cryptographic keys generated by KeyGenerator68.
Use of a key that is not generated with a secure random number generator significantly
weakens the strength of the algorithm and may allow offline attacks.
4.6.1.2 Key Storage
Do not store keys in plaintext format. Use a cryptographic vault for secure storage of keys
and make sure that compromised or outdated keys are properly revoked.
Android Specific Recommendations
Use the KeyStore69 for long-term storage and retrieval of
cryptographic keys.
4.6.1.3 Custom Encryption Algorithms
Do not implement custom encryption algorithms. Use a proper algorithm that is widely used
and accepted as secure.
Android Specific Recommendations
Do not write custom protocols for implementing secure tunnels. Instead use
HttpsURLConnection70 or SSLSocket66.
If a custom protocol is needed, do not implement new algorithms. Instead, use existing
cryptographic algorithms such as the implementations of AES and RSA provided in the Cipher71 class.
4.6.1.4 Deprecated Algorithms
OWASP defines following algorithms as deprecated and not to be used for encryption purposes72.
4.7 M6 - Insecure Authorization
This is a category to capture any failures in the authorization. It is distinct from
authentication issues. If the app does not authenticate users at all in a situation where
it should, then that is an authentication failure, not an authorization failure57.
4.7.1 Prevention Techniques
4.7.1.1 Permissions
Do not use roles and permission information coming from the mobile device for
authorization. Backend code should independently verify that any incoming identifiers
associated with a request (operands of a requested operation) that come along with the
identify match up and belong to the incoming identity.
It is a must to request only the minimum number of permissions that your app requests, to
reduce the risk of misusing permissions. In application documentation, it is required to
mention the actual usage of each permission, so that users and security reviewers have
access to this information.
Android Specific Recommendations
In Android, permissions are categorized into four protection levels73.
normal
The default value. A lower-risk permission that gives requesting applications access to
isolated application-level features, with minimal risk to other applications, the system,
or the user. The system automatically grants this type of permission to a requesting
application at installation, without asking for the user's explicit approval (though the
user always has the option to review these permissions before installing).
dangerous
A higher-risk permission that would give a requesting application access to private user
data or control over the device that can negatively impact the user. Because this type of
permission introduces potential risk, the system may not automatically grant it to the
requesting application. For example, any dangerous permissions requested by an application
may be displayed to the user and require confirmation before proceeding, or some other
approach may be taken to avoid the user automatically allowing the use of such facilities.
signature
A permission that the system grants only if the requesting application is signed with the
same certificate as the application that declared the permission. If the certificates
match, the system automatically grants the permission without notifying the user or asking
for the user's explicit approval.
signatureOrSystem
A permission that the system grants only to applications that are in the Android system
image or that are signed with the same certificate as the application that declared the
permission.The "signatureOrSystem" permission is used for certain special situations where
multiple vendors have applications built into a system image and need to share specific
features explicitly because they are being built together.
When defining new permissions, use the dangerous protection level if the permission has the
ability to access the stored data or affect the operation of other applications. Use the
signature protection level if the permission has the ability to share data between
applications signed with the same certificate.
When exposing data over Interprocess Communication(IPC), check the permissions of the data
that are being exposed. Other applications might not have the same level of permission for
the exposed data.
4.7.1.2 Proof Key for Code Exchange (PKCE)
Using OAuth 2.0 authorization code grant type is susceptible to interception attacks. An
attacker has the ability to intercept the authorization code received from the
authorization endpoint via an unprotected communication such as inter-application
communication within the client’s operating system. Proof Key for Code Exchange (PKCE)74 is used to mitigate this vulnerability. In PKCE,
-
A unique cryptographic random key (code verifier) is created by the application with
every authorization request.
- The code verifier is transformed into a code challenge and sent to the authorization
server along with the transform method.
- The authorization server stores the code challenge and the transform method.
- During the request for an access token, the application has to send it’s generated
code verifier to the authorization server.
- The server transforms the received code verifier with the stored transform method and
compares with the previously stored code challenge.
4.8 M7 - Poor Code Quality
This would be the catch-all for code-level implementation problems in the mobile client.
That's distinct from server-side coding mistakes. This would capture things like buffer
overflows, format string vulnerabilities, and various other code-level mistakes where the
solution is to rewrite some code that's running on the mobile device57.
4.8.1 Prevention Techniques
4.8.1.1 Input Validation
All the input from the app and user should be treated as untrusted data. Therefore, input
validation must be used when handling such data.
Android Specific Recommendations
When accessing a content provider, use parameterized query methods such as query(),
update(), and delete() to prevent from potential SQL injection from untrusted sources.
If the content provider is serving files based on filename, make sure that path traversals
are filtered out.
4.8.1.2 Third Party Libraries
Applications rely heavily on third-party libraries. Making the application secure won’t be
enough if the third party libraries contain vulnerabilities. Therefore, security auditing
must thoroughly test third-party libraries for vulnerabilities.
4.8.1.3 Buffer Overflows
Buffer overflows are not possible in Java. However, the application is susceptible to
buffer overflows if it contains native code such as C or C++. To avoid buffer overflows,
make sure that length of the incoming buffer data will not exceed the length of the target
buffer.
Android Specific Recommendations
Run Android Lint75 on the application code using Android
SDK and correct any identified issues.
4.9 M8 - Code Tampering
This category covers binary patching, local resource modification, method hooking, method
swizzling, and dynamic memory modification.
Once the application is delivered to the mobile device, the code and data resources are
resident there. An attacker can either directly modify the code, change the contents of
memory dynamically, change or replace the system APIs that the application uses, or modify
the application's data and resources. This can provide the attacker a direct method of
subverting the intended use of the software for personal or monetary gain57.
4.9.1 Prevention Techniques
4.9.1.1 Tamper Detection
Use tamper detection techniques such as checksums or digital signatures to identify code
tampering.
In an event of code tampering use an appropriate mechanism such as wipe the user data or
send a notification to the server to protect the sensitive data.
4.9.1.2 Restricting Debuggers
Prevent the operating system from permitting to attach a debugger to the application. This
will increase the complexity of an attack76.
Android Specific Recommendations
Set the android:debuggable=”false” in the application manifest to restrict the debuggers.
4.9.1.3 Stripping Binaries
Strip the native binaries to increase the difficulty for an attacker to debug or reverse
engineer.
4.10 M9 - Reverse Engineering
This category includes analysis of the final core binary to determine its source code,
libraries, algorithms, and other assets. Binary inspection tools give the attacker insight
into the inner workings of the application. This may be used to exploit other nascent
vulnerabilities in the application, as well as revealing information about back-end
servers, cryptographic constants and ciphers, and intellectual property57.
4.10.1 Prevention Techniques
4.10.1.1 Obfuscation
Use an obfuscation tool to modify the code to become difficult to understand if the
application gets decompiled.To measure the effectiveness of the obfuscator, use a
deobfuscator.
Example: Hex Rays and Hopper are popular deobfuscators
that are used in reverse engineering applications.
Android Specific Recommendations
ProGuard77 is a Java class file shrinker, optimizer,
obfuscator, and preverifier. The obfuscation step in ProGuard renames the remaining
classes, fields, and methods using short meaningless names making it difficult for
attackers to reverse engineer.
4.10.1.2 Hide Application Logic
In an event of reverse engineering, the attacker might get hold of the source code
containing all the application logic. To reduce the impact critical parts of the
application logic can be moved to a web service.
4.10.1.3 Use C/C++
Java is easier to decompile when compared to C/C++. Use C/C++ to write security sensitive
sections of the code to make reverse engineering difficult for an attacker.
4.11 M10 - Extraneous Functionality
Often, developers include hidden backdoor functionality or other internal development
security controls that are not intended to be released into a production environment. For
example, a developer may accidentally include a password as a comment in a hybrid app.
Another example includes disabling of 2-factor authentication during testing57.
4.11.1 Prevention Techniques
- Examine the app's configuration settings to discover any hidden switches.
-
Verify that all test code is not included in the final production build of the app.
-
Examine all API endpoints accessed by the mobile app to verify that these endpoints are
well documented and publicly available.
-
Examine all log statements to ensure nothing overly descriptive about the backend is
being written to the logs78.
5. General Recommendations for Secure Coding
5.1 Server Side Request Forgery (SSRF) Prevention
By providing URLs to unexpected hosts or ports, attackers can make it appear that the server
is sending the request, possibly bypassing access controls such as firewalls that prevent
the attackers from accessing the URLs directly. The server can be used as a proxy to
conduct port scanning of hosts in internal networks, use other URLs such as that can access
documents on the system (using file://), or use other protocols such as gopher:// or
tftp://, which may provide greater control over the contents of requests79.
This is identified as a high severity attack because it allows an attacker to perform tasks
which are usually prevented by the perimeter security devices such as firewalls. For
example, an attacker would be able to appear to internal and external nodes as the
vulnerable host and perform following:
- Scan a network segment which is behind a firewall by analyzing responses received from
the vulnerable host.
- Perform a service enumerate attack by enumerating through services that are running on
a particular host.
- Bypass host based restrictions, policies or authentication mechanisms in place.
- Query and attack internal hosts that are not normally accessible.
In detail explanations and analysis on SSRF can be found at SSRF Bible Cheatsheet80.
5.1.1.1 Prevention Techniques
Avoid Using User Inputs in Backend Requests
Avoid accepting information used in internal requests such as following from the user:
- URLs used in back channel operations (backend calls made, where WSO2 product acts as
the client).
- IP addresses used in internal or back channel operations.
- Unvalidated and unrestricted system paths used in internal file access.
- Unvalidated XML payloads that are passed to XML parsers without
proper security attributes set.
Perform Strict Error Handling
Display minimum information on the client side at event of an error or something unexpected
occurs. For example, if content type validation failed, provide a generic error message
such as "Invalid Data Retrieved". Also, ensure that the error message is same when the
request fails in the backend and if invalid data is retrieved. This will make it hard to
distinguish between open and closed ports or services.
Perform Strict Response Handling
Validate responses received from the remote resource. If a certain content type is expected
by the application, validate the received response at the server-side before displaying it
on client's side or processing it for the client.
5.1.1.2 Java Specific Recommendations
Proper Usage of Java Security Manager
Java Security Manager should be enabled in any production deployment and any other
deployment that requires additional security (such as environments used for security
related testing).
Steps relevant to enabling Java Security Manager is explained in "Enabling Java Security
Manager" section of the "Administration Guide"81. Security
policy file should be used to only allow network level connections to trusted and
pre-identified hosts or subnets, using SocketPermission82.
Example Policy File:
grant signedBy "wso2carbon" {
// Other policies
permission java.net.SocketPermission
"internal.example.com:1234", "connect, accept";
};
5.2 ClickJacking and Cross Frame Scripting Prevention
Clickjacking, also known as a "UI redress attack", is when an attacker uses multiple
transparent or opaque layers to trick a user into clicking on a button or a link on another
page when they were intending to click on the top level page. Thus, the attacker is
"hijacking" clicks meant for their page and routing them to another page, most likely owned
by another application, domain, or both83.
Cross-Frame Scripting (XFS) is an attack that combines malicious JavaScript with an iframe
that loads a legitimate page in an effort to steal data from an unsuspecting user. This
attack is usually only successful when combined with social engineering. An example would
consist of an attacker convincing the user to navigate to a web page the attacker controls.
The attacker's page then loads malicious JavaScript and an HTML iframe pointing to a
legitimate site. Once the user enters credentials into the legitimate site within the
iframe, the malicious JavaScript steals the keystrokes84.
5.2.1.1 Prevention Techniques
X-Frame-Options
X-Frame-Options HTTP response header should be used to indicate that the browser
should not allow rendering relevant response in a <frame> or <iframe>.
"X-Frame-Options: DENY" should be used in all the cases, except in situations where
the product itself needs to frame a page exposed elsewhere in the same product. In
a situation where product itself needs to frame a page exposed elsewhere in the
same product, "X-Frame-Options: SAMEORIGIN" can be used.
Content-Security-Policy
In addition to non-standard X-Frame-Options header, the standard frame-ancestors
directive can be used in a Content-Security-Policy HTTP response header to indicate
whether or not a browser should be allowed to render a page in a <frame> or
<iframe>. "Content-Security-Policy: frame-ancestors 'none'" should be used in
all cases, except in situations where the product itself needs to frame a page
exposed elsewhere in the same product. In a situation where product itself needs to
frame a page exposed elsewhere in the same product, "Content-Security-Policy:
frame-ancestors 'self'" can be used.
Alert - Approval Required:
If any component requires that, framing of the page should be allowed globally,
the use-case, as well as controls in place to provide required protection, must
be reviewed and approved by Platform Security Team, before proceeding with the
release of such component.
5.2.1.2 Java Specific Recommendations
X-Frame-Options
"org.apache.catalina.filters.HttpHeaderSecurityFilter" Servlet Filter should be
used to add X-Frame-Options header to the HTTP response.
5.3 Random Number Generation
When an undesirably low amount of entropy is available, Pseudo Random Number
Generators are susceptible to suffer from insufficient entropy when they are
initialized, because entropy data may not be available to them yet85.
This will leave patterns or clusters of values that are more likely to occur than
others86.
5.3.1.1 Java Specific Recommendations
Avoid usage of java.util.Random class for security sensitive operations and use
java.security.SecureRandom.
- Periodically throw away the existing java.security.SecureRandom instance and
create a new one. This will generate a new instance with a new seed.
- Periodically add new random material to the PRNG seed by making a call to
java.security.SecureRandom.setSeed(java.security.SecureRandom.generateSeed(int)).87
- SecureRandom.getInstanceStrong() introduced with Java 8 should not be used in
web applications, since application gets blocked until PRNG could collect
required amount of entropy. This could affect Docker containers badly, since
when instance spawning happens, entropy gathering will take significant amount
of time88,89.
Example Incorrect Usage:
int randomPasswordPrefix = new
Random().nextInt(9999);
Example Correct Usage:43
//...
int randomPasswordPrefix;
try {
SecureRandom secureRandom =
SecurityUtil.getSecureRandom();
randomPasswordPrefix = secureRandom.nextInt(9999);
} catch (NoSuchAlgorithmException e) {
//Exception handling
}
//...
private class SecurityUtil
{
//......
private static
SecureRandom secureRandom = null;
private static int secureRandomUsageCount = 0;
private static final
int SECURE_RANDOM_ROTATION_LIMIT =
1000;
private static
SecureRandom getSecureRandom() {
if(secureRandomUsageCount
> SECURE_RANDOM_ROTATION_LIMIT)
synchronized
(SecurityUtil.class) {
if(secureRandomUsageCount
> SECURE_RANDOM_ROTATION_LIMIT) {
secureRandom = null;
}
}
}
if(secureRandom ==
null) {
synchronized
(SecurityUtil.class) {
if(secureRandom ==
null) {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
}
}
}
secureRandomUsageCount++;
return
secureRandom;
}
}
5.4 Cross-Origin Resource Sharing
A wildcard same-origin policy is appropriate when a page or API response is
considered completely public content and it is intended to be accessible to
everyone, including any code on any site.
"Access-Control-Allow-Origin: *" should not be used in WSO2 products. If such
option should be available, user/admin should have a way to configure between using
wildcard and using domain restriction.
Alert - Approval Required:
If any component requires that, framing of the page should be allowed globally,
the use-case, as well as controls in place to provide required protection, must
be reviewed and approved by Platform Security Team, before proceeding with the
release of such component.
5.5 Security Related HTTP Headers
There are HTTP response headers that can be used to configure the security controls
enforced by browsers.
5.6 Securing Cookies
Sensitive cookies such as JSESSIONID should be secured to avoid stealing the
cookies from an insecure network or with the use of an XSS vulnerability of the
application.
5.6.1 Avoiding sending of cookies over insecure (unencrypted)
networks
By setting "secure" attribute of the cookie, it is possible to instruct the browser
not to send the cookie over insecure networks (not to send the cookie unless HTTPS
is used).
Alert - Approval Required:
If any component requires that, "secure" attribute of the cookie should not be
set for a sensitive cookie, the use-case, as well as controls in place to
provide required protection, must be reviewed and approved by Platform Security
Team, before proceeding with the release of such component.
Example Correct Usage:
cookie.setSecure(true);
5.6.2 Avoiding cookies being read by JavaScript and other
client side scripts
If an XSS vulnerability is present in the web application a malicious JavaScript
would be able to read sensitive information stored in cookies and communicate those
to an external party.
An attacker could leverage this pattern to use an XSS vulnerability to read the
session cookie from the victim's browser and conduct session hijacking attack.
To prevent JavaScripts and other client-side scripts from access cookie values,
"HttpOnly" attribute should be set on the cookie.
Alert - Approval Required:
If any component requires that, "HttpOnly" attribute of the cookie should not
be set for a sensitive cookie, the use-case, as well as controls in place to
provide required protection, must be reviewed and approved by Platform Security
Team, before proceeding with the release of such component.
Example of Setting HttpOnly Flag with Java
cookie.setHttpOnly(true);
5.6.3 Summary of Recommendations
All cookies containing sensitive information should:
- Include "secure" attribute
- Include "HttpOnly" attribute
- Include "path" attribute and the "path" attribute should contain accurate
context information
- Not include "expires" attribute (cookie should be a session cookie)
5.7 ZIP Slip Vulnerability Prevention
Zip Slip is a critical directory traversal security vulnerability that can be
exploited when extracting files from an archive125.
To exploit this vulnerability an attacker can create a malicious archive that holds
directory traversal file names (e.g. ../../../../evil.sh). Exploitation of the said
vulnerability could lead to overwriting executable files outside the target folder
where the archived files are being extracted, thus causing the possibility of
remote command execution.
This vulnerability can also be used to overwrite configuration files and other
sensitive resources. The Snyk Security team is maintaining a GitHub repository126 with all projects that have been found
vulnerable to Zip Slip and have been responsibly disclosed to, including fix dates
and versions.
5.7.1 Java Specific Recommendations
When using the java.util.zip library, the name of the file that is being extracted
needs to be validated before storing.
Example Incorrect Usage:125
Enumeration<ZipEntry> entries = zip.getEntries();
while(entries.hasMoreElements()){
ZipEntry e = entries.nextElement();
File f = new
File(destinationDir, e.getName());
InputStream input = zip.getInputStream(e);
IOUtils.copy(input,
write(f));
}
Example Correct Usage:
String canonicalDestinationDirPath = destinationDir.getCanonicalPath();
File destinationfile = new
File(destinationDir, e.getName());
String canonicalDestinationFile = destinationfile.getCanonicalPath();
if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath
+ File.separator)) {
throw new ArchiverException("Entry is outside of
the target dir: " + e.getName());
}
When using a library other than java.util.zip, refer the Snyk Security team’s
GitHub repository126 for a fixed higher version.
Alert - Approval Required: Libraries not
mentioned in the Snyk Security team’s GitHub repository126,
or unable to find a fixed version, must be reviewed and approved by Platform
Security Team.
6. Tooling Recommendations for Secure Coding
6.1 Security Related Dynamic Analysis
OWASP Zed Attack Proxy
90 (ZAP) is the recommended tool
for performing dynamic security analysis.
6.2 Security Related Static Code Analysis
Find Security Bugs
91, FindBugs plugin is the
recommended tool for performing static security analysis.
6.3 Dependency Vulnerability Analysis
OWASP Dependency Check92 is the recommended tool
for performing dependency vulnerability analysis.