Aller au contenu

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_READABLE ou MODE_WORLD_WRITEABLE lors 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

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