Personally Identifiable Information (PII) Leakage
Fuite d'informations personnellement identifiables (PII)
Description
Les informations personnellement identifiables (PII) sont, selon la publication spéciale 800-122 du NIST, un terme collectif désignant toute information pouvant être utilisée pour distinguer ou tracer l'identité d'un individu, telle que le nom, le numéro de sécurité sociale, la date et le lieu de naissance, le nom de jeune fille de la mère ou des données biométriques ; ainsi que toute autre information liée ou liable à un individu, telle que des informations médicales, éducatives, financières et professionnelles.
Dans le contexte de la sécurité mobile, la fuite de PII se produit lorsque des informations PII en texte clair sont enregistrées dans les journaux de l'application ou dans un fichier lisible par tous, ce qui les rend accessibles à toutes les applications de l'appareil de l'utilisateur.
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)
// En supposant que vous avez des champs EditText pour le nom d'utilisateur et le mot de passe dans votre disposition
// avec les identifiants 'usernameEditText' et 'passwordEditText' respectivement
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// Enregistre les informations d'identification de l'utilisateur
Log.i("UserCredentials", "Username: $username, Password: $password")
// Effectuez la logique de connexion ici
performLogin(username, password)
// Efface les champs EditText après la connexion
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()
// Effectuez toute configuration supplémentaire après le chargement de la vue.
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// Gérez un nom d'utilisateur ou un mot de passe vide
return
}
// Enregistre les informations d'identification de l'utilisateur
os_log_info("Username: %@, Password: %@", log: .default, type: .info, username, password)
// Effectuez la logique de connexion ici
performLogin(username, password)
// Efface les champs de texte après la connexion
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;
// Enregistre les informations d'identification de l'utilisateur à titre d'information
logger.i('Username: $username, Password: $password');
// Effectuez la logique de connexion ici
performLogin(username, password)
// Efface les champs de texte après la connexion
_usernameController.clear();
_passwordController.clear();
// En option, vous pouvez naviguer vers un autre écran ou effectuer d'autres actions après une connexion réussie
}
}
Recommandation
- Lors de la sauvegarde locale des données PII/PHI, assurez-vous qu'elles sont chiffrées et que les clés de chiffrement sont stockées dans le KeyChain.
- L'accès aux informations PII doit être protégé par une authentification biométrique.
- Si possible, évitez de mettre en cache les informations PII localement ; interrogez-les plutôt depuis les serveurs backend.
- Supprimez de manière sécurisée les données PII/PHI lorsqu'il n'y a plus de besoin professionnel de les conserver sur l'appareil.
- Envisagez de chiffrer et/ou de hacher les données PHI/PII avant de les enregistrer sur l'appareil.
- Fournissez aux utilisateurs un moyen de retirer leur consentement à la conservation de leurs données PHI/PII.
- Ne mettez pas en cache les données PII/PHI.
- Minimisez l'utilisation de bibliothèques et d'API tierces qui accèdent aux données des utilisateurs.
- Certaines juridictions peuvent exiger que vous fournissiez une politique de confidentialité pour accéder aux informations personnelles.
- S'il est nécessaire de consigner des informations PII, définissez le niveau de journalisation sur debug afin que les PII n'apparaissent pas dans les journaux de l'application en production.
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)
// En supposant que vous avez des champs EditText pour le nom d'utilisateur et le mot de passe dans votre disposition
// avec les identifiants 'usernameEditText' et 'passwordEditText' respectivement
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// Enregistre les informations d'identification de l'utilisateur
Log.d("UserCredentials", "Username: $username, Password: $password")
// Effectuez la logique de connexion ici
performLogin(username, password)
// Efface les champs EditText après la connexion
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()
// Effectuez toute configuration supplémentaire après le chargement de la vue.
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// Gérez un nom d'utilisateur ou un mot de passe vide
return
}
// Enregistre les informations d'identification de l'utilisateur
os_log_debug("Username: %@, Password: %@", log: .default, type: .info, username, password)
// Effectuez la logique de connexion ici
performLogin(username, password)
// Efface les champs de texte après la connexion
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;
// Enregistre les informations d'identification de l'utilisateur à titre d'information
logger.d('Username: $username, Password: $password');
// Effectuez la logique de connexion ici
performLogin(username, password)
// Efface les champs de texte après la connexion
_usernameController.clear();
_passwordController.clear();
// En option, vous pouvez naviguer vers un autre écran ou effectuer d'autres actions après une connexion réussie
}
}
Liens
Normes
- 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