Insecure Storage of Application Data
Stockage non sécurisé des données de l'application
Description
La vulnérabilité de stockage non sécurisé dans les applications mobiles fait référence à une faille de sécurité où des données sensibles, telles que les identifiants de l'utilisateur, des informations personnelles ou d'autres données confidentielles, sont stockées sur l'appareil dans des emplacements lisibles par tous (world-readable). Cela peut rendre les données vulnérables à un accès non autorisé par d'autres applications sur l'appareil ou par un acteur malveillant ayant un accès physique à l'appareil.
Le stockage non sécurisé ne se produit pas seulement lorsque l'application écrit dans des emplacements lisibles par tous, mais aussi lorsque l'application charge des données telles que des fichiers de configuration depuis des emplacements ouverts en écriture (world-writable), où un acteur malveillant peut modifier les fichiers de configuration et par conséquent altérer le fonctionnement de l'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');
}
}
}
Recommandation
- Use Secure Storage: Stockez les données sensibles telles que les jetons d'authentification ou les identifiants de manière sécurisée. Utilisez les mécanismes de stockage sécurisé fournis par la plateforme, tels que Keychain pour iOS et SharedPreferences avec chiffrement pour Android.
- Data Encryption: Chiffrez les données sensibles au repos en utilisant des algorithmes de chiffrement robustes pour protéger les données stockées sur l'appareil de l'utilisateur.
- Avoid Hardcoding Sensitive Information: Au lieu de coder en dur (hardcoding) des informations sensibles comme des noms d'utilisateur, des mots de passe ou des jetons directement dans votre code, envisagez les mécanismes de stockage sécurisé suggérés ci-dessus.
- Avoid Permissive File Permissions: Assurez-vous que les autorisations de fichiers sont définies de manière appropriée pour restreindre l'accès aux fichiers et répertoires sensibles. Sur Android par exemple, évitez d'utiliser
MODE_WORLD_READABLEouMODE_WORLD_WRITEABLElors de la création de fichiers, car ils accordent un large accès en lecture ou en écriture à toutes les applications. Suivez toujours le principe du moindre privilège lors de la définition des autorisations de fichiers et limitez l'accès à ce qui est strictement nécessaire au fonctionnement de l'application. - Input Validation: Lors du chargement de données à partir d'emplacements de stockage publics, assurez-vous toujours de les valider et de les nettoyer (sanitize).
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.');
}
}
Liens
- OWASP Mobile Top 10
- CWE-200: Information Exposure
- CWE-359: Exposure of Private Information ("Privacy Violation")
Normes
- 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
- CNIL_FOR_EDITORS:
- EDITORS_4_3_3
- HIPAA_CONTROLS:
- SECURITY251
- SECURITY212
- SECURITY213