Insecure Storage of Application Data
Insecure Storage of Application Data
Description
Insecure storage vulnerability in mobile applications refers to a security flaw where sensitive data, such as user credentials, personal information, or other confidential data, is stored on the device in world readable locations. This can leave the data vulnerable to unauthorized access by other applications on the device or by a malicious actor with physical access to the device.
Insecure storage does not only occur when the application writes to world-readable locations but also when the application loads data such as configuration files from world-writable locations where a malicious actor can alter the configuration files and consequently tamper with the functionality of the application.
import android.os.Bundle
import android.os.Environment
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStreamWriter
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val loginButton: Button = findViewById(R.id.login_button)
loginButton.setOnClickListener {
// Dummy authentication process
val token = authenticate("username", "password") // authentication logic
// Store token in public storage
storeTokenInPublicStorage(token)
}
}
private fun storeTokenInPublicStorage(token: String) {
val filePath = "/sdcard/insecure_app/jwt_config.txt"
val file = File(filePath)
try {
val outputStreamWriter = OutputStreamWriter(FileOutputStream(file))
outputStreamWriter.write(token)
outputStreamWriter.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let loginButton = UIButton(type: .system)
loginButton.setTitle("Login", for: .normal)
loginButton.addTarget(self, action: #selector(loginButtonTapped), for: .touchUpInside)
view.addSubview(loginButton)
loginButton.translatesAutoresizingMaskIntoConstraints = false
loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loginButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
@objc func loginButtonTapped() {
// Dummy authentication process
let token = authenticate(username: "username", password: "password")
// Store token in public storage
storeTokenInPublicStorage(token: token)
}
private func storeTokenInPublicStorage(token: String) {
let filePath = "/var/mobile/Media/insecure_app/jwt_config.txt"
let fileURL = URL(fileURLWithPath: filePath)
do {
try token.write(to: fileURL, atomically: true, encoding: .utf8)
print("Token stored successfully.")
} catch {
print("Failed to write token: \(error)")
}
}
}
import 'dart:io';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Insecure Storage'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// Dummy authentication process
String token = authenticate("username", "password");
// Store token in public storage
storeTokenInPublicStorage(token);
},
child: Text('Login'),
),
),
);
}
void storeTokenInPublicStorage(String token) {
final directory = Directory('/sdcard/insecure_app/');
directory.createSync(recursive: true);
final file = File('${directory.path}/jwt_config.txt');
try {
file.writeAsStringSync(token);
print('Token stored successfully.');
} catch (e) {
print('Failed to write token: $e');
}
}
}
Recommendation
- Use Secure Storage: Store sensitive data such as authentication tokens or credentials securely. Use secure storage mechanisms provided by the platform, such as Keychain for iOS and SharedPreferences with encryption for Android.
- Data Encryption: Encrypt sensitive data at rest using strong encryption algorithms to protect data stored on user device.
- Avoid Hardcoding Sensitive Information: Instead of hardcoding sensitive information like usernames, passwords, or tokens directly into your code, consider secure storage mechanisms suggested above.
- Avoid Permissive File Permissions: Ensure that file permissions are set appropriately to restrict access to sensitive files and directories. On Android for example, avoid using
MODE_WORLD_READABLE
orMODE_WORLD_WRITEABLE
when creating files, as they grant broad read or write access to all applications. Always follow the principle of least privilege when setting file permissions and limit access to only what is necessary for the application's functionality. - Input Validation: When loading data from public storage locations, always make sure to validate and sanitize it.
import android.content.Context
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import java.io.IOException
class MainActivity : AppCompatActivity() {
private lateinit var encryptedSharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize encrypted shared preferences
initializeEncryptedSharedPreferences()
val loginButton: Button = findViewById(R.id.login_button)
loginButton.setOnClickListener {
// Dummy authentication process
val token = authenticate("username", "password") // authentication logic
// Store token securely in encrypted shared preferences
storeTokenInSharedPreferences(token)
}
}
private fun initializeEncryptedSharedPreferences() {
try {
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
encryptedSharedPreferences = EncryptedSharedPreferences.create(
"secure_preferences",
masterKeyAlias,
applicationContext,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun storeTokenInSharedPreferences(token: String) {
encryptedSharedPreferences.edit().putString("jwt_token", token).apply()
Toast.makeText(this, "Token stored securely.", Toast.LENGTH_SHORT).show()
}
}
import UIKit
import Security
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let loginButton = UIButton(type: .system)
loginButton.setTitle("Login", for: .normal)
loginButton.addTarget(self, action: #selector(loginButtonTapped), for: .touchUpInside)
view.addSubview(loginButton)
loginButton.translatesAutoresizingMaskIntoConstraints = false
loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loginButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
@objc func loginButtonTapped() {
// Dummy authentication process
let token = authenticate(username: "username", password: "password")
// Store token securely in Keychain
storeTokenInKeychain(token: token)
}
private func storeTokenInKeychain(token: String) {
let keychainQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "jwtToken",
kSecValueData as String: token.data(using: .utf8)!,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
if status == errSecSuccess {
print("Token stored securely in Keychain.")
} else {
print("Failed to store token in Keychain: \(status)")
}
}
}
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final storage = FlutterSecureStorage();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Secure Storage Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
// Dummy authentication process
String token = await authenticate("username", "password");
// Store token securely
await storeTokenInSecureStorage(token);
},
child: Text('Login'),
),
),
);
}
Future<void> storeTokenInSecureStorage(String token) async {
await storage.write(key: 'jwtToken', value: token);
print('Token stored securely.');
}
}
Links
- OWASP Mobile Top 10
- Practices for Protecting Electronic Restricted Data: A Quick Reference
- CWE-200: Information Exposure
- CWE-359: Exposure of Private Information ("Privacy Violation")
Standards
- OWASP_MASVS_L1:
- MSTG_ARCH_4
- MSTG_ARCH_12
- OWASP_MASVS_L2:
- MSTG_ARCH_4
- MSTG_ARCH_12
- GDPR:
- ART_32
- ART_25
- PCI_STANDARDS:
- REQ_3_2
- REQ_3_3
- REQ_3_5
- REQ_3_6
- REQ_3_7
- REQ_4_2
- REQ_6_2
- OWASP_MASVS_v2_1:
- MASVS_STORAGE_1
- MASVS_STORAGE_2
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_6_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5