Java
Recommendations for OWASP TOP 10 2017
A1- Injection
SQL Injection
- Use of Stored Procedures
// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// Result set handling...
} catch (SQLException se) {
// Logging and error handling...
}
- Whitelist Input Validation
- Escaping Wildcard characters in Like Clauses
- Escaping All User Supplied Input
Noncompliant Code
String query = "SELECT user_id FROM user_data WHERE user_name = '"
+ req.getParameter("userID")
+ "' and user_password = '" + req.getParameter("pwd") +"'";
try {
Statement statement = connection.createStatement( … );
ResultSet results = statement.executeQuery( query );
}
Compliant Code
Codec ORACLE_CODEC = new OracleCodec();
String query = "SELECT user_id FROM user_data WHERE user_name = '"
+ ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("userID"))
+ "' and user_password = '"
+ ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("pwd")) +"'";
- Enforcing Least Privilege
- Performing Whitelist Input Validation as a Secondary Defense
- Use of Prepared Statements (with Parameterized Queries)
Noncompliant Code
String query = "SELECT * FROM users WHERE username = '"+ username +"' AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
Compliant Code
String query = "select * from db_user where username=? and password=?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, username);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();
HQL Injection
- Use of Prepared Statements (with Parameterized Queries)
Noncompliant Code
Query unsafeHQLQuery = session.createQuery("from Inventory where productID='"+userSuppliedParameter+"'");
Compliant Code
Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);
OS Command Injection
- Avoid calling OS commands directly
Noncompliant Code
ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");
Compliant Code
ProcessBuilder pb = new ProcessBuilder("TrustedCmd", "TrustedArg1", "TrustedArg2");
Map<String, String> env = pb.environment();
pb.directory(new File("TrustedDir"));
Process p = pb.start();
- Escape values added to OS commands specific to each OS
- Parameterization in conjunction with Input Validation
- Use structured mechanisms that automatically enforce the separation between data and command.
Noncompliant Code
String folder = request.getParameter("folder");
String cmd = "mkdir " + folder;
Runtime.getRuntime().exec(cmd);
Compliant Code
String folder = request.getParameter("folder");
String cmd = "mkdir " + ESAPI.encoder().encodeForOS(new WindowsCodec(), folder);
Runtime.getRuntime().exec(cmd);
LDAP Injection
- Escape all variables using the right LDAP encoding function
public String escapeDN (String name) {
//From RFC 2253 and the / character for JNDI
final char[] META_CHARS = {'+', '"', '<', '>', ';', '/'};
String escapedStr = new String(name);
//Backslash is both a Java and an LDAP escape character,
//so escape it first
escapedStr = escapedStr.replaceAll("\\\\\\\\","\\\\\\\\");
//Positional characters - see RFC 2253
escapedStr = escapedStr.replaceAll("\^#","\\\\\\\\#");
escapedStr = escapedStr.replaceAll("\^ | $","\\\\\\\\ ");
for (int i=0 ; i < META_CHARS.length ; i++) {
escapedStr = escapedStr.replaceAll("\\\\" +
META_CHARS[i],"\\\\\\\\" + META_CHARS[i]);
}
return escapedStr;
}
Note, that the backslash character is a Java String literal and a regular expression escape character.
public String escapeSearchFilter (String filter) {
//From RFC 2254
String escapedStr = new String(filter);
escapedStr = escapedStr.replaceAll("\\\\\\\\","\\\\\\\\5c");
escapedStr = escapedStr.replaceAll("\\\\\*","\\\\\\\\2a");
escapedStr = escapedStr.replaceAll("\\\\(","\\\\\\\\28");
escapedStr = escapedStr.replaceAll("\\\\)","\\\\\\\\29");
escapedStr = escapedStr.replaceAll("\\\\" +
Character.toString('\\u0000'), "\\\\\\\\00");
return escapedStr;
}
- Use Frameworks that Automatically Protect from LDAP Injection
- Minimize the privileges assigned to the LDAP binding account in your environment.
- Input validation can be used to detect unauthorized input before it is passed to the LDAP query.
Noncompliant Code
String filter = "(&(uid=" + user + ")(userPassword=" + pass + "))";
NamingEnumeration<SearchResult> results = ctx.search("ou=system", filter, new SearchControls());
return results.hasMore();
Compliant Code
String filter = "(&(uid={0})(userPassword={1}))";
NamingEnumeration<SearchResult> results = ctx.search("ou=system", filter, new String[]{user, pass}, new SearchControls();
return results.hasMore();
Code Injection
Noncompliant Code
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.eval("print('"+ firstName + "')");
Compliant Code
// Whitelisting
if (!firstName.matches("[\\w]*")) {
throw new IllegalArgumentException();
}
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.eval("print('"+ firstName + "')");
Log Injection
- Filter the user input used to prevent injection of Carriage Return (CR) or Line Feed (LF) characters.
- Limit the size of the user input value used to create the log message.
- Make sure all XSS defenses are applied when viewing log files in a web browser.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
...
Logger logger = LogManager.getLogger(MyClass.class);
logger.info(logMessage);
...
NoSQL Injection
- Do not use string concatenation to build API call expression but use the API to create the expression.
Example: MongoDB
String userInput = "Brooklyn";
ArrayList<String> specialCharsList = new ArrayList<String>() { {
add("'");
add("\"");
add("\\");
add(";");
add("{");
add("}");
add("$");
} };
specialCharsList.forEach(specChar -> Assert.assertFalse(userInput.contains(specChar)));
Assert.assertTrue(userInput.length() <= 50);
try(MongoClient mongoClient = new MongoClient()){
MongoDatabase db = mongoClient.getDatabase("test");
Bson expression = eq("borough", userInput);
FindIterable<org.bson.Document> restaurants = db.getCollection("restaurants").find(expression);
restaurants.forEach(new Block<org.bson.Document>() {
@Override
public void apply(final org.bson.Document doc) {
String restBorough = (String)doc.get("borough");
Assert.assertTrue("Brooklyn".equals(restBorough));
}
});
}
HTML/JavaScript/CSS Injection
- Either apply strict input validation (whitelist approach)
- Use output sanitizing+escaping if input validation is not possible (combine both every time is possible).
Example:
String userInput = "You user login is owasp-user01";
Assert.assertTrue(Pattern.matches("[a-zA-Z0-9\\s\\-]{1,50}", userInput));
Assert.assertEquals(0, StringUtils.countMatches(userInput.replace(" ", ""), "--"));
String outputToUser = "You <p>user login</p> is <strong>owasp-user01</strong>";
outputToUser += "<script>alert(22);</script><img src='#' onload='javascript:alert(23);'>";
PolicyFactory policy = new HtmlPolicyBuilder().allowElements("p", "strong").toFactory();
String safeOutput = policy.sanitize(outputToUser);
safeOutput = Encode.forHtml(safeOutput);
String finalSafeOutputExpected = "You <p>user login</p> is <strong>owasp-user01</strong>";
Assert.assertEquals(finalSafeOutputExpected, safeOutput);
Server Side Request Forgery(SSRF)
- Use a whitelist of allowed domains, resources and protocols from where the web server can fetch resources.
- Any input accepted from the user should be validated and rejected if it does not match the positive specification expected.
- If possible, do not accept user input in functions that control where the web server can fetch resources.
- Disable unused URL schemas
** Noncompliant Code **
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
URL url = new URL(req.getParameter("url"));
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // Noncompliant
}
** Compliant Code **
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String urlWhiteListed = "https://example.com/";
String str = req.getParameter("url");
if (!str.startsWith(urlWhiteListed))
throw new IOException();
URL url2 = new URL(str);
HttpURLConnection conn2 = (HttpURLConnection) url2.openConnection(); // compliant
}
A2 - Broken Authentication
- Where possible, implement multi-factor authentication to prevent automated, credential stuffing, brute force, and stolen credential re-use attacks.
- Ensure registration, credential recovery, and API pathways are hardened against account enumeration attacks by using the same messages for all outcomes.
- Limit or increasingly delay failed login attempts. Log all failures and alert administrators when credential stuffing, brute force, or other attacks are detected.
- Use a server-side, secure, built-in session manager that generates a new random session ID with high entropy after login. Session IDs should not be in the URL, be securely stored and invalidated after logout, idle, and absolute timeouts.
Noncompliant Code
This example violates the principle of least privilege because an unprivileged caller could also cause the authentication library to be loaded. An unprivileged caller cannot invoke the System.loadLibrary() method directly because this could expose native methods to the unprivileged code
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
String passwordFile = System.getProperty("user.dir") + File.separator + "PasswordFileName";
f[0] = new FileInputStream(passwordFile);
// Check whether oldPassword matches the one in the file
// If not, throw an exception
System.loadLibrary("authentication");
} catch (FileNotFoundException cnf) {
// Forward to handler
}
return null;
}
}); // End of doPrivileged()
Compliant Code
This compliant solution moves the call to System.loadLibrary() outside the doPrivileged() block. Doing so allows unprivileged code to perform preliminary password reset checks using the file but prevents it from loading the authentication library.
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
String passwordFile = System.getProperty("user.dir") + File.separator + "PasswordFileName";
f[0] = new FileInputStream(passwordFile);
// Check whether oldPassword matches the one in the file
// If not, throw an exception
} catch (FileNotFoundException cnf) {
// Forward to handler
}
return null;
}
}); // End of doPrivileged()
System.loadLibrary("authentication");
Authentication Solution and Sensitive Accounts
- Do NOT allow login with sensitive accounts (i.e. accounts that can be used internally within the solution such as to a back-end / middle-ware / DB) to any front-end user-interface
- Do NOT use the same authentication solution (e.g. IDP / AD) used internally for unsecured access (e.g. public access / DMZ)
Implement Proper Password Strength Controls
- Minimum length of the passwords should be enforced by the application. Passwords shorter than 8 characters are considered to be weak
- Maximum password length should not be set too low, as it will prevent users from creating passphrases. A common maximum length is 64 characters due to limitations in certain hashing algorithms.
- Allow usage of all characters including unicode and whitespace. There should be no password composition rules limiting the type of characters permitted.
- Implement weak-password checks, such as testing new or changed passwords against a list of the top 10000 worst passwords.
- Ensure credential rotation when a password leak, or at the time of compromise identification.
- Include password strength meter to help users create a more complex password and block common and previously breached passwords
- Align password length, complexity and rotation policies with NIST 800-63 B’s guidelines in section 5.1.1 for Memorized Secrets or other modern, evidence based password policies.
- Do not ship or deploy with any default credentials, particularly for admin users.
Compare Password Hashes Using Safe Functions
Ensure that the comparison function:
- Has a maximum input length, to protect against denial of service attacks with very long inputs.
- Explicitly sets the type of both variable, to protect against type confusion attacks.
- Returns in constant time, to protect against timing attacks.
Authentication and Error Messages
- The application may should different HTTP Error code depending on the authentication attempt response.
- Do not return different error messages.
Authentication Responses
Using any of the authentication mechanisms (login, registration, password reset or password recovery), an application must respond with a generic error message regardless of whether:
- The user ID or password was incorrect.
- The account does not exist.
- The account is locked or disabled.
Password Managers
Web applications should at least not make password managers job more difficult than necessary by observing the following recommendations:
- Use standard HTML forms for username and password input with appropriate type attributes.
- Avoid plugin-based login pages (such as Flash or Silverlight).
- Allow any printable characters to be used in passwords.
- Allow users to paste into the username and password fields.
- Allow users to navigate between the username and password field with a single press of the Tab key.
Forgot Password Services
- Return a consistent message for both existent and non-existent accounts.
- Ensure that the time taken for the user response message is uniform.
- Use a side-channel to communicate the method to reset their password.
- Use URL tokens for the simplest and fastest implementation.
- Ensure that generated tokens or codes are:
- Randomly genererated using a cryptographically safe algorithm.
- Sufficiently long to protect against brute-force attacks.
- Stored securely.
- Single use and expire after an appropriate period.
Session management
- Force Session Logout On Web Browser Window Close Events
- Disable Web Browser Cross-Tab Sessions
- Renew the Session ID After Any Privilege Level Change
- All sessions should implement an idle or inactivity timeout.
- Session IDs are vulnerable to hijacking when they are passed across the network with http requests. As with hashed passwords it’s essential that session ID tokens are protected with SSL to ensure they remain secure.
- Session IDs should never be under the control of users and clients to create. They should all be generated, controlled, and secured centrally by the authentication and authorisation mechanism.
- Session IDs should not be stored between browser sessions. They should be destroyed when a browser is closed. This prevents an attacker retrieving a valid session by using the history in a Browser or by finding the Session ID on the client’s local storage.
A3 - Sensitive Data Exposure
- Classify data processed, stored or transmitted by an application. Identify which data is sensitive according to privacy laws, regulatory requirements, or business needs.
- Apply controls as per the classification.
- Don’t store sensitive data unnecessarily. Discard it as soon as possible or use PCI DSS compliant tokenization or even truncation. Data that is not retained cannot be stolen.
- Make sure to encrypt all sensitive data at rest.
- Ensure up-to-date and strong standard algorithms, protocols, and keys are in place; use proper key management.
- Encrypt all data in transit with secure protocols such as TLS with perfect forward secrecy (PFS) ciphers, cipher prioritization by the server, and secure parameters. Enforce encryption using directives like HTTP Strict Transport Security (HSTS).
- Disable caching for response that contain sensitive data.
- Store passwords using strong adaptive and salted hashing functions with a work factor (delay factor), such as Argon2, scrypt, bcrypt or PBKDF2.
- Verify independently the effectiveness of configuration and settings.
Unrestricted File Upload
- The file types allowed to be uploaded should be restricted to only those that are necessary for business functionality.
- Never accept a filename and its extension directly without having an allow list filter.
- The application should perform filtering and content checking on any files which are uploaded to the server. Files should be thoroughly scanned and validated before being made available to other users. If in doubt, the file should be discarded.
- It is necessary to have a list of only permitted extensions on the web application. And, file extension can be selected from the list.
- All the control characters and Unicode ones should be removed from the filenames and their extensions without any exception. Also, the special characters such as “;”, “:”, “>”, “<”, “/” ,”\”, additional “.”, “*”, “%”, “$”, and so on should be discarded as well. If it is applicable and there is no need to have Unicode characters, it is highly recommended to only accept Alpha-Numeric characters and only 1 dot as an input for the file name and the extension; in which the file name and also the extension should not be empty at all (regular expression: [a-zA-Z0-9]{1,200}.[a-zA-Z0-9]{1,10}).
- Limit the filename length. For instance, the maximum length of the name of a file plus its extension should be less than 255 characters (without any directory) in an NTFS partition.
- It is recommended to use an algorithm to determine the filenames. For instance, a filename can be a MD5 hash of the name of file plus the date of the day.
- Uploaded directory should not have any “execute” permission and all the script handlers should be removed from these directories.
- Limit the file size to a maximum value in order to prevent denial of service attacks (on file space or other web application’s functions such as the image resizer).
- Restrict small size files as they can lead to denial of service attacks. So, the minimum size of files should be considered.
- Use Cross Site Request Forgery protection methods.
- Prevent from overwriting a file in case of having the same hash for both.
- Use a virus scanner on the server (if it is applicable). Or, if the contents of files are not confidential, a free virus scanner website can be used. In this case, file should be stored with a random name and without any extension on the server first, and after the virus checking (uploading to a free virus scanner website and getting back the result), it can be renamed to its specific name and extension.
- Try to use POST method instead of PUT (or GET!)
- Log users’ activities. However, the logging mechanism should be secured against log forgery and code injection itself.
- In case of having compressed file extract functions, contents of the compressed file should be checked one by one as a new file.
- If it is possible, consider saving the files in a database rather than on the filesystem.
- If files should be saved in a filesystem, consider using an isolated server with a different domain to serve the uploaded files.
- File uploaders should be only accessible to authenticated and authorised users if possible.
- Write permission should be removed from files and folders other than the upload folders.
- Ensure that configuration files such as “.htaccess” or “web.config” cannot be replaced using file uploaders. Ensure that appropriate settings are available to ignore the “.htaccess” or “web.config” files if uploaded in the upload directories.
- Ensure that files with double extensions (e.g. “file.jsp.txt”) cannot be executed especially in Apache.
- Ensure that uploaded files cannot be accessed by unauthorised users.
- Adding the
“Content-Disposition: Attachment”
and“X-Content-Type-Options: nosniff”
headers to the response of static files will secure the website against Flash or PDF-based cross-site content-hijacking attacks. It is recommended that this practice be performed for all of the files that users need to download in all the modules that deal with a file download. Although this method does not fully secure the website against attacks using Silverlight or similar objects, it can mitigate the risk of using Adobe Flash and PDF objects, especially when uploading PDF files is permitted. - CORS headers should be reviewed to only be enabled for static or publicly accessible data. Otherwise, the “Access-Control-Allow-Origin” header should only contain authorised addresses. Other CORS headers such as “Access-Control-Allow-Credentials” should only be used when they are required. Items within the CORS headers such as “Access-Control-Allow-Methods” or “Access-Control-Allow-Headers” should be reviewed and removed if they are not required.
Noncompliant Code
public class UploadAction extends ActionSupport {
private File uploadedFile;
// setter and getter for uploadedFile
public String execute() {
try {
// File path and file name are hardcoded for illustration
File fileToCreate = new File("filepath", "filename");
// Copy temporary file content to this file
FileUtils.copyFile(uploadedFile, fileToCreate);
return "SUCCESS";
} catch (Throwable e) {
addActionError(e.getMessage());
return "ERROR";
}
}
}
Compliant Code
public class UploadAction extends ActionSupport {
private File uploadedFile;
// setter and getter for uploadedFile
public String execute() {
try {
// File path and file name are hardcoded for illustration
File fileToCreate = new File("filepath", "filename");
boolean textPlain = checkMetaData(uploadedFile, "text/plain");
boolean img = checkMetaData(uploadedFile, "image/JPEG");
boolean textHtml = checkMetaData(uploadedFile, "text/html");
if (!textPlain && !img && !textHtml) {
return "ERROR";
}
// Copy temporary file content to this file
FileUtils.copyFile(uploadedFile, fileToCreate);
return "SUCCESS";
} catch (Throwable e) {
addActionError(e.getMessage());
return "ERROR";
}
}
public static boolean checkMetaData(
File f, String getContentType) {
try (InputStream is = new FileInputStream(f)) {
ContentHandler contenthandler = new BodyContentHandler();
Metadata metadata = new Metadata();
metadata.set(Metadata.RESOURCE_NAME_KEY, f.getName());
Parser parser = new AutoDetectParser();
try {
parser.parse(is, contenthandler, metadata, new ParseContext());
} catch (SAXException | TikaException e) {
// Handle error
return false;
}
if (metadata.get(Metadata.CONTENT_TYPE).equalsIgnoreCase(getContentType)) {
return true;
} else {
return false;
}
} catch (IOException e) {
// Handle error
return false;
}
}
}
A4 - XML External Entities (XXE)
- Whenever possible, use less complex data formats such as JSON, and avoiding serialization of sensitive data.
- Patch or upgrade all XML processors and libraries in use by the application or on the underlying operating system. Use dependency checkers. Update SOAP to SOAP 1.2 or higher.
- Disable XML external entity and DTD processing in all XML parsers in the application.
- Implement positive (“whitelisting”) server-side input validation, filtering, or sanitization to prevent hostile data within XML documents, headers, or nodes.
- Verify that XML or XSL file upload functionality validates incoming XML using XSD validation or similar.
- SAST tools can help detect XXE in source code, although manual code review is the best alternative in large, complex applications with many integrations.
XXE attacks
Noncompliant Code
class XXE {
private static void receiveXMLStream(InputStream inStream,
DefaultHandler defaultHandler)
throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(inStream, defaultHandler);
}
public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException {
try {
receiveXMLStream(new FileInputStream("evil.xml"), new DefaultHandler());
} catch (java.net.MalformedURLException mue) {
System.err.println("Malformed URL Exception: " + mue);
}
}
}
Compliant Code
class CustomResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
// Check for known good entities
String entityPath = "file:/Users/onlinestore/good.xml";
if (systemId.equals(entityPath)) {
System.out.println("Resolving entity: " + publicId + " " + systemId);
return new InputSource(entityPath);
} else {
// Disallow unknown entities by returning a blank path
return new InputSource();
}
}
}
XPath Injection
- Treat all user input as untrusted, and perform appropriate sanitization.
- When sanitizing user input, verify the correctness of the data type, length, format, and content. For example, use a regular expression that checks for XML tags and special characters in user input. This practice corresponds to input sanitization. See IDS52-J. Prevent code injection for additional details.
- In a client-server application, perform validation at both the client and the server sides.
- Extensively test applications that supply, propagate, or accept user input.
Noncompliant Code
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//users/user[username/text()='" + userName + "' and password/text()='" + pwd + "' ]");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
Compliant Code
XQuery xquery = new XQueryFactory().createXQuery(new File("login.xq"));
Map queryVars = new HashMap();
queryVars.put("userName", userName);
queryVars.put("password", pwd);
NodeList nodes = xquery.execute(doc, null, queryVars).toNodes();
XML Injection
Noncompliant Code
public class OnlineStore {
private static void createXMLStreamBad(final BufferedOutputStream outStream,
final String quantity) throws IOException {
String xmlString = "<item>\n<description>Widget</description>\n"
+ "<price>500</price>\n" + "<quantity>" + quantity
+ "</quantity></item>";
outStream.write(xmlString.getBytes());
outStream.flush();
}
}
Compliant Code
public class OnlineStore {
private static void createXMLStream(final BufferedOutputStream outStream,
final String quantity) throws IOException, NumberFormatException {
// Write XML string only if quantity is an unsigned integer (count).
int count = Integer.parseUnsignedInt(quantity);
String xmlString = "<item>\n<description>Widget</description>\n"
+ "<price>500</price>\n" + "<quantity>" + count + "</quantity></item>";
outStream.write(xmlString.getBytes());
outStream.flush();
}
}
A5 - Broken Access Control
Access control is only effective if enforced in trusted server-side code or server-less API, where the attacker cannot modify the access control check or metadata.
- With the exception of public resources, deny by default.
- Implement access control mechanisms once and re-use them throughout the application, including minimizing CORS usage.
- Model access controls should enforce record ownership, rather than accepting that the user can create, read, update, or delete any record.
- Unique application business limit requirements should be enforced by domain models.
- Disable web server directory listing and ensure file metadata (e.g. .git) and backup files are not present within web roots.
- Log access control failures, alert admins when appropriate (e.g. repeated failures).
- Rate limit API and controller access to minimize the harm from automated attack tooling.
- JWT tokens should be invalidated on the server after logout.
- Developers and QA staff should include functional access control unit and integration tests.
Role-Based Access Control (RBAC)
- Roles must be only be transferred or delegated using strict sign-offs and procedures.
- When a user changes their role to another one, the administrator must make sure that the earlier access is revoked such that at any given point of time, a user is assigned to only those roles on a need to know basis.
- Assurance for RBAC must be carried out using strict access control reviews.
Discretionary Access Control (DAC)
- Assurance while granting trusts to different user roles.
- Assurance for DAC must be carried out using strict access control reviews.
Mandatory Access Control (MAC)
- Classification and sensitivity assignment at an appropriate and pragmatic level
- Assurance for MAC must be carried out to ensure that the classification of the objects is at the appropriate level.
Permission Based Access Control
- Access decisions should be made by checking if the current user has the permission associated with the requested application action.
- The has relationship between the user and permission may be satisfied by creating a direct relationship between the user and permission (called a grant), or an indirect one.
Missing Functional Level Access Controls
- The authentication mechanism should deny all access by default, and provide access to specific roles for every function.
- In a workflow based application, verify the users’ state before allowing them to access any resources.
Insecure Direct Object References
- Developers should use only one user or session for indirect object references.
- It is also recommended to check the access before using a direct object reference from an untrusted source.
A6 - Security Misconfiguration
- A repeatable hardening process that makes it fast and easy to deploy another environment that is properly locked down. Development, QA, and production environments should all be configured identically, with different credentials used in each environment. This process should be automated to minimize the effort required to setup a new secure environment.
- A minimal platform without any unnecessary features, components, documentation, and samples. Remove or do not install unused features and frameworks.
- A task to review and update the configurations appropriate to all security notes, updates and patches as part of the patch management process. In particular, review cloud storage permissions (e.g. S3 bucket permissions).
- A segmented application architecture that provides effective, secure separation between components or tenants, with segmentation, containerization, or cloud security groups (ACLs).
- Sending security directives to clients, e.g. Security Headers.
- An automated process to verify the effectiveness of the configurations and settings in all environments.
- Ensure that a strong application architecture is being adopted that provides effective, secure separation between components.
- It can also minimize the possibility of this attack by running automated scans and doing audits periodically.
Clickjacking
- Preventing the browser from loading the page in frame using the X-Frame-Options or Content Security Policy (frame-ancestors) HTTP headers.
- Preventing session cookies from being included when the page is loaded in a frame using the SameSite cookie attribute.
- Implementing JavaScript code in the page to attempt to prevent it being loaded in a frame (known as a “frame-buster”).
A7 - Cross Site Scripting (XSS)
- Implement Content Security Policy.
Content-Security-Policy: default-src: 'self'; script-src: 'self' static.domain.tld
- Never Insert Untrusted Data Except in Allowed Locations.
- HTML Escape Before Inserting Untrusted Data into HTML Element Content. Escape the following characters with HTML entity encoding to prevent switching into any execution context, such as script, style, or event handlers
& --> &
< --> <
> --> >
" --> "
' --> '
/ --> /
- Sanitize HTML Markup with a Library Designed for the Job
PolicyFactory sanitizer = Sanitizers.FORMATTING.and(Sanitizers.BLOCKS);
String cleanResults = sanitizer.sanitize("<p>Hello, <b>World!</b>");
- Use HTTPOnly cookie flag
Noncompliant Code
Cookie c = new Cookie(COOKIENAME, sensitivedata);
c.setHttpOnly(false);
Compliant Code
Cookie c = new Cookie(COOKIENAME, sensitivedata);
c.setHttpOnly(true);
- Applications that use user provided data to construct the response header should always validate the data first. Validation should be based on a whitelist.
Noncompliant Code
String value = req.getParameter("value");
resp.addHeader("X-Header", value); //
Compliant Code
String value = req.getParameter("value");
String whitelist = "safevalue1 safevalue2";
if (!whitelist.contains(value))
throw new IOException();
resp.addHeader("X-Header", value);
- Encode user provided data being reflected as output. Adjust the encoding to the output context so that, for example, HTML encoding is used for HTML content, HTML attribute encoding is used for attribute values, and JavaScript encoding is used for server-generated JavaScript.
- Example 1:
Noncompliant Code
String name = req.getParameter("name");
PrintWriter out = resp.getWriter();
out.write("Hello " + name);
Compliant Code
String name = req.getParameter("name");
String encodedName = org.owasp.encoder.Encode.forHtml(name);
PrintWriter out = resp.getWriter();
out.write("Hello " + encodedName);
- Example 2:
Noncompliant Code
String url = "https://example.com?query=" + q;
return url;
Compliant Code
String encodedUrl = "https://example.com?query=" + Base64.getUrlEncoder().encodeToString(q.getBytes());
return encodedUrl;
A8 - Insecure Deserialization
The only safe architectural pattern is not to accept serialized objects from untrusted sources or to use serialization mediums that only permit primitive data types. If that is not possible, consider one of more of the following:
- Implementing integrity checks such as digital signatures on any serialized objects to prevent hostile object creation or data tampering.
- Enforcing strict type constraints during deserialization before object creation as the code typically expects a definable set of classes. Bypasses to this technique have been demonstrated, so reliance solely on this is not advisable.
- Isolating and running code that deserializes in low privilege environments when possible.
- Log deserialization exceptions and failures, such as where the incoming type is not the expected type, or the deserialization throws exceptions.
- Restricting or monitoring incoming and outgoing network connectivity from containers or servers that deserialize.
- Monitoring deserialization, alerting if a user deserializes constantly.
- In your code, override the
ObjectInputStream#resolveClass()
method to prevent arbitrary classes from being deserialized. This safe behavior can be wrapped in a library like SerialKiller. - Use a safe replacement for the generic
readObject()
method as shown below. Note that this addresses “billion laughs” type attacks by checking input length and number of objects deserialized.
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
- Harden Your Own java.io.ObjectInputStream
public class LookAheadObjectInputStream extends ObjectInputStream {
public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}
A9 - Using Components with known vulnerabilities
There should be a patch management process in place to:
- Remove unused dependencies, unnecessary features, components, files, and documentation.
- Continuously inventory the versions of both client-side and server-side components (e.g. frameworks, libraries) and their dependencies using tools like versions, DependencyCheck, retire.js, etc. Continuously monitor sources like CVE and NVD for vulnerabilities in the components. Use software composition analysis tools to automate the process. Subscribe to email alerts for security vulnerabilities related to components you use.
- Only obtain components from official sources over secure links. Prefer signed packages to reduce the chance of including a modified, malicious component.
- Monitor for libraries and components that are unmaintained or do not create security patches for older versions. If patching is not possible, consider deploying a virtual patch to monitor, detect, or protect against the discovered issue.
- Every organization must ensure that there is an ongoing plan for monitoring, triaging, and applying updates or configuration changes for the lifetime of the application or portfolio.
A10 - Insufficient Logging and Monitoring
- Ensure all login, access control failures, and server-side input validation failures can be logged with sufficient user context to identify suspicious or malicious accounts, and held for sufficient time to allow delayed forensic analysis.
- Ensure that logs are generated in a format that can be easily consumed by a centralized log management solutions.
- Ensure high-value transactions have an audit trail with integrity controls to prevent tampering or deletion, such as append-only database tables or similar.
- Establish effective monitoring and alerting such that suspicious activities are detected and responded to in a timely fashion.
- Establish or adopt an incident response and recovery plan, such as NIST 800-61 rev 2 or later. There are commercial and open source application protection frameworks such as OWASP AppSensor, web application firewalls such as ModSecurity with the OWASP ModSecurity Core Rule Set, and log correlation software with custom dashboards and alerting.
Cross Site Request Forgery (CSRF)
- Check if your framework has built-in CSRF protection and use it.
- If framework does not have built-in CSRF protection add CSRF tokens to all state changing requests (requests that cause actions on the site) and validate them on backend.
- Always use SameSite Cookie Attribute for session cookies
- Use custom request headers
- Verify the origin with standard headers
- Use double submit cookies
- Consider implementing user interaction based protection for highly sensitive operations
- Remember that any Cross-Site Scripting (XSS) can be used to defeat all CSRF mitigation techniques.
- See the OWASP XSS Prevention Cheat Sheet for detailed guidance on how to prevent XSS flaws.
- Do not use GET requests for state changing operations.
- CSRF can be avoided by creating a unique token in a hidden field which would be sent in the body of the HTTP request rather than in an URL, which is more prone to exposure.
- Forcing the user to re-authenticate or proving that they are users in order to protect CSRF. For example, CAPTCHA.
Noncompliant Code
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // It will disable the CSRF protection
}
}
Compliant Code
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable(); // By default CSRF protection is enabled
}
}
Unvalidated Redirects and Forwards
- Simply avoid using redirects and forwards.
- If used, do not allow the URL as user input for the destination.
- Where possible, have the user provide short name, ID or token which is mapped server-side to a full target URL.
- This provides the highest degree of protection against the attack tampering with the URL.
- Be careful that this doesn’t introduce an enumeration vulnerability where a user could cycle through IDs to find all possible redirect targets
- If user input can’t be avoided, ensure that the supplied value is valid, appropriate for the application, and is authorized for the user.
- Sanitize input by creating a list of trusted URLs (lists of hosts or a regex).
- This should be based on a white-list approach, rather than a blacklist.
- Force all redirects to first go through a page notifying users that they are going off of your site, with the destination clearly displayed, and have them click a link to confirm.
Noncompliant Code
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String location = req.getParameter("url");
resp.sendRedirect(location); // Noncompliant
}
Compliant Code
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String whiteList = "http://localhost/safe";
String location = req.getParameter("url");
if (!location.startsWith(whiteList))
throw new IOException();
resp.sendRedirect(location); // Compliant
}