Personally Identifiable Information (PII) Leakage
个人身份信息(PII)泄露
描述
根据 NIST 特别出版物 800-122,个人身份信息(PII)是指可用于区分或追踪个人身份的任何信息的统称,例如姓名、社会安全号码、出生日期和地点、母亲的娘家姓或生物识别记录;以及与个人关联或可关联的任何其他信息,例如医疗、教育、财务和就业信息。
在移动安全的背景下,当明文的PII信息被记录到应用程序日志或全局可读文件中,使其可供用户设备上的所有应用程序访问时,就会发生PII泄露。
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 假设在您的布局中有用户名和密码的 EditText 字段
// 其 ID 分别为 'usernameEditText' 和 'passwordEditText'
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// 记录用户凭据
Log.i("UserCredentials", "Username: $username, Password: $password")
// 在此处执行登录逻辑
performLogin(username, password)
// 登录后清除 EditText 字段
usernameEditText.text.clear()
passwordEditText.text.clear()
}
}
}
import UIKit
import os.log
class ViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// 加载视图后进行任何其他设置。
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// 处理空的用户名或密码
return
}
// 记录用户凭据
os_log_info("Username: %@, Password: %@", log: .default, type: .info, username, password)
// 在此处执行登录逻辑
performLogin(username, password)
// 登录后清除文本字段
usernameTextField.text = ""
passwordTextField.text = ""
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logger/logger.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Login Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LoginPage(),
);
}
}
class LoginPage extends StatelessWidget {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final logger = Logger(
printer: PrettyPrinter(),
output: FileOutput('login_log.txt'),
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Username',
),
),
SizedBox(height: 10.0),
TextField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
),
obscureText: true,
),
SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
_login(context);
},
child: Text('Login'),
),
],
),
),
);
}
void _login(BuildContext context) {
final username = _usernameController.text;
final password = _passwordController.text;
// 将用户凭据记录为信息
logger.i('Username: $username, Password: $password');
// 在此处执行登录逻辑
performLogin(username, password)
// 登录后清除文本字段
_usernameController.clear();
_passwordController.clear();
// 可选:您可以在成功登录后导航到另一个屏幕或执行其他操作
}
}
建议
- 在本地保存 PII/PHI 时,确保它们已被加密,并且加密密钥存储在 KeyChain 中。
- 对PII信息的访问应受到生物识别身份验证的保护。
- 如果适用,请避免在本地缓存PII信息,而是从后端服务器查询。
- 当不再有业务需要在设备上保留PII/PHI时,安全地删除它们。
- 在将PHI/PII数据保存到设备之前,请考虑对其进行加密和/或哈希处理。
- 为用户提供撤回保留其PHI/PII数据同意的途径。
- 不要缓存PII/PHI数据。
- 尽量减少使用访问用户数据的第三方库和API。
- 某些司法管辖区可能要求您提供访问个人信息的隐私政策。
- 如果有必要记录PII信息,请将日志级别设置为debug,以确保PII不会出现在生产环境应用程序的日志中。
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 假设在您的布局中有用户名和密码的 EditText 字段
// 其 ID 分别为 'usernameEditText' 和 'passwordEditText'
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// 记录用户凭据
Log.d("UserCredentials", "Username: $username, Password: $password")
// 在此处执行登录逻辑
performLogin(username, password)
// 登录后清除 EditText 字段
usernameEditText.text.clear()
passwordEditText.text.clear()
}
}
}
import UIKit
import os.log
class ViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// 加载视图后进行任何其他设置。
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// 处理空的用户名或密码
return
}
// 记录用户凭据
os_log_debug("Username: %@, Password: %@", log: .default, type: .info, username, password)
// 在此处执行登录逻辑
performLogin(username, password)
// 登录后清除文本字段
usernameTextField.text = ""
passwordTextField.text = ""
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logger/logger.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Login Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LoginPage(),
);
}
}
class LoginPage extends StatelessWidget {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final logger = Logger(
printer: PrettyPrinter(),
output: FileOutput('login_log.txt'),
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Username',
),
),
SizedBox(height: 10.0),
TextField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
),
obscureText: true,
),
SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
_login(context);
},
child: Text('Login'),
),
],
),
),
);
}
void _login(BuildContext context) {
final username = _usernameController.text;
final password = _passwordController.text;
// 将用户凭据记录为信息
logger.d('Username: $username, Password: $password');
// 在此处执行登录逻辑
performLogin(username, password)
// 登录后清除文本字段
_usernameController.clear();
_passwordController.clear();
// 可选:您可以在成功登录后导航到另一个屏幕或执行其他操作
}
}
链接
标准
- OWASP_MASVS_L1:
- MSTG_ARCH_12
- OWASP_MASVS_L2:
- 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_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- HIPAA_CONTROLS:
- SECURITY251
- SECURITY212
- SECURITY213