Should Security be a top priority for JAVAdevelopers?
Java is the programming language that makes thousands of applications and websites work seamlessly.
If you have ever ordered an item on Amazon, booked a room on Airbnb, listened to music or a podcast on Spotify, hailed a ride through Uber, watched a film or program on Netflix, pinned an item on Pinterest, posted photos on Instagram, or looked up something on Google, then you’ve used Java.
Since the advancement in technology and daily life going digital, information and sniffers are found in every corner and earlier this year, an ethical hacker breached the systems of a who’s who of technology companies, including Apple, Microsoft, Netflix, PayPal, Shopify, Tesla and Uber by exploiting open-source development tools.
Last year, the SolarWinds hack affected an estimated 18,000 of the company’s customers who downloaded a software update with malicious code believed to have been inserted by Russian agents. More than 100 companies including Microsoft, Intel and Cisco were compromised. So were federal government agencies including the Treasury, Justice, Energy and Defense departments.
As developers and coders, we are collecting vast amounts of personal information and we have to protect it. It goes right up to the design level, you’ve got to think about it right from the get-go. Certain platforms collect a lot of information for marketing purposes, but that’s going to expose them to liability. So I think everybody needs to be aware of security in this environment.
According to a study by Deloitte, the average number of attacks between 2020 and 2021 has increased from 1,69 incidents in 2020, to 2,13 incidents this past year; that is 26% more and the main repercussions of cyberattacks are the loss or compromise of sensitive data, Ransonware and Malware, as shown in the following graph:
Though most of the attacks are from Ransomware and malware as developers we should concentrate on mitigating the risks from the web based attacks. Most of the web application attacks occur due to common security vulnerabilities such as SQL injection, cross-site scripting, and buffer overflows.
Secure coding practices are crucial for the protection of sensitive data and preventing unauthorized access to a system. Developers need to ensure that their code is secure and that it does not contain any vulnerabilities that could be exploited by attackers. One of the most common ways that attackers exploit vulnerabilities in code is through injection.
Injection:
There are different types of injection attacks, such as SQL, NoSQL, OS, and LDAP injection. An injection can occur when untrusted data is sent to the front-end server as part of a command or query. The attacker’s data can trick the server into executing unintended commands or accessing data without proper authorization. This is often caused by user-supplied data that isn’t validated. A application should not solely rely on front-end validationand should also validate the data on the backend server-side. Always. Front-end validation is easy to get around (e.g. when a user has JavaScript disabled). SQL injection is the most common injection attack.
What is SQL Injection?
SQL, or Structured Query Language, is the standard language for interacting with relational databases. In apps and other types of programming, databases are used to store user data such as usernames and passwords.
Databases are also often the most effective, secure solution for storing other types of data from public blog posts and comments to confidential bank account numbers. SQL statements often use arguments to pass data from users into a secured database or vice versa.
SQL injection is a weakness in web security that could let an attacker change the SQL queries that are run on the database. This can be used to get sensitive information like the structure of the database, its tables, columns, and data set. An SQL injection attack is carried out when a hacker inserts a SQL statement into data that is placed into a web form, url, query string, or any other input channel that is available to people on the outside. Malicious code usually takes the form of an SQL query that tries to get sensitive information. However, it might also take the form of a SQL statement that is intended to change the content of the database, going as far as to delete database tables.
How SQLi Attacks Works?
For example, if you put in a value that contains a single quote, does the program treat that character as user data, or does it treat it as code? If you include a always true expression like ‘1=1’ in your input, are you able to gain access as though you entered a valid password?
Consider a shopping application that displays products in different categories. When the user clicks on the Gifts category, their browser requests the URL:
https://insecure-website.com/products?category=Gifts
This causes the application to make a SQL query to retrieve details of the relevant products from the database:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
The restriction released = 1 is being used to hide products that are not released. For unreleased products, presumably released = 0. The application doesn’t implement any defenses against SQL injection attacks, so an attacker can construct an attack like:
https://insecure-website.com/products?category=Gifts%27--
This results in the SQL query:
SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1
The key thing here is that the double-dash sequence – – is a comment indicator in SQL, and means that the rest of the query is interpreted as a comment.
This effectively removes the remainder of the query, so it no longer includes AND released = 1. This means that all products are displayed, including unreleased products. Going further, an attacker can cause the application to display all the products in any category, including categories that they don’t know about:
https://insecure-website.com/products?category=Gifts%27+OR+1=1--
This results in the SQL query:
SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1
The modified query will return all items where either the category is Gifts, or 1 is equal to 1. Since 1=1 is always true, the query will return all items.
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
This query returns the user whose username is administrator and successfully logs the attacker in as that user. Here, an attacker can log in as any user without a password simply by using the SQL comment sequence – – to remove the password check from the WHERE clause of the query.
Because of their simplicity, SQLi attacks are common. They can have devastating consequences for you and your users.
- In 2009, Russian and U.S. nationals used SQLi attacks to gain access to 160 million credit card numbers from Heartland Payment Systems in what was at the time the largest breach of financial information. The attackers sold the stolen card numbers on the black market, and other criminals quickly ran up bills in the hundreds of millions of dollars, harming corporations and individuals alike.
- Even MySQL, a popular SQL provider, suffered from an SQLi attack to its website in 2011 that released hundreds of usernames and password hashes. The fact that an SQL provider can fall victim to SQLi attacks shows that no one is immune, and attackers will do their best to take advantage of insecure code.
Preventing SQL Injection Attacks:
SQLi can be prevented by following secure coding practices.
TESTING the application where it connects to the database by passing unusual values such as single quotes and semicolons into user input data and looking for error messages that return information about database structure and naming schemes.
Once the vulnerability is discovered , it’s time to REPAIR them. Validating the input and ensuring the database accounts have the smallest amount of privileges needed to read or insert data to your database can help reduce the injection attacks.
Cross Site Scripting Attacks (XSS)
Cross-Site Scripting (XSS) is a type of cyber attack where an attacker injects malicious code into a web page, which then gets executed by a victim’s browser. This malicious code can be used to steal sensitive information such as passwords, credit card details, or to take control of the victim’s account.
For example, imagine you visit a legitimate website that allows users to leave comments. An attacker could use a vulnerability in the website’s code to inject malicious code into the comments section. When you or another user views the comments section, the malicious code runs on your browser, and the attacker can steal your login credentials or perform other malicious actions.
How does XSS work?
Cross-site scripting works by manipulating a vulnerable web site so that it returns malicious JavaScript to users. When the malicious code executes inside a victim’s browser, the attacker can fully compromise their interaction with the application.
Types of XSS attacks:
Reflected cross-site scripting : Reflected cross-site scripting (or XSS) arises when an application receives data in an HTTP request and includes that data within the immediate response in an unsafe way.
Suppose a website has a search function which receives the user-supplied search term in a URL parameter:
https://insecure-website.com/search?term=gift
The application echoes the supplied search term in the response to this URL:
<p>You searched for: gift</p>
Assuming the application doesn’t perform any other processing of the data, an attacker can construct an attack like this:
https://insecure-website.com/search?term=%3Cscript%3E/*+Bad+stuff+here...+*/%3C/script%3E
This URL results in the following response:
<p>You searched for: <script>/* Bad stuff here... */</script></p>
If another user of the application requests the attacker’s URL, then the script supplied by the attacker will execute in the victim user’s browser, in the context of their session with the application.
There are many different varieties of reflected cross-site scripting. The location of the reflected data within the application’s response determines what type of payload is required to exploit it and might also affect the impact of the vulnerability. In addition, if the application performs any validation or other processing on the submitted data before it is reflected, this will generally affect what kind of XSS payload is needed.
Stored cross-site scripting: Stored cross-site scripting (also known as second-order or persistent XSS) arises when an application receives data from an untrusted source and includes that data within its later HTTP responses in an unsafe way.
Suppose a website allows users to submit comments on blog posts, which are displayed to other users. Users submit comments using an HTTP request like the following:
POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Content-Length: 100
postId=3&comment=This+post+was+extremely+helpful.&name=Carlos+Montoya&email=carlos%40normal-user.net
After this comment has been submitted, any user who visits the blog post will receive the following within the application’s response:
<p>This post was extremely helpful.</p>
Assuming the application doesn’t perform any other processing of the data, an attacker can submit a malicious comment like this:
<script>/* Bad stuff here... */</script>
Within the attacker’s request, this comment would be URL-encoded as:
comment=%3Cscript%3E%2F*%2BBad%2Bstuff%2Bhere...%2B*%2F%3C%2Fscript%3E
Any user who visits the blog post will now receive the following within the application’s response:
<p><script>/* Bad stuff here... */</script></p>
The script supplied by the attacker will then execute in the victim user’s browser, in the context of their session with the application.
To prevent XSS attacks, website and application developers must implement secure coding practices and validation mechanisms to ensure that user input is properly sanitized and validated. This involves filtering out any malicious code that may be included in user input, such as scripts or HTML tags. Additionally, developers can use security tools and techniques, such as Content Security Policy (CSP), to further protect against XSS attacks.
In SpringBoot Java built in Spring security can be used to prevent XSS attacks:
@Configuration
public class SecurityConf {@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
return http.build();
}
}
The 3rd most common attack which developers can avoid using secure coe practices is Buffer Overflow.
What is Buffer Overflow?
Almost every computer has a buffer. A buffer, or data buffer, is an area of physical memory storage used to temporarily store data while it is being moved from one place to another. These buffers typically live in RAM memory. Computers frequently use buffers to help improve performance; most modern hard drives take advantage of buffering to efficiently access data, and many online services also use buffers.
For example, buffers are frequently used in online video streaming to prevent interruption. When a video is streamed, the video player downloads and stores perhaps 20% of the video at a time in a buffer and then streams from that buffer. This way, minor drops in connection speed or quick service disruptions won’t affect the video stream performance.
Buffers are designed to contain specific amounts of data. Unless the program utilizing the buffer has built-in instructions to discard data when too much is sent to the buffer, the program will overwrite data in memory adjacent to the buffer.
A buffer overflow can be:
Accidental:You try to do too much within one program, and you exceed the space the program gives you. The program may begin to behave erratically, and in some cases, it may stop working altogether.
Intentional:Someone sends in data that’s too large for your program. That dataset contains code that can replace the valid versions. When the new code runs, it can make the program do things you don’t expect.
It may seem difficult for a hacker to craft a program that both understands your program completely and can change the way it works. But unfortunately, these attacks are relatively common.
Buffer overflow is an anomaly that occurs when software writing data to a buffer overflows the buffer’s capacity, resulting in adjacent memory locations being overwritten. In other words, too much information is being passed into a container that does not have enough space, and that information ends up replacing data in adjacent containers. Buffer overflows can be exploited by attackers with a goal of modifying a computer’s memory in order to undermine or take control of program execution.
How can attackers exploit buffer overflows?
An attacker can deliberately feed a carefully crafted input into a program that will cause the program to try and store that input in a buffer that isn’t large enough, overwriting portions of memory connected to the buffer space. If the memory layout of the program is well-defined, the attacker can deliberately overwrite areas known to contain executable code. The attacker can then replace this code with his own executable code, which can drastically change how the program is intended to work.
For example if the overwritten part in memory contains a pointer (an object that points to another place in memory) the attacker’s code could replace that code with another pointer that points to an exploit payload. This can transfer control of the whole program over to the attacker’s code.
Examples of Buffer Overflow attacks:
The Morris Worm attack in 1988:
Probably the most infamous buffer overflow example, the Morris Worm attack was one of the first complex cyber attacks that used malware (a worm). The worm first attacked the buffer and the resulting chaos unintentionally crashed ARPANET (the network that became the basis for the Internet) via a Denial of Service (DoS). The attack didn’t require human interaction or execution as it self-replicated itself while sitting on a host system
SQL Slammer attack in 2003:
SQL Slammer Attack was a computer worm that infected 75,000 users in only 10 minutes. It also affected a number of DNS servers, many ISPs lost their connections, and it slowed down internet traffic around the world. This attack exploited a buffer overflow vulnerability in Microsoft’s SQL server and Desktop Engine database products.
WhatsApp attack in 2019:
The most prolific buffer overflow attack in recent history. The attack that exploited a buffer overflow bug happened to the ostensibly secure WhatsApp messaging app. The app’s users were disturbed by the news since WhatsApp offers end-to-end encryption and promises secure conversations. The attackers used voice-over-internet protocols and their vulnerabilities. They managed to overflow its buffer and then use it as a gateway to inject users’ devices with malware. The hackers simply needed to call the user, who didn’t even need to pick up the phone for the malware to be installed. These calls also weren’t visible in the call-log, so some users were completely unaware that their devices were infected. The malware gave hackers access to users’ messages, microphones and cameras.
Preventive measures for Buffer Overflow attacks:
Avoid writing code in C/C++ because these programming languages don’t offer protection against accessing or overwriting data in their memory. Use PERL, Java, JavaScript, or C# instead. Employ bounds-checking for buffers and enforce it at runtime. Avoid standard library functions that have not been bounds-checked when writing code. Test the code for buffer overflows and patch out any bugs that cause them.
How is java protected against Buffer Overflow Attacks and not c/c++?
Java protects the programmer from the common programming error called “Buffer Overflow” by consistently throwing ArrayIndexOutofBounds exceptions whenever an attempt to access an array outside its bounds is made. Java defends the system from the common hacking technique called “Buffer Overflow” by making sure that attempts to access outside the bounds of allocated memory does not cause memory corruption. C and C++ are vulnerable to the hacking technique called “Buffer Overflow” because they allow access of unallocated memory and merely state that the effect of this activity is undefined.
By following secure coding practices developers can avoid these three attacks on the applications.
Authentication and authorization:
In any application a resource should only be accessed if the user has the right permissions and is able to identify themselves which is authorisation and authentication. When a hacker attacks an application, they steal username and passwords and other authentication factors so as to have prolonged access to the resource. Together, authentication and authorization work to ensure that only authorized users are able to access sensitive data or functionality, and that they are only able to perform actions that they are permitted to do so. It’s important for Java API developers to implement strong authentication and authorization mechanisms to prevent unauthorized access and protect sensitive data.
Authentication:
OAuth2:
Delegates security to the HTTPS protocol. OAuth (1) does not require this and uses alternative methods to remain secure. OAuth2 also introduced the use of refresh tokens that allow authentications to expire, unless “refreshed” on a periodic basis.
JSON Web Token (JWT):
The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS) and/or encrypted using JSON Web Encryption (JWE). In simple terms, it is just another way of encoding a JSON object and using that encoded object as access tokens for authentication from the server.
If we want our users to authenticate once and then not have to reauthenticate again as they interact with the endpoint, oour application will need to manage the refresh token from the endpoint (when available) in addition to the initial access token. This is a best practice, as users can become tired of constant requests for authentication. Notably, OAuth 2.0 is the only auth mechanism that currently has refresh tokens.
When looking at access tokens, it’s important to remember that some expire in an hour while others may last as long as a year or never expire. It’s especially important with token-based authentication methods to come up with a plan for managing your refresh tokens and for making sure they’re stored securely. At the end of the day, – it’s developers responsibility to protect tokens. If the application stores tokens, it’s highly recommended that we encrypt it with 256-bit encryption and rest within the data storage system with the key owned by the end user. Encrypted tokens stored with 256-bit encryptions are really tough to break, protecting the application and the customers’ usernames and passwords.
Example of Authentication methods that can be used are Key value pairs of Username and password, OTP, face recognition, fingerprint identification. Though the underlying concept of authentication remains constant with growing technilogy a variety of authentication methods can be implemented.
Authorization:
Authorization comes into play when the user is first connected and associated with the AccessControlContext. Using the Java security policy, we can grant one or more access control rights to Principals. We can then prevent access to sensitive code by calling the SecurityManager#checkPermission method:
SecurityManager.checkPermission(Permission perm)
An access control right or permission is the ability to execute an action on a resource. We can implement a permission by subclassing the Permission abstract class. To do so, we need to provide a resource name and a set of possible actions. For example, we can use FilePermission to configure access control rights on files. Possible actions are read, write, execute, and so on. For scenarios where actions are not necessary, we may simply use the BasicPermision.
public final class ResourcePermission extends BasicPermission {
public ResourcePermission(String name) {
super(name);
}
}
Grant Permission:
grant principal com.sun.security.auth.UserPrincipal testuser {
permission com.resourceAuthorize.ResourcePermission "test_resource"
};
Checking Permissions:
Once the Subject is authenticated and permissions are configured, we can check for access by calling the Subject#doAs or Subject#doAsPrivilieged static methods.
public class ResourceAction implements PrivilegedAction {
@Override
public Object run() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new ResourcePermission("test_resource"));
}
System.out.println("I have access to test_resource !");
return null;
}
}
Subject subject = loginContext.getSubject();
PrivilegedAction privilegedAction = new ResourceAction();
Subject.doAsPrivileged(subject, privilegedAction, null);
PrivilegedAction privilegedAction = new ResourceAction();
Subject.doAsPrivileged(subject, privilegedAction, null);
Encryption:
In the event that an application is hacked, encrypted data is much more difficult to access and exploit than plain text data. Even if the attacker manages to steal the encrypted data, they would still need to decrypt it to be able to read it. Without the proper decryption key, the stolen data would be useless to the attacker.
Encryption is the process of converting plain text or data into a secret code or cipher, so that only those who have the key to decrypt the code can read and understand the message or data. It is like putting your message or data in a locked box, and only the person with the key to the box can unlock it and read the message. Encryption helps to keep your information secure and private, even if it is intercepted by unauthorized parties during transmission or storage.
Types of Encryption:
Symmetric encryption:
In symmetric encryption the same key is used for encryption and decryption. It is therefore critical that a secure method is considered to transfer the key between sender and recipient.
Asymmetric encryption:
Asymmetric encryption uses the notion of a key pair: a different key is used for the encryption and decryption process. One of the keys is typically known as the private key and the other is known as the public key. The private key is kept secret by the owner and the public key is either shared amongst authorised recipients or made available to the public at large. Data encrypted with the recipient’s public key can only be decrypted with the corresponding private key. Data can therefore be transferred without the risk of unauthorised or unlawful access to the data.
Hashing:
Hashing is the process of generating a string, or hash, from a given message using a mathematical function known as a cryptographic hash function.
While there are several hash functions out there, those tailored to hashing passwords need to have four main properties to be secure:
It should be deterministic: the same message processed by the same hash function should always produce the same hash.
It’s not reversible: it’s impractical to generate a message from its hash
It has high entropy: a small change to a message should produce a vastly different hash
It resists collisions: two different messages should not produce the same hash .
A hash function that has all four properties is a strong candidate for password hashing since together they dramatically increase the difficulty in reverse-engineering the password from the hash.
Though, password hashing functions should be slow. A fast algorithm would aid brute force attacks in which a hacker will attempt to guess a password by hashing and comparing billions (or trillions) of potential passwords per second. So we need a passord hashing function which is slow and uses maximum computer capactiy to decrypt. The latest hashes used are: PBKDF2, BCrypt, and SCrypt.
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
Salt is necessary since two identical looking passwords should have different hashes.
create a PBEKeySpec and a SecretKeyFactory which we’ll instantiate using the PBKDF2WithHmacSHA1 algorithm:
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
The third parameter (65536) is effectively the strength parameter. It indicates how many iterations that this algorithm run for, increasing the time it takes to produce the hash and SecretKeyFactory to generate the hash.
Although Java natively supports both the PBKDF2 and SHA hashing algorithms, it doesn’t support BCrypt and SCrypt algorithms. Luckily for us, Spring Security ships with support for all these recommended algorithms via the PasswordEncoder interface: Pbkdf2PasswordEncoder gives us PBKDF2
BCryptPasswordEncoder gives us BCrypt,
and SCryptPasswordEncoder gives us SCrypt.
By following secure code practices, making sure the users are authenticated and authorized and encrypting the data are the ways a developer can contribute to cyber security and SECURITY IS EVERYONE’S RESPONSIBILITY.