So, you've created a form that works. Did you take the time to consider if it leaves your user open to attacks?
As developers, we're often more concerned with making sure our code works, is maintainable, and is easy to read than fixing bugs. However, if we don't prioritize security, we leave our applications open to malicious intents. As such, it's crucial to make security part of our development process. But to do this, we first have to understand the types of threats our apps are open to.
In this post, I'll explain how to defend your Kotlin application against broken authentication. First, I'll explain a few common authentication vulnerabilities. Then, we'll look at solutions for making your Kotlin applications more secure. Most importantly, you'll see that protecting your users against nefarious attackers doesn't have to mean extra work!
What Is Broken Authentication?
Broken authentication is a broad term that encompasses several vulnerabilities. Our focus will be on two main weaknesses:
session management: the hijacking of a user's session IDs
credential management: the hijacking of an authentic user's credentials
Attacks can either be very specific and target an individual. It's also common for attackers to launch widespread attacks.
Session Management
To begin with, whenever a user interacts with an application, they're assigned a session ID to help identify them. This is a unique ID that the application uses to communicate with the user. Session IDs are commonly found in the form of shared preferences. Since shared preferences are a great way to keep users logged in, attackers will try using various methods to obtain these IDs, thereby obtaining protected information.
Let's take a look at some different types of session management attacks and how you can prevent them.
Attack: Session ID URL Rewriting
In session ID URL rewriting, a user's session ID appears in the URL. In this scenario, you've exposed your users' private data to widespread attacks. This can lead to attackers sniffing out and gaining access to the session ID.
Solution
Kotlin is still mostly used for developing mobile apps however it is possible to build web apps using Kotlin. It will be important to use HTTPS on that website. In short, HTTPS authenticates websites and protects information the user submits. Hackers are unable to decrypt user information, thus decreasing the likelihood of an attack.
The padlock symbol you see next to the URL of most websites represents HTTPS and lets your user know the website is secure. The Adobe business platform back end was migrated from Java to Kotlin. The lock icon assures users that the website is safe to use.
The lock icon next to the URL assures users a website is secure
Fortunately, Kotlin has many frameworks you can use to serve, consume, and test HTTPS. For example,Ktor and http4k are popular frameworks for connecting Kotlin applications. Comparatively, Ktor is fully asynchronous while http4k isn't. It's important to choose the framework that works best for your needs.
Attack: Session Fixation
Mobile apps connect to external services to authenticate a user. These include their host servers, other mobile resources, and third-party services. Hackers will use vulnerabilities within this communication to launch an attack.
In session fixation, the attacker tries to trick a user into authenticating a predetermined session ID. An attacker will try to predetermine a session ID. They then send a link with the predetermined session ID to the user. This will prompt the user to log in. Misled into believing the link leads to a legitimate application, they enter their credentials. Attackers are able to gain access to protected resources because they now have access to an authenticated session ID.
Solution
Mobile Client Certificates
The easiest way to block non-human attacks from gaining access to your app is through mobile client certificates. In essence, applications use mobile client certificates to authenticate themselves to the back end. The client certificate simply validates that the connection is coming from a trusted source and should be allowed access to the back end. As a result, this allows the firewall to block unauthorized apps or malicious bots. Most importantly, this allows only trusted apps to gain access to your back end. Think of it as a digital handshake between your application and the external service.
Fortunately, Kotlin has support for incorporating mobile client certificates within your app. There are three ways to add certificates using Kotlin: network security configuration, trust manager, and certificate pinning. Each has its own strengths and weaknesses, so make sure to pick the one that's best for your Kotlin app. The code below is an example of certificate pinning used together with okHttpClient.
val certificatePinner = CertificatePinner.Builder()
.add(
"www.secureserver.com",
"sha256/7HIpactkIAq2Y49orFEOQKurWxkmSFZhBCoQYcRyJ3Y="
).build()
val okHttpClient = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
Ending A Session
Additionally, ending the session length after a long period of inactivity and logging out after a certain period of time can reduce the chances of a breach. You can remove or add session IDs using shared preferences. Equally important, Kotlin will store the shared preferences within the device itself. So, remember to encrypt them so anyone who gains access cannot read them. Also, make sure to destroy the IDs once your user becomes inactive for a long period of time.
To illustrate, consider a movie streaming app like Amazon Prime. A streaming app is going to want to make it easy for its users to navigate through the app without logging in every time. Conversely, a banking app should log out users after a few minutes since the likelihood of an attack is much higher. With this in mind, be sure to clear the saved data when the user is no longer active or leaves the app. You can use the activity lifecycle, onDestroy and onResume, to do this.
onDestroy(){
val sharedPref = context.getSharedPreferences(STRING_VALUE, Context.MODE_PRIVATE)
sharedPref.edit().clear().apply()
}
onResume(){
context.getSharedPreferences(STRING_VALUE, 0).edit().clear().commit();
}
Credential Management
The easiest way for attackers to gain access to protected information is by using a user's credentials.
Attackers will try to gain access to a user's credential pair (i.e., their username and password). Gaining access to these subsequently leads to an attacker having control of a user's account. This gives the attacker access to protected resources.
Attack: Credential Stuffing
Credential stuffing is on the rise instead of targeting large-scale corporations. Basically, this type of attack happens when an attacker already has access to user credentials.
Often, hackers will sell or give away credentials to other attackers. They have gained access to an unencrypted database. These attackers will automate trying different username and password pairs. In fact, hackers will do this using virtual environments such as emulators.
Above all, this is a numbers game. The intention is to hijack user accounts. Attackers do this because people often use the same password and username on different applications. In reality, there are billions of credentials currently that attackers have been able to gain access to.
Solution
For that reason, one of the best ways to significantly prevent your mobile app from falling victim to credential stuffing is to prevent it from being run on virtual devices. These include simulators, emulators, virtual environments, debugging tools, and other virtual environments. The code below simply checks to see if the app is opened in an emulator.
First, we gather all the emulators in the market. Second, Kotlin allows us to evaluate their properties, mainly their build value. Finally, we use the return value to determine if the application can be granted access.
private fun checkBuildConfig(): Boolean {
var isEmulator = (Build.MANUFACTURER.contains("Genymotion")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.toLowerCase().contains("droid4x")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.HARDWARE == "goldfish"
|| Build.HARDWARE == "vbox86"
|| Build.HARDWARE.toLowerCase().contains("nox")
|| Build.FINGERPRINT.startsWith("generic")
|| Build.PRODUCT == "sdk"
|| Build.PRODUCT == "google_sdk"
|| Build.PRODUCT == "sdk_x86"
|| Build.PRODUCT == "vbox86p"
|| Build.PRODUCT.toLowerCase().contains("nox")
|| Build.BOARD.toLowerCase().contains("nox")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")))
return isEmulator
}
Hackers want to use the servers and back end to gain access to the app. In this situation, remember to encrypt all API endpoints, as attackers will use this information for credential stuffing. For instance, it's advisable to encrypt usernames and passwords since attackers need this information for an attack. For added protection, consider using a keystore, which makes it more difficult to extract the encrypted data from the device.
fun main():{
val plaintext: ByteArray = ...
al keygen = KeyGenerator.getInstance("AES")
keygen.init(256)
val key: SecretKey = keygen.generateKey()
val cypher = Cypher.getInstance("AES/CBC/PKCS5PADDING")
cypher.init(CYPHER.ENCRYPT_MODE, key)
val cyphertext: ByteArray = cypher.doFinal(plaintext)
val iv: ByteArray = cypher.iv
}
Attack: Password Spraying
Password spraying, a brute force attack, is similar to credential stuffing. Spraying doesn't involve already existing passwords and username pairs. Attackers will try to guess user credentials using common phrases, terms, birthdays, etc. The assumption is many people will choose weak passwords. On average, users have to remember 100 passwords. This results in people using weak or reusing passwords to make their lives easier. Above all, this can lead to your app being vulnerable to malicious attacks and breaches.
Solution
In general, using passwordless authentication eliminates unsafe password behavior. Users often pick common passwords that are very easy to hack. These make it easy for hackers to exploit this vulnerability. Consider sending users verification links, requiring an SMS that verifies ownership, or using biometrics. Biometrics, in particular, is a great security feature as it requires a unique physical action from the user. And Kotlin supports using biometrics within your application.
val biometricManager = BiometricManager.from(this)
when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
BiometricManager.BIOMETRIC_SUCCESS ->
Log.d("MY_APP_TAG", "App can authenticate using biometrics.")
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
Log.e("MY_APP_TAG", "No biometric features available on this device.")
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
Log.e("MY_APP_TAG", "Biometric features are currently unavailable.")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
// Prompts the user to create credentials that your app accepts.
val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
}
startActivityForResult(enrollIntent, REQUEST_CODE)
}
}
Conclusion
Broken authentication can have serious ramifications for your Kotlin application. That's why it's important to make security a part of your development process.
As a developer, you need to understand the most basic types of vulnerabilities out there when coding in Kotlin.
This way, you can plan better and execute code that protects your users from malicious attacks. All things considered, implementing session and credential management shouldn't be an afterthought.
Fortunately, Kotlin comes with many frameworks and libraries we can use to mitigate these risks. Using a tool like StackHawk that will run tests and find all vulnerability issues before the application is pushed to production will save you in the end. As more businesses provide mobile applications for their users, this will make them an attractive target for attackers. It's important as developers to place protocols in place that will deter such attacks.
This post was written by Sinazo Bogicevic. Sinazo is a software developer with a passion for blockchain and mobile development. When she's not creating amazing products, she can be found either writing or talking about tech for the not so tech-savvy.