Ongoing Security: a step-by-step guide to a secure app development process
Mobile devices have become an integral part of our daily lives. We communicate with one another, entertain ourselves, bank, and shop with a few swipes and strokes of our fingertips. That’s why securing these devices and the apps we use is so important.
But this importance doesn’t always correlate to common secure practices during development.
Neglecting security in the development of mobile apps can have serious consequences. Hackers can exploit vulnerabilities in the app to access sensitive user data like login credentials and financial information. This can lead to identity theft, financial loss to the end user, and damage to the company's reputation. Insecure mobile apps can also be used to spread malware, which can compromise the security of other devices and networks.
It’s essential, then, that security be a top priority in the mobile development process. This is a commitment that requires a holistic approach involving all key stakeholders and incorporating various security measures at every stage of the development pipeline. The understanding of security as a never ending process is crucial in making digital products secure.
This leads us to the idea of building security in the product rather than adding it as a separate step. Only by building the solution securely - by design - can we get close enough to a truly secure product.
In this article, we’ll introduce the concept of ongoing security by introducing the security measures applied at each step of the product life cycle. We’ll demonstrate some security-by-design principles to follow, too.
Why you need to think about security holistically
Lots of companies are tempted to bolt security on at the end of the development cycle. But this is a tactic that has proved itself time and again not to work.
You see, security vulnerabilities can be introduced at any stage in the development process. That means if you only think about security at the end, it might already be too late to fix issues that have crept in.
It’s vital to consider security throughout the entire mobile development process to ensure the development of secure and reliable apps. Or, in other words, to enjoy the benefits of a Secure App Development Lifecycle.
Security measures can dramatically impact the design and functionality of an app. For example, implementing encryption or authentication controls could require changes to the app's architecture and user experience. If these measures aren’t considered until the end of the development process, it may be difficult (or even impossible) to integrate them without a significant amount of rework.
Testing for security vulnerabilities is most effective when it’s performed early and often. If security testing is only done at the end of the process, it can be difficult to identify and fix all the vulnerabilities before the app is released. It can also actually lead to delays in the release of the app and increase the overall cost of development.
This is a theme we’ll return to again in this article - namely that waiting to think about security at the end doesn’t actually save you time at all. Investing some time to think about security from the outset will save you a lot more time in the long run.
This is one of the main reasons forward-thinking companies now accept that “shifting left” (incorporating security thinking throughout development) is a necessity.
Security should begin before you start coding
Developers usually think that security work should begin once the first bunch of features are ready - when they need to think about authentication and protecting parts of the application. But again, this isn’t the right approach. You need to think about security before development even starts. At what we typically call the threat modeling stage.
Threat modeling is the process of identifying, analyzing, and prioritizing the potential threats and vulnerabilities that your app may face. It’s an important step in the mobile app development process because it helps to ensure that your app is designed and built with security in mind.
There are several approaches to threat modeling, but one common method is to start by identifying the assets that need to be protected (sensitive user data, system resources, and so on.) Then you identify the threats and vulnerabilities that could compromise these assets before evaluating the likelihood and potential impact of each threat. At which point you can figure out the appropriate countermeasures.
Threat modeling - like security in general - should be an ongoing process performed throughout the development cycle - not only before implementation. It’s one more way of empowering the development team to identify and address potential security issues early on, rather than trying to fix them later in the process.
Some examples of threats and vulnerabilities that may be identified through threat modeling include:
- Injection attacks, such as SQL injection and cross-site scripting (XSS)
- Unauthorized access to sensitive data
- Lack of encryption for sensitive data transmitted over the network
- Lack of proper authentication and authorization controls
- Insecure communication channels
It’s crucial to include all stakeholders in the threat modeling process. So, not only developers, but security experts, and business analysts, too. This helps to ensure that all aspects of the solution are considered and that the appropriate countermeasures are implemented.
Designing for a zero trust world is also a vital part of the threat modeling process. After all, during development you use lots of external tools, cloud services and other resources owned by third parties. Even the runtime environment - devices and mobile operational systems - are not controlled by you at any given point in time. So, we need to mitigate these potential risks. Take a read of this article we wrote about protecting your app in case of it being run on a rooted or a jailbroken device.
Coding safely
You might say that the only code which is free of security problems is the code that hasn’t been written yet. That’s why we need to follow best practices when writing code in the first place, and then follow it up with security testing.
To minimize the probability of introducing an accidental vulnerability, developers should follow security code guidelines. These exist for every major platform and programming language out there, including a guideline for Java, the security guideline for Android, and a guide for iOS.
Before you commit your code to a repository or even run it locally, you can and should test it. Static testing means evaluating the quality and functionality of a solution without executing it. Mobile Development cannot be secure without it, as many problems are identified and fixed at this very early stage.
Static testing is represented by various techniques. The first one worth mentioning here is code review. This involves manually reviewing the source code of your application to identify issues such as bugs and security vulnerabilities.
You can also leverage something called linting. This is a tool that checks the code to make sure that it adheres to coding standards and best practices.
Lastly you can run a static code analysis tool. This examines the code for issues such as vulnerabilities, performance problems, and code complexity.
Static testing should be performed regularly throughout the development process to ensure the quality and reliability of your app. Static security testing should be run both on the developer’s machine during the development itself and on the CI/CD pipeline during builds. That way problems can be identified as you go and solutions to those problems found. The idea here is that you monitor the health of your app as it evolves.
For example, consider the following code snippet in an Android app:
String input = request.getParameter("input");
String query = "SELECT * FROM users WHERE username = '" + input + "'";
ResultSet results = statement.executeQuery(query);
This code is vulnerable to SQL injection attacks because the user input is not properly sanitized. An attacker could exploit this vulnerability by entering special characters or scripts into the "input" parameter, which could then be executed by the database.
Static analysis tools can detect this vulnerability and alert the development team. The issue can then be fixed by properly sanitizing the user input, as shown below:
String input = request.getParameter("input");
input = input.replaceAll("[^a-zA-Z0-9]", "");
String query = "SELECT * FROM users WHERE username = '" + input + "'";
ResultSet results = statement.executeQuery(query);
This is a bit of an artificial example which you’d probably only encounter in real-life in a public kiosk-type application. But it provides a good example of what static analysis can do.
The example below is of an iOS app snippet:
let token = "aabb11ff11ee";
let url = URL(string: kGraphURI + "/v1.0/me/?token=" + authToken)
var request = URLRequest(url: url!)
Here, the security testing tool can detect hard coded credentials and raise an error for the development team to investigate.
By performing a static analysis and fixing vulnerabilities early on, the development team can ensure the security of the app and protect against potential attacks.
The build process
Developers work mostly with source code. But the fruits of their labor end up running on the device itself. This magical transformation that most people take for granted happens by compiling the code and bundling the end result - assets and other resources - into a working app. CI/CD pipelines are responsible for this process. And as with every other part of the development process, vulnerabilities can emerge here.
After all, a significant part of your application probably contains dependencies: frameworks and libraries which are responsible for common tasks like networking, dependency injection, image downloading, and so on. The CI/CD pipeline downloads these from public servers where bad actors might have injected modified versions of these dependencies that can compromise the app.
That’s why you have to regularly update the software and libraries used in the app or device to ensure that any known vulnerabilities are fixed. Failing to do so will subject the app to supply chain attacks further down the line, compromising not only your application but your end user’s device as well.
Another aspect of building a target application is a signing mechanism - a way to make sure that it’s actually you uploading that new version of your app to the AppStore. CI/CD pipelines must have access to the provisioning profiles, certificates and the keystore in order to sign the app. Those secrets should be stored and accessed securely, otherwise your app could be subject to cloning attempts.
To mitigate these risks CI/CD pipelines should incorporate strong authentication, RBAC models, and user control lists to limit the amount of people able to alter the pipeline. They should store secrets in industry-standard products and limit the read access only to the pipeline itself.
When you have an app in place
Once you have several features combined, it’s time to think how they can influence each other from a security perspective. Penetration testing can help with that.
Mobile penetration testing is the process of evaluating the security of a mobile app or device by attempting to exploit vulnerabilities.
There are several types of mobile penetration testing, including:
- Black box testing. This involves testing the app or device without any prior knowledge of its internal workings. The tester is only given the external interface of the app or device and must try to identify vulnerabilities through testing.
- White box testing. This involves testing the app or device with complete knowledge of its internal workings. The tester is given access to the source code and other internal details of the app or device and can use this information to identify vulnerabilities.
- Gray box testing. This involves testing the app or device with partial knowledge of its internal workings. The tester is given some, but not all, of the internal details of the app or device and must use this information to identify vulnerabilities.
Let’s think about an example. Consider a mobile banking app that is being tested for vulnerabilities. During testing, the team may discover that the app is vulnerable to brute force: attackers trying to find a password matching a particular user’s account. This is an issue that could have been missed through other types of testing. Static analysis for example would never uncover such a problem. But by actively trying to exploit the app, this kind of issue can be found and a proper remedy provided. In this particular case the server side should limit the login attempts and leverage multi factor authentication. Again, design decisions like this cannot be introduced last minute.
Now let’s consider white box testing as an example of incorporating penetration testing into a development lifecycle. White box testing is particularly useful for identifying vulnerabilities that may be difficult to detect through other means, such as vulnerabilities in the underlying architecture or design of the app or device.
To perform white box testing, the tester will typically review the source code and other internal details of the app or device to identify potential vulnerabilities. They may also use tools such as static code analysis tools or debuggers to examine the code in more detail.
Once potential vulnerabilities have been identified - like insufficient encryption, brute force and others - the tester will attempt to exploit them to determine if they are serious. If a vulnerability is confirmed, the tester will work with the development team to determine the appropriate course of action, such as fixing the vulnerability or implementing a workaround. This again highlights that security is everyone’s responsibility, and not only the job of security folk.
When your app is running on a device
Once you have your release build in the hands of your users, the real fun begins!
Remember, the way your app will be used in the wild is quite different to the controlled conditions before release. Imagine your application is a car - a lot of our ongoing security advice up to this point has related to safety measures you’d implement before the vehicle leaves the factory floor. But once it’s out on the road, there are all sorts of additional security concerns. Not least the way other cars - and drivers - behave.
The device running your app may be rooted or jailbroken. And your end user might have malicious intentions with your application - they could try to reverse engineer it.
The device running your app could even be infected with the malware.
This is a complex set of risks that only a holistic strategy can mitigate. One of the key points of tackling such problems is ensuring you place a high importance on application integrity. You can achieve and maintain integrity via cryptographic integrity algorithms, checking application signatures, leveraging SSL pinning to double check the server connection safety, applying the security tools provided by the platform, and many more.
But even before the application reaches the end user you need to make sure your application hasn’t been tampered with. Integrity checks at runtime can help with this, but so too can prioritizing ongoing security across the whole development lifecycle and making your app more difficult to tamper with in the first place.
There are several other measures that you can take to ensure the ongoing security of a mobile app or device, including:
- Monitoring for security threats. Check your app or the device running it for security threats and take appropriate action if a threat is detected. This may involve implementing additional security measures or working with security experts to resolve the issue. One example is a mobile banking app integrating an anti-malware engine to make sure it’s safe to run on every device.
- Conducting regular security testing. Static analysis, dynamic testing, and penetration testing can help you identify and fix vulnerabilities before they can be exploited.
- Educating end users and stakeholders. It’s vital you teach your end users and other stakeholders about security best practices and tips to protect their sensitive data. This may involve providing resources and training materials or implementing security awareness programs. Make sure that you communicate clearly and transparently with your end users so they know how to differentiate genuine comms and bogus phishing messages. And preach the importance of being suspicious in general.
- Using tested primitives. A big part of encryption problems come from incorrect usage of libraries such as failing to provide good random seeds or mixing up the parameters. Don’t reinvent the wheel and rely on good quality tools that exist to minimize errors (like BoringCrypto.)
By taking these ongoing security measures, the development team can ensure the security of your app over time and protect against potential threats.
Ongoing security is about layers
At this point it’s important to say that you can’t limit your security to one single measure. All of the mechanisms we’ve mentioned in this article play their part.
The goal here is to add one measure on top of the other until you get what we call multiple layers of security. This is also often referred to as "defense in depth" and is a key principle of cybersecurity.
Some examples of how different layers of security can help to keep a mobile app or device safe include:
- Network security. Network security controls, such as firewalls and intrusion detection systems can help to protect the app or device from network-based attacks.
- Device security. Device-level security controls such as encryption and biometric authentication can help to protect the app or device from unauthorized access.
- App security. App-level security controls like secure coding practices and input validation. These can help to protect your app from vulnerabilities.
- Data security. Controls like encryption and secure data storage. They protect sensitive data stored on the app or device from being accessed by unauthorized parties.
What if an incident does occur?
Some security vendors market their products as if they’re offering 100% security. But in reality this is a bogus concept. There’s no such thing as 100% security because human nature doesn’t allow it. Mistakes can always happen. People can be tricked by sophisticated social engineering campaigns, for example.
In the event that a security incident does occur, it’s important for you and your development team to have a plan in place for responding to and resolving the issue. This may involve the following steps:
- Assess the severity of the incident. This is vital when determining the appropriate level of response. For example, a minor security breach will require a different level of response than a major data breach.
- Contain the incident: Prevent the incident from spreading or worsening. This may involve disconnecting affected systems from the network, shutting down certain services, or taking other appropriate measures.
- Investigate the incident: Conduct a thorough investigation of the incident to determine the root cause and identify any potential vulnerabilities that may have contributed to the incident.
- Communicate with relevant stakeholders: Transparency is key. Let your end users, customers, and regulatory authorities know about the incident and any actions that are being taken to resolve it.
- Implement corrective measures: Based on the results of the investigation, you should put corrective measures in place to fix any vulnerabilities and prevent similar incidents from occurring in the future.
By following these steps you can minimize the impact on the app itself and on your business reputation. A well-defined incident response plan can ensure that your team is prepared to handle any security incidents that may arise.
Repeating the release cycle
Once one version of your app reaches your end user, the work on the next version is typically already in motion. So, you need to carry out threat modeling again, then run security testing, provide application integrity, protect your CI/CD pipeline process, and secure data at rest and in transit.
This is ongoing security.
We hope you’ve learned from this article that the security of your application is not a one-time step. It’s an ongoing, holistic process which involves subject matter experts, developers, quality assurance engineers, product managers, SREs, security experts and others.
At each step of your application development process, be it gathering requirements, writing code, or shipping to production, you need to pay close attention to be thinking about security. Which measures or techniques can you apply?
Truly secure applications can only be developed via an ongoing security process.
Like the article? Wanna receive new content earlier, than everybody else? Consider helping to run it at Patreon or Boosty. The funds go to pay for the hosting and some software like a Camo Studio license. Patrons and Boosty subscribers of a certain level also get access to a private Architecture Community and of course every supporter gets early access. Big thanks to Nikita, Anatoly, Oleksandr, Dima, Pavel B, Pavel, Robert, Roman, Iyri, Andrey, Lidia, Vladimir, August and Roman for already supporting the newsletter. Join them as well!