White Paper

TECHNICAL REPORTS

Secure Engineering Guidelines

By WSO2 Platform Security Team
June 2017 | TR-001

Table of Contents

A. WSO2 Secure Coding Guidelines

Version: 1.8 | Date: 28th May 2017

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 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.

"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 Instructions

Code formatter available at https://hilite.me should be used for code blocks, with the appropriate programming language and "emacs" style selected.

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 (Replace the italic text according to requirement and make any adjustments to the text if additional instructions should be added):

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. Reference should use "Footnote" format and should not include direct links:

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].

Inline hyperlinks should be used only when referring to a different section of the same document. Footnote format should be used for all external document references.

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 28th May 2017 Ayoma Wijethunga, Tharindu Edirisinghe Initial version of Secure Coding Guidelines - 2nd Edition

2. OWASP Top 10 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 whitelisting against the user input.

Example: A whitelist 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 whitelist. 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,5

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 Extension6:

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 7,8.

All user inputs that are getting directly appended to any LDAP queries should be filtered through an encoding function that does proper encoding for LDAP9.

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 validation10.

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" function11, "WScript.Shell"12 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 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 impacts13.

2.1.4.1 Java Specific Recommendations

In order to resolve this issue, it is required to configure the XML parser correctly performing following actions.

  1. Enable the DocumentBuilderFactory namespace awareness
  2. Disable the DocumentBuilderFactory entity reference expansion
  3. Enable the DocumentBuilderFactory secure processing feature
  4. Disable the DocumentBuilderFactory "https://xml.org/sax/features/external-general-entities" feature
  5. Use a SecurityManager for the DocumentBuilderFactory
  6. 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.

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 DocumentBuilderFactory getSecuredDocumentBuilder() {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
try {
dbf.setFeature(Constants.SAX_FEATURE_PREFIX +
Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false);
dbf.setFeature(Constants.SAX_FEATURE_PREFIX +
Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false);
dbf.setFeature(Constants.XERCES_FEATURE_PREFIX +
Constants.LOAD_EXTERNAL_DTD_FEATURE, false);
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (ParserConfigurationException e) {
logger.error(
"Failed to load XML Processor Feature " +
Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE + " or " +
Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE + " or " +
Constants.LOAD_EXTERNAL_DTD_FEATURE);
}

SecurityManager securityManager = new SecurityManager();
securityManager.setEntityExpansionLimit(ENTITY_EXPANSION_LIMIT);
dbf.setAttribute(Constants.XERCES_PROPERTY_PREFIX +
Constants.SECURITY_MANAGER_PROPERTY, securityManager);
return dbf;
}

2.1.4.2 PHP Specific Recommendations

If libxml is used in XML processing, libxml_disable_entity_loader function14 must be used to protection against XXE attacks15,16.

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.1.5 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 attacks17.

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 responses18.

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.5.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 values19,20.

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 621 and Tomcat 722 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 branch23. 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.6 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 reconciliation24.

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.6.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 Guide25.

2.2 A2 - Broken Authentication and Session Management

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 Server26.

The session token could be compromised in different ways; the most common methods controllable by application developer are:

  • Session Sniffing
  • Man-in-the-middle attack
  • Predictable session token
  • Client-side attacks (XSS, malicious JavaScript Codes, Trojans, etc)
2.2.1.1 Prevention Techniques

Preventing session sniffing and man-in-the-middle 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 HTTPS27.

HSTS header prevents sensitive information exposure over unencrypted channels by avoiding SSLStrip attacks28 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.

WSO2 Document Reference: Further information on required changes and recommended configuration for WSO2 products as well as production deployments are available at "Engineering Guidelines - Security Related HTTP Headers".

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 server29.

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 user30. In addition, HPKP header prevents attack patterns such as SSLSplit31 attack.

WSO2 Document Reference: Further information on required changes and recommended configuration for WSO2 products as well as production deployments are available at "Engineering Guidelines - Security Related HTTP Headers".

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 it32.

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, he 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 he successfully gets access to the application33.

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 recommendations34.

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" documentation35.

2.2.3.3 PHP Specific Recommendations

Below minimum session configuration should be used with PHP applications36 :

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 - 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 etc37.

2.3.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 references38.

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.3.1.2 Java Specific Recommendations

Output Encoding

OWASP Java Encoder39 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 Sanitizer40 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 policy41. 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.

WSO2 Document Reference: Further information on required changes and recommended configuration for WSO2 products as well as production deployments are available at "Engineering Guidelines - Security Related HTTP Headers".

2.3.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, "&quot;").replace(/'/g, '&#39;') );

2.3.1.4 PHP Specific Recommendations

Output Encoding

"htmlspecialchars" function42 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 Purifier43 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

WSO2 Document Reference: Further information on required changes and recommended configuration for WSO2 products as well as production deployments are available at "Engineering Guidelines - Security Related HTTP Headers".

2.4 A4 - Insecure Direct Object References

2.4.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 files44.

2.4.1.1 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.

2.4.1.2 Java Specific Recommendations

Normalize45 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: 46
//...
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;
}
}

2.4.1.3 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: 47
<?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 A5 - 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 misconfiguration48.

2.5.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.6 A6 - Sensitive Data Exposure

2.6.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 methods49.

2.6.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.6.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 instead50.

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.6.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 user51.

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 password52.

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: 53
<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.7 A7 - 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 Control54.

2.7.1.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.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 application55.

2.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 Sheet56.

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"57.

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 he will not have required cross domain access to read the token value and inject same into the HTML forms.

2.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 CSRFGuard58.
  • 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.

WSO2 Document Reference: Further information on required changes and code-level examples are available at "Engineering Guidelines - OWASP CSRF Guard" document.

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.

2.9 A9 - Using Known Vulnerable Components

Outdated components may have known vulnerabilities that are exploitable. Public databases such as NVD59 and ExploitDB60 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 - OWASP Dependency Check". Generated report should be attached with the usual "Approval Request" and the approval request should be copied to security-leads@wso2.com.

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 - 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@wso2.com" 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.
    1. If yes, take necessary corrective actions to migrate to a higher version of the dependency with no known security vulnerabilities.
    2. 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.

  1. 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 - 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 access61.

2.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 Sheet62)>, 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.

2.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
}

3. General Recommendations for Secure Coding

3.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 requests63.

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 Cheatsheet64.

3.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.

3.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"65. Security policy file should be used to only allow network level connections to trusted and pre-identified hosts or subnets, using SocketPermission66.

Example Policy File:
grant signedBy "wso2carbon" {
// Other policies
permission java.net.SocketPermission "internal.example.com:1234", "connect, accept";
};

3.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 both67.

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 keystrokes68.

3.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.

3.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.

WSO2 Document Reference: Further information on required changes and recommended configuration for WSO2 products as well as production deployments are available at "Engineering Guidelines - Security Related HTTP Headers".

3.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 yet69. This will leave patterns or clusters of values that are more likely to occur than others70.

3.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)).71
  • 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 time72,73.

Example Incorrect Usage:
int randomPasswordPrefix = new Random().nextInt(9999);

Example Correct Usage:74
//...
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;
}
}

3.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.

3.5 Security Related HTTP Headers

There are HTTP response headers that can be used to configure the security controls enforced by browsers.

WSO2 Document Reference: Further information on required changes and recommended configuration for WSO2 products as well as production deployments are available at "Engineering Guidelines - Security Related HTTP Headers".

3.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.

3.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);

3.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);

3.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)

4. Tooling Recommendations for Secure Coding

4.1 Security Related Dynamic Analysis

OWASP Zed Attack Proxy75 (ZAP) is the recommended tool for performing dynamic security analysis.

WSO2 Document Reference: Further information on using OWASP Zed Attack Proxy (ZAP) with WSO2 recommended security policies are available at "Engineering Guidelines - Tooling - Dynamic Analysis with OWASP ZAP" document.

4.2 Security Related Static Code Analysis

Find Security Bugs 76, FindBugs plugin is the recommended tool for performing static security analysis.

WSO2 Document Reference: Further information on using OWASP Zed Attack Proxy (ZAP) with WSO2 recommended security policies are available at "Engineering Guidelines - Tooling - Static Code Analysis using FindSecurityBugs" document.

B. WSO2 Engineering Guidelines - Security Related HTTP Headers

Version: 1.1 | Date: 7th April 2017

Revision History

Version Release Date Contributors / Authors Summary of Changes
1.0 26th May 2016 Ayoma Wijethunga Initial version
1.1 7th April 2017 Ayoma Wijethunga Formatting changes

Formatting Instructions

Please refer to the "Formatting Instructions" section of "Secure Coding Guidelines - 2nd Edition" document77.

1. Introduction

1.1 Mandatory Headers

Below list consists of standard and non-standard security related HTTP headers, that must be enabled in order to enhance security aspects of web applications:

X-XSS-Protection: 1; mode=block
Enables reflected XSS protection in supported web browsers78.

X-Content-Type-Options: nosniff
Disable mime sniffing, which can result reflected or stored XSS in certain browsers79.

1.2 Configurable Headers

In addition, following security headers should be configured according to requirement of the application and they can be customized based on URL pattern:

X-Frame-Options: DENY
Disable embedding web application in iframes or frames80.

X-Frame-Options: SAMEORIGIN
Allow embedding web application in iframes or frames, only within same origin81.

1.3 Production Recommandations

Production or staging deployments (with CA signed certificates) should enable following headers for additional security:

Strict-Transport-Security: max-age=15768000; includeSubDomains
Prevent any communication over HTTP, since the time last response was received with
the aforementioned header, up-to duration defined in max-age82.

Security headers that need to be set with external filter (based on customer and security needs) or that should be incorporated into Tomcat filter in future release includes following:

Public-Key-Pins: pin-sha256="<sha256>"; pin-sha256="<sha256>"; max-age=15768000; includeSubDomains
Instructs web client to associate a specific cryptographic public key with a certain
web server to prevent MITM attacks with forged certificates83.

Content-Security-Policy:
Allow declaring what dynamic resources are allowed to load to serve current
response84,85. Replaces X-Fame-Options and X-XSS-Protection, non-standard headers with
standardized headers. For additional detail refer to Content-Security-Policy.com86.

2. Securing Java Web Applications

2.1 Recommended Default Configuration

Recommended web.xml filter mapping for development environments is as follows (WSO2 products should be released with this default configuration):

<filter>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>

2.2 Recommandation Production Configuration

Recommended web.xml filter mapping for production environments is as follows:

<filter>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsMaxAgeSeconds</param-name>
<param-value>15768000</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>

2.3 Customized Configuration

It is possible to use filter mappings to cater product level customizations required. For example in order to enable X-Frame-Options only for particular URLs, below configuration can be used:

<filter>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<!-- Disable sending X-Frame-Options with all responses-->
<init-param>
<param-name>antiClickJackingEnabled</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>

<filter>
<filter-name>HttpHeaderSecurityFilter_AntiClickJacking</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<!-- Disable other headers except X-Frame-Options (not required, but enhances
performance)-->
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>blockContentTypeSniffingEnabled</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xssProtectionEnabled</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter_AntiClickJacking</filter-name>
<url-pattern>/carbon/*</url-pattern>
<url-pattern>/dashboard/*</url-pattern>
</filter-mapping>

If an application requires enabling SAMEORIGIN framing only for a particular URL, below configuration can be used :

<filter>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>false</param-value>
</init-param>
</filter>

<filter>
<filter-name>HttpHeaderSecurityFilter_AntiClickJacking_SpecialURL</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<!-- Disable other headers except X-Frame-Options (not required, but enhances
performance)-->
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>blockContentTypeSniffingEnabled</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xssProtectionEnabled</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>antiClickJackingOption</param-name>
<param-value>SAMEORIGIN</param-value>
</init-param>
</filter>

<!-- Global filter mapping -->
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>

<!-- Overriding filter mapping for the specific URL, should come after global filter
mapping-->
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter_AntiClickJacking_SpecialURL</filter-name>
<url-pattern>/special_url/*</url-pattern>
</filter-mapping>

Further details on configuration is available at Tomcat official documentation on HTTP_Header_Security_Filter87 and relevant source files88.

3. Securing Jaggery Applications

It is required to upgrade Jaggery version to 0.12.6 or later.

3.1 Recommended Default Configuration

Recommended jaggery.conf filter mapping for development environments is as follows (WSO2 products should be released with this default configuration):

"filters":[
{
"name":"HttpHeaderSecurityFilter",
"class":"org.apache.catalina.filters.HttpHeaderSecurityFilter",
"params" : [{"name" : "hstsEnabled", "value" : "false"}]
}
],
"filterMappings":[
{
"name":"HttpHeaderSecurityFilter",
"url":"*"
}
]

3.2 Recommandation Production Configuration

Recommended jaggery.conf filter mapping for production environments is as follows :

"filters":[
{
"name":"HttpHeaderSecurityFilter",
"class":"org.apache.catalina.filters.HttpHeaderSecurityFilter"
"params" : [{"name" : "hstsMaxAgeSeconds", "value" : "15768000"}]
}
],
"filterMappings":[
{
"name":"HttpHeaderSecurityFilter",
"url":"*"
}
]

3.3 Customized Configuration

It is possible to use filter mappings to cater product level customizations required. For example in order to enable X-Frame-Options only for particular URLs, below configuration can be used :

"filters":[
{
"name":"HttpHeaderSecurityFilter",
"class":"org.apache.catalina.filters.HttpHeaderSecurityFilter"
"params" : [{"name" : "antiClickJackingEnabled", "value" : "false"}]
},
{
"name":"HttpHeaderSecurityFilter_AntiClickJacking",
"class":"org.apache.catalina.filters.HttpHeaderSecurityFilter"
"params" : [
{"name" : "hstsEnabled", "value" : "false"},
{"name" : "blockContentTypeSniffingEnabled", "value" : "false"},
{"name" : "xssProtectionEnabled", "value" : "false"}
]
}
],
"filterMappings":[
{
"name":"HttpHeaderSecurityFilter",
"url":"*"
},
{
"name":"HttpHeaderSecurityFilter_AntiClickJacking",
"url":"/example1/*"
},
{
"name":"HttpHeaderSecurityFilter_AntiClickJacking",
"url":"/example2/*"
}
],

If an application requires enabling SAMEORIGIN framing only for a particular URL, below configuration can be used :

"filters":[
{
"name":"HttpHeaderSecurityFilter",
"class":"org.apache.catalina.filters.HttpHeaderSecurityFilter",
"params" : [{"name" : "hstsEnabled", "value" : "false"}]
},
{
"name":"HttpHeaderSecurityFilter_AntiClickJacking_SpecialURL",
"class":"org.apache.catalina.filters.HttpHeaderSecurityFilter"
"params" : [
{"name" : "hstsEnabled", "value" : "false"},
{"name" : "blockContentTypeSniffingEnabled", "value" : "false"},
{"name" : "xssProtectionEnabled", "value" : "false"},
{"name" : "antiClickJackingOption", "value" : "SAMEORIGIN"}
]
}
],
"filterMappings":[
{
"name":"HttpHeaderSecurityFilter",
"url":"*"
},
{
"name":"HttpHeaderSecurityFilter_AntiClickJacking_SpecialURL",
"url":"/special_url/*"
}
],

Further details on configuration is available at Tomcat official documentation on HTTP_Header_Security_Filter89 and relevant source files90.

C. WSO2 Engineering Guidelines - OWASP CSRFGuard

Version: 1.1 | Date: 7th April 2017

Revision History

Version Release Date Contributors / Authors Summary of Changes
1.0 30th May 2016 Ayoma Wijethunga Initial version
1.1 7th April 2017 Ayoma Wijethunga Formatting changes

Formatting Instructions

Please refer to the "Formatting Instructions" section of "Secure Coding Guidelines - 2nd Edition" document91.

1. Introduction

This document introduces OWASP CSRFGuard and further summarizes best practices and configuration recommendations for applications hosted on WSO2 platform. In addition, this document further explains configuration values that can be fine tuned to increase security, based on security requirements of the specific application.

OWASP CSRFGuard92 is an OWASP flagship project that provides synchronizer token pattern based CSRF protection in a comprehensive and customizable manner.

CSRFGuard offers complete protection over CSRF scenarios by covering HTTP POST, HTTP GET as well as AJAX based requests.

Forms based on HTTP POST and HTTP GET methods can be protected by injecting CSRF tokens into "action" of the form, or by embedding token in a hidden field. In addition, HTTP GET requests sent as a result of resource inclusions and links can also be protected by appending relevant token in the "href" or "src" attributes. Token inclusions can be done manually using provided JSP tag library or by using a JavaScript based automated injection mechanism. AJAX requests are protected by injecting an additional header which contains CSRF token.

2. Recommended Approach for WSO2 Products

Any state changing actions should be performed with HTTP POST method, with exception for usage of PUT and DELETE methods in REST APIs.

CSRFGuard should not validate HTTP GET requests for CSRF protection. Token injection should be performed using JavaScript mechanism93. Hidden input field injection should be the only injection operation performed by CSRFGuard, that will protect HTTP POST based forms. In addition, AJAX POST requests should be protected by sending CSRF token in a header.

CSRF token values should not be exposed in URL. In situations where script injection can not be performed and built in AJAX protection does not suffice, product teams may decide to use JST tag library based manual inclusion94, after verifying with Platform Security Team.

Product teams should append CSRF exclusion URLs exposed from the root context, relevant to the particular product, in "Owasp.CsrfGuard.Carbon.properties" by following the steps mentioned in section 6.

3. Securing Web Applications

Recommended web.xml changes:

<!-- OWASP CSRFGuard context listener used to read CSRF configuration -->
<listener>
<listener-class>org.owasp.csrfguard.CsrfGuardServletContextListener</listener-class>
</listener>

<!-- OWASP CSRFGuard session listener used to generate per-session CSRF token -->
<listener>
<listener-class>org.owasp.csrfguard.CsrfGuardHttpSessionListener</listener-class>
</listener>

<!-- OWASP CSRFGuard per-application configuration property file location-->
<context-param>
<param-name>Owasp.CsrfGuard.Config</param-name>
<param-value>repository/conf/security/Owasp.CsrfGuard.Carbon.properties</param-value>
</context-param>

<!-- OWASP CSRFGuard filter used to validate CSRF token-->
<filter>
<filter-name>CSRFGuard</filter-name>
<filter-class>org.owasp.csrfguard.CsrfGuardFilter</filter-class>
</filter>

<!-- OWASP CSRFGuard filter mapping used to validate CSRF token-->
<filter-mapping>
<filter-name>CSRFGuard</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- OWASP CSRFGuard servlet that serves dynamic token injection JavaScript (application can customize the URL pattern as required)-->
<servlet>
<servlet-name>JavaScriptServlet</servlet-name>
<servlet-class>org.owasp.csrfguard.servlet.JavaScriptServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>JavaScriptServlet</servlet-name>
<url-pattern>/csrf.js</url-pattern>
</servlet-mapping>

Include "JavaScriptServlet" in the HTML template of the application, so that element of all pages that need to be protected, should have JavaScriptServlet as the first JavaScript inclusion.

<html>
<head>
...
<script
type="text/javascript" src="/csrf.js"></script>

<!-- other JavaScript inclusions should follow "csrf.js" inclusion -->
<script type="text/javascript" src="/main.js">lt;/script>
...
</head>
<body>
...
</body>
</html>

Prepare and store per-application CSRF configuration file according to section 5 and 6 of the document.

4. Securing Jaggery Applications

Update Jaggery version to 0.12.6
Recommended jaggery.conf changes:

"listeners" : [
{
"class" : "org.owasp.csrfguard.CsrfGuardServletContextListener"
},
{
"class" : "org.owasp.csrfguard.CsrfGuardHttpSessionListener"
}
],
"servlets" : [
{
"name" : "JavaScriptServlet",
"class" : "org.owasp.csrfguard.servlet.JavaScriptServlet"
}
],
"servletMappings" : [
{
"name" : "JavaScriptServlet",
"url" : "/csrf.js"
}
],
"contextParams" : [
{
"name" : "Owasp.CsrfGuard.Config",
"value" : "repository/conf/security/Owasp.CsrfGuard.dashboard.properties"
}
],
"filters" : [
{
"name" : "CSRFGuard",
"class" : "org.owasp.csrfguard.CsrfGuardFilter"
}
],
"filterMappings" : [
{
"name" : "CSRFGuard",
"url" : "/*"
}

],

Include "JavaScriptServlet" in HTML template of the application, so that <head> element of all pages that need to submit or make ajax requests to a protected URL should reference it as the first JavaScript inclusion.

<html>
<head>
...
<script
type="text/javascript" src="/csrf.js"></script>

<!-- other JavaScript inclusions should follow "csrf.js" inclusion -->
<script type="text/javascript" src="/main.js"></script>
...
</head>
<body>
...
</body>
</html>

Prepare and store per-application CSRF configuration file according to section 5 and 6 of the document.

5. Recommended Default Configuration Changes

Diff available at95 highlights recommended changes, which are further listed below.

# WSO2 : Since state-changing operations are not performed via HTTP GET,
# disabling CSRF validation for GET method.

org.owasp.csrfguard.UnprotectedMethods=GET

# WSO2 : Considering overhead, necessity, as well as current unintended behaviour
# of library after blocking a CSRF attack, disabling per-page tokens.

org.owasp.csrfguard.TokenPerPage=false

# WSO2 : Disabling token rotation after blocking a CSRF attack, since this behaviour
# will break back navigation after blocking an attack.
#org.owasp.csrfguard.action.Rotate=org.owasp.csrfguard.action.Rotate

# WSO2 : Disable redirecting user to an error page after blocking a CSRF attack
#org.owasp.csrfguard.action.Redirect=org.owasp.csrfguard.action.Redirect
#org.owasp.csrfguard.action.Redirect.Page=%servletContext%/error.html

# WSO2 : Enable sending a 403 error after blocking a CSRF attack. Product teams
# can add error page that handles 403 or "org.owasp.csrfguard.action.Error" to
# display custom error pages.

org.owasp.csrfguard.action.Error=org.owasp.csrfguard.action.Error
org.owasp.csrfguard.action.Error.Code
=403
org.owasp.csrfguard.action.Error.Message
=Security violation.

# WSO2 : Since, CSRFGuard will send relevant token name as HTTP header
# "X-" prefix was added to express that this is a non-standard header.

org.owasp.csrfguard.TokenName=X-CSRF-Token

# WSO2 : Disable printing configuration during start-up
org.owasp.csrfguard.Config.Print = false

# WSO2 : Disable JavaScript from injecting token value to HTTP GET based forms.
# This prevents token leakage that could occur when sending token in URL.
# State-changing actions should not be performed over HTTP GET

org.owasp.csrfguard.JavascriptServlet.injectGetForms = false

# WSO2 : Disable JavaScript from injecting token value to form action.
# This prevents token leakage that could occur when sending token in URL.

org.owasp.csrfguard.JavascriptServlet.injectFormAttributes = false

# WSO2 : Disable JavaScript from injecting token value to "src" and "href".
# This prevents token leakage that could occur when sending token in URL.

org.owasp.csrfguard.JavascriptServlet.injectIntoAttributes = false

# WSO2 : Changing X-Request-With header text to avoid unnecessary information disclosure.
<org.owasp.csrfguard.JavascriptServlet.xRequestedWith = WSO2 CSRF Protection

# WSO2 - Pseudo-random number generator provider should be configured based on
# environment (SUN/IBMJCE)

org.owasp.csrfguard.PRNG.Provider=SUN

6. Excluding URLs from CSRF Validation

Web applications can include property keys with "org.owasp.csrfguard.unprotected." prefix to exclude relevant patterns from CSRF protection.

Examples :

org.owasp.csrfguard.unprotected.Default=%servletContext%/exampleAction
org.owasp.csrfguard.unprotected.Default_1
=%servletContext%/exampleAction
org.owasp.csrfguard.unprotected.Example
=%servletContext%/exampleAction/*
org.owasp.csrfguard.unprotected.ExampleRegEx
=^%servletContext%/.*Public\.do$

Note : Please do not use additional "." (period) symbols in unprotected URL alias which is followed by "org.owasp.csrfguard.unprotected."

Example relevant to above note:

Example Incorrect Usage:
org.owasp.csrfguard.unprotected. auth.example=%servletContext%/auth

Example Correct Usage:
org.owasp.csrfguard.unprotected.authExample=%servletContext%/auth

7. Further Enhancing Security

This section list down configurations values that can be used to further enhance security or introduce additional restrictions. Changes to following configuration values should only be done based on customer request or justifiable application level requirement, since they will affect performance or user experience.

Below property can be used to change the hashing algorithm used to generate CSRF token:

org.owasp.csrfguard.PRNG=SHA1PRNG

Following property can be used to define the length of CSRF token:

org.owasp.csrfguard.TokenLength=32

Following property can be enabled to invalidate the user session if an CSRF attack attempt was blocked by CSRFGuard:

#org.owasp.csrfguard.action.Invalidate=org.owasp.csrfguard.action.Invalidate

8. WSO2 Product Integration Checklist

Checklist Item 1:
Make sure state changing actions are performed only with HTTP POST method, with exception for usage of PUT and DELETE methods in REST APIs. No state changing operation should happen through GET requests.

Checklist Item 2:
CSRFGuard configuration should be changed to allow unauthenticated sessions to pass through the filter, by setting "org.owasp.csrfguard.ValidateWhenNoSessionExists" property to "false" in "/repository/conf/security/Owasp.CsrfGuard.Carbon.properties". This can be done using product distribution POM file by adding below rule in "tasks" section of "maven-antrun-plugin" :

<!-- Update Owasp.CsrfGuard.properties file with ValidateWhenNoSessionExists to disable validation on requests made with no valid session -->
<
replace
file="target/wso2carbon-core-${carbon.kernel.version}/repository/conf/security/
Owasp.CsrfGuard.Carbon.properties"
token="org.owasp.csrfguard.ValidateWhenNoSessionExists = true"
value="org.owasp.csrfguard.ValidateWhenNoSessionExists = false"/>

Checklist Item 3 (For WSO2 Carbon 4.4.6 / 4.4.7 / 4.4.8 based products only):
CSRFGuard configuration location should be changed in web.xml to adapt into IBM JDK and DevStudio deployment environments. Default configuration location should be changed from
"/repository/conf/security/Owasp.CsrfGuard.Carbon.properties" to "repository/conf/security/Owasp.CsrfGuard.Carbon.properties". This can be done using product distribution POM file by adding below rule in "tasks" section of "maven-antrun-plugin" (if any product completely replaces the web.xml file derived from carbon, product should add this change in their version of web.xml file) :

Note : This path correction should also be done in any web.xml files other than carbon web.xml, and also in jaggery.conf files, if a product contains such.

<!-- Update Owasp.CsrfGuard.properties file location to fix IBM JDK and DevStudio issue-->
<replace
file="target/wso2carbon-core-${carbon.kernel.version}/repository/conf/tomcat/carbon/
WEB-INF/web.xml"
token="/repository/conf/security/Owasp.CsrfGuard.Carbon.properties"
value="repository/conf/security/Owasp.CsrfGuard.Carbon.properties"/>

Checklist Item 4
CSRFGuard configuration for Carbon console is available at
"/repository/conf/security/Owasp.CsrfGuard.Carbon.properties". Product team should append product specific CSRF exclusions for "Carbon console" to configuration file, during "distribution" maven build.

Product specific patterns used in previous implementation are available at96 for reference.

Please refer to "Excluding URLs from CSRF Validation" section for more details on adding exclusion patterns.

Checklist Item 5
If product contains Java web applications other than "Carbon console", make sure web.xml and template follows instructions in "Securing Web Applications" section.

While preparing CSRFGuard configuration file for the web application, you may duplicate "/repository/conf/security/Owasp.CsrfGuard.Carbon.properties" and make application specific changes (if there is any). Thereafter, use "Owasp.CsrfGuard.Config" context parameter to point to the configuration file location.

Checklist Item 6
If product contains Jaggery web applications, make sure jaggery.conf and template follows instructions in "Securing Jaggery Applications" section.

While preparing CSRFGuard configuration file for the Jaggery application, you may duplicate "/repository/conf/security/Owasp.CsrfGuard.Carbon.properties" and make application specific changes (if there is any). Thereafter, use "Owasp.CsrfGuard.Config" context parameter to point to the configuration file location.

Checklist Item 7
If any component made available within carbon context (root context) product contains a screen that is not rendered within the "Carbon console" template, but submits data to a "CSRF protected" resources exposed from "Carbon console" (root context) :

  • Component should include the CSRFGuard JavaScript as the first JavaScript inclusion in <head> section.
  • Example : TryIt (Example PR97)

Checklist Item 8
If any component contains JavaScript logic that generates forms dynamically using "document.createElement('form')" and submits same to a CSRF protected URL, it is required to add CSRF token manually as a form element using taglib:

  • Add taglib to the jsp,

<%@ taglib uri="https://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project/Owasp.CsrfGuard.tld" prefix="csrf" %>

  • Manually inject the csrf token to form

var input = document.createElement("input");
input.setAttribute('type',"hidden");
input.setAttribute('name',"<csrf:tokenname/>");
input.setAttribute('name',"<csrf:tokenvalue/>");
form.appendChild(input);

Checklist Item 9
If any AJAX POST request sent to a CSRF protected, relative path (path not starting with "https://" or "https://") contains a colon (":") in the request URL, CSRFGuard will fail to add X-CSRF-Token header. This will result in a CSRF error. As a fix, it is required to include the CSRF token manually as a header similar to following:

jQuery.ajax({
type: "POST",
url: "../eventreceiver/get_adapter_properties.jsp?name=example:1.0.0",
data: {},
contentType: "application/json; charset=utf-8",
dataType: "text",
beforeSend: function(xhr) {
xhr.setRequestHeader("<csrf:tokenname/>","<csrf:tokenvalue/>");
},
success: function (propertiesString) {
...
}
});

Checklist Item 10
If an integration test submits data to a URL protected by CSRFGuard, test case should do following to prevent test failures:

  • Call configured JavaScriptServlet with HTTP header "FETCH-CSRF-TOKEN: 1" set to retrieve CSRF token for current session.

Example Request: curl 'https://localhost:9443/carbon/admin/js/csrfPrevention.js' -X POST -H 'FETCH-CSRF-TOKEN: 1' -H 'JSESSIONID=0C3C606B62FC7AC2D175AA1B9DCA971D;' --compressed --insecure

Example Response: X-CSRF-Token:TCPT-DI3J-DVGZ-2684-NQ67-L9OR-DTZU-FW85

  • Send CSRF token received in response as a parameter of submission.

Checklist Item 11
For file uploads (multipart requests), the recommendation is to manually inject the csrf token to action of the form (including requests made to /fileupload path). To do so, following steps need to be followed:

  • Add taglib to the jsp,

<%@ taglib
uri="https://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project/Owasp.CsrfGuard.tld" prefix="csrf" %>

  • Manually inject the csrf token to form action

action="../../fileupload/webapp?<csrf:tokenname/>=<csrf:tokenvalue/>"

D. WSO2 Engineering Guidelines - Dynamic Analysis with OWASP ZAP

Version: 1.2 | Date: 4th June 2016

Revision History

Version Release Date Contributors / Authors Summary of Changes
1.0 4th June 2016 Tharindu Edirisinghe Initial version
1.1 21st June 2016 Prakhash Sivakumar ZAP API Information
1.2 25th April 2017 Ayoma Wijethunga Formatting Changes

Formatting Instructions

Please refer to the "Formatting Instructions" section of "Secure Coding Guidelines - 2nd Edition" document98.

1. Introduction

This document provides details of all necessary steps for configuring the OWASP Zed Attack Proxy (OWASP ZAP)99 tool for scanning WSO2 products in order to discover security threats.

2. OWASP ZAP Setup

2.1 Increase JVM Heap Size for Running ZAP

The heap size is defined in the zap.sh (for linux) and zap.bat (for Windows) files. Default value is set to Xmx512m (if available free memory is above 1,500 MB) and increase the value appropriately based on the memory availability of your system. (At-least 4GB is recommended)

In addition to that, ZAP scan is a long running process. Therefore it is better to run ZAP in a cloud instance or in a dedicated server to avoid any interruptions.

2.2 Fine Tune ZAP Tool with Pre-Configured Policy

ZAP tool should be fine tuned before running a scan for obtaining better results. For this, you can download the WSO2 policy file for ZAP)100 which contains the settings to fine tune ZAP.

Go to Analyze --> Scan Policy Manager in ZAP.

figure1

Figure 1

In the Scan Policy Manager window, click on Import.

figure2

Figure 2

Browse and select the WSO2 Policy file you downloaded.

figure3

Figure 3

Since the policy file is imported correctly, you can use this later when you run the spider and the scan.

2.3 Configuring ZAP Proxy to Trace Browser Traffic

Rather than providing the URL of WSO2 server and attack the URL with ZAP, it is much more effective if we record the UI actions we do on the WSO2 server and let ZAP tool capture the traffic which can then be used for performing attacks.

Go to Tools --> Options --> Local Proxy and set the hostname/ip address and the port number for the proxy. (In this example, the port is set to 7777 which is selected randomly)

figure4

Figure 4

Now ZAP tool is ready to capture the traffic going through the above set port number. Next step is to configure the browser to send traffic through this port number so ZAP tool can trace them.

In Firefox, go to Edit --> Preferences and in the Advanced options, click on Settings under the Network tab.

Select the Manual proxy configuration and set the hostname/ip and the port number.

figure5

Figure 5

If any exception for localhost, 127.0.0.1 or the hostname of the WSO2 server you are trying to scan is given in No Proxy for: text box, remove them so ZAP can detect the traffic flow of that as well.

Now, go to firefox and access the WSO2 server. You will see the SSL warning below because the traffic goes through ZAP proxy. Add an exception to the site in the browser.

figure6

Figure 6

Now the WSO2 Server URL should be opened in the browser. Also it should be listed in ZAP under the sites.

figure7

Figure 7

Select the Mode of the scan as Protected Mode. With this, you can choose which sites to be used for attacking. (For example, if you are trying out the Federated Authentication scenario with WSO2 Identity Server and Facebook, under the Sites in ZAP tool, facebook website also will be listed to be attacked. In the scope of scanning, external websites should be removed. With the Protected Mode, you have the capability to select the Sites that should be attacked by ZAP tool)

3. Scanning Process

3.1 Excluding the Server Logout from Spider

When we run the ZAP scan with an active user session, if ZAP executes the action to logout from the server in the middle of the scan, the actions that should be performed with a logged in user session would not be performed after that (because active session is removed with logout action ). In order to avoid that, we need to exclude the logout action from the Spider.

For that, first login to the WSO2 Server and then logout so that the logout action is traced by ZAP. Then, find the GET:logout_action.jsp and right click on it and exclude from Spider.

The logout action is listed under <server_url> --> carbon --> admin in the Sites tree.

figure8

Figure 8

Then in the Session Properties window, it will show the URL regex that ZAP is going to exclude in the spider. Click OK.

figure9

Figure 9

3.2 Perform UI Actions Manually (or using Selenium)

With the above steps, ZAP is tracing the sites that you visit in the browser. Next step is to manually perform UI actions in the browser so that ZAP traces all the actions which we can then use for attacking.

This is helpful for testing a specific feature. For example, if we want to identify the possible issues in the user creation flow, we can create a new user in Management Console while ZAP traces all actions through the proxy.

figure10

Figure 10

On the URLs ZAP discovered, it can perform attacks to find possible issues. We can improve the coverage of the scan by manually performing all actions in the browser (i.e in Management Console) so that ZAP discovers each flow that it can try attacking.

You can also automate this by having selenium scripts for all UI actions. After setting the ZAP to act as the proxy, you can run selenium scripts so that in the browser, it automatically plays the actions you would do manually. For every action, ZAP will discover the URLs.

3.3 Removing Unnecessary Sites from Scan

When the ZAP is acting as the proxy, all the URLs that the browser calls will be traced under the Sites in ZAP. We need to remove external websites and select only the WSO2 Server's scope for scan.

Right click on the Site that should be included in the scan and select Include in Context -> New Context.

figure11

Figure 11

Then it will show the Session Properties window with the regex for including the URL patterns in the scan. Provide a name for the context so that we can identify the site uniquely when we have multiple sites added as different contexts. (For example when you are testing a product like WSO2 API Manager, the Management Console, API Store and API Publisher can be three major sites where you can add each as a different context)

figure12

Figure 12

Once the site is added to the Context for scan, the icon image for each entry under the tree of the site will be changed showing that it is added to the context.

figure13

Figure 13

Under the Sites list, you can click on the Show all URLs button to view only the sites that are added to the contexts. All other sites will be hidden from the Sites panel with this option.

figure14

Figure 14

Once you have added the site/s to the context, you can filter the scan results (after running a scan) easily. In the History tab, Show only URLs in Scope filters the results and shows only the URLs that belong to the context.

figure15

Figure 15

When searching also you can search URLs that belong only to the context with the Search all URLs option enabled.

figure16

Figure 16

You can also filter the alerts that belong only the contexts you have added with the Show all URLs option enabled.

figure17

Figure 17

3.4 Globally Excluding URL Patterns

When the ZAP tool starts crawling the site, it will increase the network traffic heavily. We can reduce the traffic by excluding the URL patterns globally so that ZAP will ignore such URLs when crawling. For example if we exclude URLs of .mp4 video files, ZAP tool will not download mp4 video files which saves network bandwidth.

Go to Tools -> Options and select Global Exclude URL option. By default there are some patterns already added to ZAP. You can select all of them for exclusion. Additionally, if there is any URL pattern you need to exclude, you can add the regex for the URL as a new entry.

figure18

Figure 18

3.5 Creating the Logged in User Session

When running the spider to crawl the site, we have to let ZAP discover the URLs that are accessible only by logged in users as well. For that, we need to create a logged in user session in ZAP so that same as a logged in user can browse the URLs in web browser, ZAP will be able to crawl through those URLs.

Click on Show All Tabs in the toolbox so that it will display all the tabs.

figure19

Figure 19

Go to the Http Sessions tab. If there are already created sessions listed, you can remove them.

figure20

Figure 20

Now while ZAP proxy is tracing the traffic, go to the browser and login to the site you need to scan. (When ZAP performs the scan, it will attack to the URLs with the associated privileges of the user you logged in). Once you login, the session ID should be listed in the HTTP Sessions tab. Right click on the session and Set as Active.

figure21

Figure 21

Above should be done only if the authentication to the site is tracked via the JSESSIONID. In a scenario like Single Sign On ( i. e when testing Identity Server Dashboard), the session is maintained using the commonAuthId cookie. In such case, go to the Params tab, right click on the JSESSIONID and select Unflag as session Token.

figure22

Figure 22

After that, right click on commonAuthId and Flag as Session Token. With this ZAP will take commonAuthId value for maintaining the session.

figure23

Figure 23

In both cases above, the browser should have an active user (logged in) session with the particular session value traced in ZAP which should then be flagged as the session token.

3.6 Configuring and Running AJAX Spider

When you have multiple sites added to the context and when you need all the sites to have the same configuration, you can set them globally. Go to Tools -> Options and select AJAX Spider.

Set the maximum crawl depth, maximum crawl states and maximum duration to 0 so that the AJAX Spider will go on crawling completely without any limitation.

figure24

Figure 24

You can choose the browser to be used for crawling by the AJAX spider. If your browser is not listed in the dropdown, go to Tools -> Options and in the Selenium option, browse and provide the selenium driver for the particular browser. (You can download the selenium driver for the particular web browser from internet). Once you have provided the driver, in AJAX Spider configuration's browser dropdown the browser will be listed.

figure25

Figure 25

If you have multiple sites added to the context, but need to have separate ajax spider configuration for a particular site, you cannot use global settings. In such case, right click on the particular site and go to Attack -> AJAX Spider.

Also you need to select Protected Mode (from the dropdown in toolbox) for running the AJAX spider so that it will crawl through the sites added to the context and will skip any URL that is out of scope.

figure26

Figure 26

Select Show advanced options in the Scope tab which will make the Options tab visible.

figure27

Figure 27

In the Options tab of AJAX Spider, you can set the configuration specific to this particular site.

figure28

Figure 28

Once the configuration is set, you can Start Scan.

Before starting the scan, you need to make sure that you have an active user session set in ZAP (Follow the steps in 1.8 Creating the Logged in User Session section) so that AJAX spider can crawl URLs that are accessible by the logged in user.

3.7 Running Spider

The global configuration for Spider is in Tools -> Options under Spider option which is applicable to all the sites added to the context.

You can set the maximum depth to crawl to 5. At this point we have already run the AJAX Spider and discovered most of the URLs with crawling. Therefore crawling more up to a depth of 5 levels would provide sufficient coverage.

figure29

Figure 29

When you have multiple sites added to the context and need to have separate Spider configuration for a particular site, you cannot use Global Settings. In such case, you can right click on the site and go to Attack -> Spider.

figure30

Figure 30

In the Spider window, select Show Advanced options and go to the Advanced tab.

figure31

Figure 31

Since we have run the AJAX Spider previously, it should have crawled most of the URLs of the server. Therefore having only 5 as the maximum depth to crawl would be sufficient to complete crawling and covering the URLs of the server.

figure32

Figure 32

With above configuration, start the scan to complete URL discovery.

3.8 Removing False Positives before Scanning

Before running the Active Scan, we can configure the Session Properties such that when reporting alerts, it would avoid known false positive URLs.

Go to File → Session Properties and under the particular context, select Alert Filters.

figure33

Figure 33

The click on Add button to define the URLs that we have already identified to be reporting false positive alerts.

figure34

Figure 34

In the Add Alert Filter window, select the type of Alert and set it as a False Positive in the New Risk Level dropdown. If the URL is a direct URL, it can be given in the URL textbox. If there are multiple URLs following the same pattern, select the URL is Regex? checkbox and define the regular expression for the URL. Finally Confirm the alert filter.

The alerts generated for these URLs would be ignored by ZAP during the scanning time and also would not appear in the identified security vulnerabilities.

3.9 Running Active Scan

When you have multiple sites in the context and need to have similar active scan configuration for all the sites, you can use the global settings. Go to Tools -> Options and select Active Scan.

As the default active scan policy and attack mode scan policy, select the WSO2Policy file which you imported in section 1.2 Fine Tune ZAP Tool with Pre-Configured Policy.

figure35

Figure 35

When you have multiple sites added to the context, but need to have specific active scan configuration for a particular site, you can right click on the particular site and go to Attack -> Active Scan.

figure36

Figure 36

It will show the Active Scan window. Select Show advanced options and go to Policy tab.

figure37

Figure 37

As the Policy, select the WSO2Policy file which you imported previously.

figure38

Figure 38

Finally start the scan. Note that you need to have a logged in user session when running the active scan. (follow the steps in 1.8 Creating the Logged in User Session)

Then the scan will begin and you can see the progress in Active Scan tab.

figure39

Figure 39

4. Report Generation

4.1 Removing False Positives Before Report Generation

Once the Active Scan is completed and the alerts are generated, if there are false positive alerts, they can be removed appearing in the reports generated. For that, go to the Alerts tab and double click on the particular alert that should be marked as a false positive.

figure40

Figure 40

Then the Edit Alert window will appear. In the Confidence dropdown, select False Positive.

figure41

Figure 41

4.2 Generating Reports

Once the Active Scan is complete, you can generate the reports for exporting the results of the scan. Go to Report -> Generate HTML Report from the menu.

figure42

Figure 42

Then it will prompt where to save the report. Once you provide a file path, it will export the ZAP scan report. By examining the report, you will be able to identify possible security threats and get them fixed.

figure43

Figure 43

5. Scanning RESTful APIs with ZAP

WSO2 products have APIs that are secured with OAuth. In order to consume such APIs, we need to provide an access token in the request. When ZAP is scanning the URLs, since the access token is not passed when making the requests, it will fail to scan the APIs completely. In such cases, we can manually test the APIs using a REST client which can be configured to send traffic through a proxy. Here we can configure the proxy host and port to the same host and port that ZAP is running so that ZAP can trace and record traffic for all the API requests.

OAuth access tokens have a fixed expiration time, which is set to 60 minutes by default in WSO2 products (i.e Identity Server). This expiration time is not sufficient when running the active scan. Therefore you have to increase the token expiration time in
<AccessTokenDefaultValidityPeriod> element in
<CARBON_HOME>/repository/conf/identity.xml file.

  1. Configure the proxies in the browser as you do in the usual scenario, since REST client runs on top of the browser ZAP is intelligent enough to identify the process.
  2. Configure the authentication headers in REST client.
figure44

Figure 44

Now run the requests in the REST client, ZAP can now scan as it does for the web UI. You will be able to do the active scan, spider and ajax spider after that as you do in the usual way.

If you see any certificate based errors like below in your REST client, allow the certificate when pops up when trying to login to the carbon console (In firefox you won't get any response).

figure45

Figure 45

E. WSO2 Engineering Guidelines - Static Code Analysis using FindSecurityBugs

Version: 1.2 | Date: 28th June 2016

Revision History

Version Release Date Contributors / Authors Summary of Changes
1.0 28th June 2016 Tharindu Edirisinghe Initial version
1.1 16th December 2016 Prakhash Sivakumar Minor wording correction
1.2 25th April 2017 Ayoma Wijethunga Formatting Changes

Formatting Instructions

Please refer to the "Formatting Instructions" section of "Secure Coding Guidelines - 2nd Edition" document101.

1. Introduction

This document provides details of all necessary steps for configuring FindBugs102 and Find Security Bugs103 for scanning source code in order to discover security threats.

2. Installation - IntelliJ Idea - FindBugs Plugin

Once you open IntelliJ IDEA, you can go to Configure -> Plugins in the opening window.

figure1

Figure 1

If you have already opened a project in IntelliJ IDEA, you can go to File -> Settings and in the left panel of the Settings window, select Plugins.

figure2

Figure 2

You can install the FindBugs plugin in two ways. If you have an internet connection, you can click on Browser repositories button and get the plugin installed. If not you can download the FindBugs plugin for IntelliJ IDEA104 and go with Install plugin from disk option where you can browse and provide the already downloaded plugin.

When you go with Browse repositories option, you can search for the findbugs plugin and select FindBugs-IDEA and get it installed.

figure3

Figure 3

3. Installation - IntelliJ Idea - Find Security Bug Plugin

Once you have installed the FindBugs plugin in IntelliJ IDEA, in the bottom of the IDE you will see the FindBugs-IDEA button. Upon clicking on it you can see all the settings of it in a panel.

figure4

Figure 4

Now we have to enable the FindSecurityBugs plugin which comes with FindBugs. This is for finding the security bugs in your code. Click on Plugin Preferences button.

figure5

Figure 5

Under the Plugins section of the General tab, click on the + button and select Add Find Security Bugs.

figure6

Figure 6

Once the FindSecurityBugs plugin is added, click on Apply and then OK.

figure7

Figure 7

Now we have successfully installed FindBugs plugin in IntelliJ IDEA and also have enabled the FindSecurityBugs plugin in it. Let's perform a static code analysis and get to know all the bugs we have in the code.

4. Code Analysis

To analyze the project, right click on the project and go to FindBugs -> Analyze Scope Files. With this, the scanning will happen only under the selected folder. You can also go with Analyze Module Files which would scan the particular module you have selected and also Analyze Project Files which would scan the entire project.

figure8

Figure 8

Once the static scan is completed, you can see the identified bugs in FindBugs-IDEA panel. Since we have enabled the FindSecurityBugs plugin, it will list all the identified security issues under the Security category.

figure9

Figure 9

5. Report Generation

You can export the reported bugs for further analysis. For that, click on the Export Bug Collection to XML/HTML button.

figure10

Figure 10

A generated report would look like below.

figure11

Figure 11

In you go to the Security Warnings section, you can see a detailed explanation for each identified security issue.

figure12

Figure 12

For more details about our solutions or to discuss a specific requirement contact us.

x

Interested in similar content?