Reputation: 311
Currently, I have the following security measures.
What else could I be doing to secure the backend?
Upvotes: 0
Views: 843
Reputation: 13064
Currently, I have the following security measures.
- JWT Token using PassportJS per request after login
Bear in mind that the JWT token only identifies who is in the request, not what is doing the request.
- Rate limiting on just my Login and Register endpoints at the moment
Congratulations by having this measure in place, but you should extend it to all endpoints and adjust the rate limit in a per endpoint basis. Try to figure out the normal rate per second/minute for a normal usage and set the rate limit just a little above it, and have monitoring in place to see when you have spikes in requests being blocked due to exceeding the rate limit.
- Emailing a user that someone unknown has logged in from a different device
That's a measure that people do not talk about often or even implement, but it's a very important one to ensure your users security. You can enhance it by only allowing that new login happens from a different device by sending an approoval link to the user email.
Before I dive into your question What else could I be doing to secure the backend?
I would like to first clear a misconception that usually I find among developers of any seniority, that is about the difference between who and what is accessing an API server.
I wrote a series of articles around API and Mobile security, and in the article Why Does Your Mobile App Need An Api Key? you can read in detail the difference between who and what is accessing your API server, but I will extract here the main takes from it:
The what is the thing making the request to the API server. Is it really a genuine instance of your mobile app, or is it a bot, an automated script or an attacker manually poking around your API server with a tool like Postman?
The who is the user of the mobile app that we can authenticate, authorize and identify in several ways, like using OpenID Connect or OAUTH2 flows.
So think about the who as the user your API server will be able to Authenticate and Authorize access to the data, and think about the what as the software making that request in behalf of the user you have authenticated with the PassportJS JWT token.
What else could I be doing to secure the backend?
As you may be aware by now you are lacking measures to allow your backend to attest what is doing the request, because your current measures are centered around the who in the request, that is represented by the PassportJS JWT token.
In order to allow the backend to have more confidence that is indeed receiving requests from what it expects, your mobile app, you need to approach this from both sides, the mobile app and backend.
So you can start by digitally sign the requests the mobile app does to the backend, and you can see here a simple approach of doing it so in the mobile app:
private fun calculateAPIRequestHMAC(url: URL, authHeaderValue: String): String {
val secret = JniEnv().getHmacSecret()
var keySpec: SecretKeySpec
// Configure the request HMAC based on the demo stage
when (currentDemoStage) {
DemoStage.API_KEY_PROTECTION, DemoStage.APPROOV_APP_AUTH_PROTECTION -> {
throw IllegalStateException("calculateAPIRequestHMAC() not used in this demo stage")
}
DemoStage.HMAC_STATIC_SECRET_PROTECTION -> {
// Just use the static secret to initialise the key spec for this demo stage
keySpec = SecretKeySpec(Base64.decode(secret, Base64.DEFAULT), "HmacSHA256")
Log.i(TAG, "CALCULATE STATIC HMAC")
}
DemoStage.HMAC_DYNAMIC_SECRET_PROTECTION -> {
Log.i(TAG, "CALCULATE DYNAMIC HMAC")
// Obfuscate the static secret to produce a dynamic secret to initialise the key
// spec for this demo stage
val obfuscatedSecretData = Base64.decode(secret, Base64.DEFAULT)
val shipFastAPIKeyData = loadShipFastAPIKey().toByteArray(Charsets.UTF_8)
for (i in 0 until minOf(obfuscatedSecretData.size, shipFastAPIKeyData.size)) {
obfuscatedSecretData[i] = (obfuscatedSecretData[i].toInt() xor shipFastAPIKeyData[i].toInt()).toByte()
}
val obfuscatedSecret = Base64.encode(obfuscatedSecretData, Base64.DEFAULT)
keySpec = SecretKeySpec(Base64.decode(obfuscatedSecret, Base64.DEFAULT), "HmacSHA256")
}
}
//Log.i(TAG, "protocol: ${url.protocol}")
//Log.i(TAG, "host: ${url.host}")
//Log.i(TAG, "path: ${url.path}")
//Log.i(TAG, "Authentication: $authHeaderValue")
// Compute the request HMAC using the HMAC SHA-256 algorithm
val hmac = Mac.getInstance("HmacSHA256")
hmac.init(keySpec)
hmac.update(url.protocol.toByteArray(Charsets.UTF_8))
hmac.update(url.host.toByteArray(Charsets.UTF_8))
hmac.update(url.path.toByteArray(Charsets.UTF_8))
hmac.update(authHeaderValue.toByteArray(Charsets.UTF_8))
return hmac.doFinal().toHex()
}
The backend verification for the dynamic HMAC is done here:
router.use(function(req, res, next) {
log.info('---> VALIDATING DYNAMIC HMAC <---')
let base64_decoded_hmac_secret = Buffer.from(config.SHIPFAST_API_HMAC_SECRET, 'base64')
// Obfuscate the static secret to produce a dynamic secret to use during HMAC
// verification for this demo stage
let obfuscatedSecretData = base64_decoded_hmac_secret
let shipFastAPIKeyData = new Buffer(config.SHIPFAST_API_KEY)
for (let i = 0; i < Math.min(obfuscatedSecretData.length, shipFastAPIKeyData.length); i++) {
obfuscatedSecretData[i] ^= shipFastAPIKeyData[i]
}
let obfuscatedSecret = new Buffer(obfuscatedSecretData).toString('base64')
hmac = crypto.createHmac('sha256', Buffer.from(obfuscatedSecret, 'base64'))
if (hmacHelpers.isValidHmac(hmac, config, req)) {
next()
return
}
res.status(400).send()
return
})
But while this is a good step in the right direction it can be bypassed by reverse engineering the mobile app with a tool like Frida:
Inject your own scripts into black box processes. Hook any function, spy on crypto APIs or trace private application code, no source code needed. Edit, hit save, and instantly see the results. All without compilation steps or program restarts.
This type of situation can be mitigated by using the Mobile App Attestation concept, and to learn more about it I recommend you to read this answer I gave to the question How to secure an API REST for mobile app?, specially on the sections for Securing the API Server and A Possible Better Solution.
In any response to a security question I always like to reference the awesome work from the OWASP foundation.
The OWASP API Security Project seeks to provide value to software developers and security assessors by underscoring the potential risks in insecure APIs, and illustrating how these risks may be mitigated. In order to facilitate this goal, the OWASP API Security Project will create and maintain a Top 10 API Security Risks document, as well as a documentation portal for best practices when creating or assessing APIs.
OWASP Mobile Security Project - Top 10 risks
The OWASP Mobile Security Project is a centralized resource intended to give developers and security teams the resources they need to build and maintain secure mobile applications. Through the project, our goal is to classify mobile security risks and provide developmental controls to reduce their impact or likelihood of exploitation.
OWASP - Mobile Security Testing Guide:
The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security development, testing and reverse engineering.
Upvotes: 3
Reputation: 1509
OWASP provides a REST Security Cheat Sheet here. https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
Actually there are many security measures that we can mentioning but an option similiar to your list is,
If you want to allow users for multi device access to service, another security feature you can add to your project is managing active devices for each user. Users can view log of any active devices and remove any of them.
Upvotes: 0