Saltar a contenido

Insecure Storage of Application Data

Almacenamiento inseguro de datos de la aplicación

Descripción

La vulnerabilidad de almacenamiento inseguro en aplicaciones móviles se refiere a un fallo de seguridad en el que datos sensibles, como credenciales de usuario, información personal u otros datos confidenciales, se almacenan en el dispositivo en ubicaciones de lectura pública (world-readable). Esto puede dejar los datos vulnerables al acceso no autorizado por parte de otras aplicaciones en el dispositivo o por un actor malicioso con acceso físico al dispositivo.

El almacenamiento inseguro no solo ocurre cuando la aplicación escribe en ubicaciones de lectura pública, sino también cuando la aplicación carga datos, como archivos de configuración, desde ubicaciones de escritura pública (world-writable), donde un actor malicioso puede alterar los archivos de configuración y, consecuentemente, manipular la funcionalidad de la aplicación.

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');
    }
  }
}

Recomendación

  • Use Secure Storage: Almacene datos sensibles, como tokens de autenticación o credenciales, de forma segura. Utilice los mecanismos de almacenamiento seguro proporcionados por la plataforma, como Keychain para iOS y SharedPreferences con cifrado para Android.
  • Data Encryption: Cifre los datos sensibles en reposo utilizando algoritmos de cifrado robustos para proteger los datos almacenados en el dispositivo del usuario.
  • Avoid Hardcoding Sensitive Information: En lugar de codificar de forma rígida (hardcoding) información sensible como nombres de usuario, contraseñas o tokens directamente en su código, considere los mecanismos de almacenamiento seguro sugeridos anteriormente.
  • Avoid Permissive File Permissions: Asegúrese de que los permisos de archivo estén configurados adecuadamente para restringir el acceso a archivos y directorios sensibles. En Android, por ejemplo, evite utilizar MODE_WORLD_READABLE o MODE_WORLD_WRITEABLE al crear archivos, ya que otorgan un amplio acceso de lectura o escritura a todas las aplicaciones. Siga siempre el principio de mínimo privilegio al establecer los permisos de archivo y limite el acceso únicamente a lo necesario para la funcionalidad de la aplicación.
  • Input Validation: Al cargar datos desde ubicaciones de almacenamiento público, asegúrese siempre de validarlos y sanearlos.
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.');
  }
}

Enlaces

Estándares

  • 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