It’s easy to think that our code is secure. Vulnerabilities or potential exploits are often the things we think about last. Most of the time, our thoughts are preoccupied with sprints, scrums, meeting notes, and whatever the latest pivots marketing got approved are.
In a world where development speed takes precedence over code security, this can be a genuine issue. A breach or a hack can cost a business big dollars, if not kill it altogether. According to IBM’s 2020 Cost of Data Breach Report, the average total cost of a breach sits at 3.86 million US dollars.
The worst part of this is that it takes an average of 280 days to identify and contain the breach. Data is digital gold, and your code is the vessel that holds it.
While Java is considered relatively safe because it is a server-side language, there are still multiple ways to attack and access things you want to remain private.
To help you get a head start on the exploits your code may develop, we’ve listed the top 10 most common Java vulnerabilities, and how you can (and should) prevent them.
Every application that accepts input is vulnerable to code injections. A code injection occurs when the data passed through the input causes unintended side-effects to how your program runs or returns data.
If you think about it, a form is a two-way process. Someone enters the data, the application consumes the data and returns a result. When that result is not what you expect it to be but something else, it can leave your application in a vulnerable state.
What did this little piece of code do?
Every time a user would hover over it, they would instantly retweet it. So when one of their followers was scrolling through, they might just retweet that little worm. This resulted in a chain effect of over 3000 retweets within a few minutes.
Though Twitter doesn’t only use Java in its stack, the cautionary event can be applied to protecting your input forms.
The easiest method is to apply input validation with output sanitizing and escaping. This means that any attempts at sending HTML code will be parsed or rejected, depending on what your application is doing.
An OS command injection, or also commonly known as a shell injection, is a security vulnerability that allows attackers to execute shell commands on the server that’s running your application.
PHP is often the target for command injections because it calls a sh/bash/cmd by default. Java, however, performs a fork() of the given command to create child processes and pass it the given arguments.
However, this doesn’t guarantee that your code is safe.
Your application may be segmented into different levels of legacy code stitched together to form the final product. These legacy products can act as entry ways for shell command injections.
Sometimes, you might need to issue a command line to your server – like sending a confirmation email. Rather than using Runtime.exec() to tap into a server, it’s better to use the available Java API located at javax.mail.*
Connection strings are a set of definitions that are used to connect an application to a data source. It may connect to your relational databases, LDAP directories and files.
For a database connection string injection, there are four parameters that a malicious user would need: the data source, the initial catalog, the user id, and password.
Connection string attacks happen when a bad actor gains access by injecting parameters into the connect strings using semicolons as separators.
The issue here is that some database providers have no limit cap and run on a ‘last one wins’ algorithm. This means that an attacker can run multiple connection injection strings, pollute them with duplicate parameters and the database will accept the valid combination.
As a result, the attacker ends up bypassing the normal authentication process without getting locked out.
For example, an injected connection string can look something like this:
Data Source = myDataSource; Initial Catalog = db; Integrated Security = no; User ID = myUsername; Password = XXX; Intergrated Security = true; Data Source = myDataSource; Initial Catalog = db; Integrated Security = no; User ID = myUsername; Password = XXX; Intergrated Security = no;
Once in, the malicious user can hijack the credentials and modify them to whatever they want.
An LDAP injection exploits input validations and injects executable queries. LDAP is the Lightweight Directory Access Protocol, which is an open and cross-platform protocol used for directory service authentication.
LDAP is a communication language that applications can use to access directory servers. These directory servers often store usernames, passwords, account details and other information that can be shared with other entities on the network.
LDAP injections can happen when an application inserts unsanitized inputs directly into an LDAP statement. When this occurs, the attacker can use the LDAP filter syntax causing the server to execute other queries and LDAP statements.
The easiest way to prevent LDAP injection is to ensure that LDAP special characters – ( ) ! | & * are escaped or rejected at validation.
A reflected XSS, or reflected cross site scripting, is the process of adding malicious scripts that is activated through a link. The request then sends the user to somewhere else.
For example, a reflected XSS can be embedded to blend in with the rest of the site in a user comment section. The user may click on it and end up going to a 3rd party site and then redirected back to the original site.
Whilst at the 3rd party, malicious activities such as cookie or session stealing may occur. Although it is hard to monitor reflected XSS, spam filters on links submitted can help reduce the frequency.
Resource injections occur when the attacker successfully changes the resource identifiers used by the application to perform malicious tasks. This might be changing the port number, modifying the file name, and gaining the ability to execute or access other resources.
How can this happen? Usually when the application defines the resource via user input.
For example, imagine that an attacker gains access to a shopping site via connection string injection, or has successfully stolen your user’s details via XSS. They can now modify or query details using resource injection. Your attacker can cause havoc by ordering things from the site, modifying or stealing more information about your customer without them knowing.
SQL injection is the process of injecting SQL within data requests that results in the backend application giving back confidential data or executing malicious scripting content on the database.
This can result in a complete compromise on the host, access to data and breaches of privacy. Not only this, SQL injection can result in data loss or corruption, and potentially lock you out of your own database. When this happens, the injection has fully taken control.
The easiest fix to this is to ensure that you validate on the server-side. Frontend inputs can be easily bypassed and the backend is the backstop to prevent unwanted characters, such as spaces and ‘ ‘ quote marks, to trickle through.
[Source: https://xkcd.com/327/ ]
Second order SQL injection is a two-step process. First, the attacker adds something to your application but doesn’t immediately execute it. They might be waiting for more data or waiting for a trigger activity.
This is where second-order SQL injection differs from normal SQL injections.
The attacker injects code into the table row, which is deemed as a trusted source. That table row is then called, causing the attack to move from its dormant state to active execution.
Testing for second-order SQL is harder because of its often covert nature.
For example, the malicious user signs up with the username ‘ or ‘hacker’=’hacker
This means that ‘ or ‘hacker’=’hacker gets stored in the database. The database may use the following query to verify a user’s identity:
SELECT * from creditcards WHERE username = '<usernamehere>';
So when the username is ‘ or ‘hacker’=’hacker, the final query looks something like this:
SELECT * from creditcards WHERE username = '' or 'hacker'='hacker';
Then when you go to login, the user name completes the validation query, giving access to other users and their account details.
A stored XSS, or persistent XSS attack takes place when an attacker injects a script into the content of a website or app. Unlike reflected XSS where third party links are embedded, store XSS is more dangerous in that it doesn’t require the user to interact with it.
Social media sites are particularly vulnerable to stored XSS attacks because of the platform’s nature. Users are encouraged to post and interact.
XSS is also commonly known as web worms, where the user ends up with the offending element and it executes on the browser. This attack can lead to stolen cookies, account information or some additional function through account impersonation.
To prevent this, rejecting or escaping special characters likes < > and @ before parsing it can be useful.
While JSON is the rising star for data structuring, XML documents are still popular and widely used. XPath is the syntax used for defining parts of an XML document. The idea behind XPath injections is similar to SQL injections.
The only difference between SQL injection and XPath is that the latter is in an XML format. This process of sending malformed data can be easily achieved if the attacker figures out the XML structure.
This allows the attacker to navigate XML documents, gaining access to various information such as username and password details.
Often, an XPath injection occurs when the query is built on unvalidated inputs. The trick to prevent XPath injections is to use precompiled XPaths. Avoid taking in full expressions from an unsecured source. If you have to parametrize your XPath, isolate it to string only parameters to prevent your query from getting hijacked.
For most injections, validating user inputs before you consume them is the easiest way to prevent potential attacks. It’s easy to offload the task over to the frontend, but they are only the first line of defense that’s not always guaranteed to hold.
While Java can be frontend and backend simultaneously, it’s still good practice to check that whatever your user gives you is what you expect it to be. Setting up your validation parameters may be a matter of identifying and specifying what’s allowed rather than trying to figure out and eliminate everything else.