Insecure Storage of Application Data
应用程序数据的不安全存储
描述
移动应用程序中的不安全存储漏洞是指一种安全缺陷,即用户凭据、个人信息或其他机密数据等敏感数据被存储在设备上任何人都可读取(world-readable)的位置。这可能会使数据容易受到设备上其他应用程序或具有物理访问权限的恶意行为者的未经授权访问。
不安全存储不仅发生在应用程序写入任何人都可读取的位置时,还发生在应用程序从任何人可写入(world-writable)的位置加载数据(如配置文件)时,恶意行为者可以在这些位置更改配置文件,从而篡改应用程序的功能。
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');
}
}
}
建议
- Use Secure Storage: 安全地存储敏感数据,例如身份验证令牌或凭据。使用平台提供的安全存储机制,例如 iOS 的 Keychain 和 Android 带加密的 SharedPreferences。
- Data Encryption: 使用强大的加密算法对静态数据进行加密,以保护存储在用户设备上的数据。
- Avoid Hardcoding Sensitive Information: 不要将用户名、密码或令牌等敏感信息直接硬编码(hardcoding)到您的代码中,而应考虑上面建议的安全存储机制。
- Avoid Permissive File Permissions: 确保适当地设置文件权限以限制对敏感文件和目录的访问。例如在 Android 上,创建文件时避免使用
MODE_WORLD_READABLE或MODE_WORLD_WRITEABLE,因为它们授予所有应用程序广泛的读或写访问权限。在设置文件权限时,始终遵循最小权限原则,并将访问权限限制为应用程序功能所必需的。 - Input Validation: 从公共存储位置加载数据时,始终确保对其进行验证(validate)和清理(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.');
}
}
链接
- OWASP Mobile Top 10
- CWE-200: Information Exposure
- CWE-359: Exposure of Private Information ("Privacy Violation")
标准
- 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