Personally Identifiable Information (PII) Leakage
Fuga de información de identificación personal (PII)
Descripción
La información de identificación personal (PII) es, de acuerdo con la Publicación Especial 800-122 del NIST, un término colectivo para cualquier información que pueda utilizarse para distinguir o rastrear la identidad de una persona, como el nombre, el número de seguro social, la fecha y el lugar de nacimiento, el apellido de soltera de la madre o los registros biométricos; y cualquier otra información que esté vinculada o sea vinculable a una persona, como la información médica, educativa, financiera y de empleo.
En el contexto de la seguridad móvil, la fuga de PII se produce cuando la información PII en texto claro se registra en los registros de la aplicación o en un archivo de lectura global, lo que la hace accesible a todas las aplicaciones en el dispositivo del usuario.
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)
// Suponiendo que tiene campos EditText para nombre de usuario y contraseña en su diseño
// con los identificadores 'usernameEditText' y 'passwordEditText' respectivamente
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// Registra las credenciales del usuario
Log.i("UserCredentials", "Username: $username, Password: $password")
// Realice la lógica de inicio de sesión aquí
performLogin(username, password)
// Limpia los campos EditText después de iniciar sesión
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()
// Realice cualquier configuración adicional después de cargar la vista.
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// Maneje el nombre de usuario o contraseña vacíos
return
}
// Registra las credenciales del usuario
os_log_info("Username: %@, Password: %@", log: .default, type: .info, username, password)
// Realice la lógica de inicio de sesión aquí
performLogin(username, password)
// Limpia los campos de texto después de iniciar sesión
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;
// Registra las credenciales del usuario como información
logger.i('Username: $username, Password: $password');
// Realice la lógica de inicio de sesión aquí
performLogin(username, password)
// Limpia los campos de texto después de iniciar sesión
_usernameController.clear();
_passwordController.clear();
// Opcionalmente, puede navegar a otra pantalla o realizar otras acciones después de iniciar sesión correctamente
}
}
Recomendación
- Al guardar PII/PHI localmente, asegúrese de que estén cifrados y que las claves de cifrado se almacenen en el KeyChain.
- El acceso a la información PII debe estar protegido por autenticación biométrica.
- Si corresponde, evite almacenar la información PII en la memoria caché local; en su lugar, consúltela desde los servidores backend.
- Elimine de forma segura la PII/PHI cuando ya no exista una necesidad empresarial para su retención en el dispositivo.
- Considere cifrar y/o aplicar funciones hash a los datos PHI/PII antes de guardarlos en el dispositivo.
- Proporcione a los usuarios una manera de retirar su consentimiento para la retención de sus datos PHI/PII.
- No almacene en caché los datos PII/PHI.
- Minimice el uso de bibliotecas de terceros y API que acceden a los datos del usuario.
- Algunas jurisdicciones pueden requerir que proporcione una política de privacidad para acceder a la información personal.
- Si es necesario registrar información PII, establezca el nivel de registro en debug para que la PII no aparezca en los registros de la aplicación de producción.
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)
// Suponiendo que tiene campos EditText para nombre de usuario y contraseña en su diseño
// con los identificadores 'usernameEditText' y 'passwordEditText' respectivamente
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// Registra las credenciales del usuario
Log.d("UserCredentials", "Username: $username, Password: $password")
// Realice la lógica de inicio de sesión aquí
performLogin(username, password)
// Limpia los campos EditText después de iniciar sesión
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()
// Realice cualquier configuración adicional después de cargar la vista.
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// Maneje el nombre de usuario o contraseña vacíos
return
}
// Registra las credenciales del usuario
os_log_debug("Username: %@, Password: %@", log: .default, type: .info, username, password)
// Realice la lógica de inicio de sesión aquí
performLogin(username, password)
// Limpia los campos de texto después de iniciar sesión
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;
// Registra las credenciales del usuario como información
logger.d('Username: $username, Password: $password');
// Realice la lógica de inicio de sesión aquí
performLogin(username, password)
// Limpia los campos de texto después de iniciar sesión
_usernameController.clear();
_passwordController.clear();
// Opcionalmente, puede navegar a otra pantalla o realizar otras acciones después de iniciar sesión correctamente
}
}
Enlaces
Estándares
- 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