Personally Identifiable Information (PII) Leakage
Personally Identifiable Information (PII) Leakage
Description
Personally Identifiable Information (PII) is, according to NIST Special Publication 800-122, a collective term for any information that can be used to distinguish or trace an individual's identity, such as name, social security number, date and place of birth, mother's maiden name, or biometric records; and any other information that is linked or linkable to an individual, such as medical, educational, financial, and employment information.
In the context of mobile security, PII leakage occurs when plain text PII information is logged to application logs or a world-readable file making it accessible to all applications on the user device.
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)
// Assuming you have EditText fields for username and password in your layout
// with ids 'usernameEditText' and 'passwordEditText' respectively
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// Log the user credentials
Log.i("UserCredentials", "Username: $username, Password: $password")
// Perform login logic here
performLogin(username, password)
// Clear the EditText fields after login
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()
// Do any additional setup after loading the view.
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// Handle empty username or password
return
}
// Log user credentials
os_log_info("Username: %@, Password: %@", log: .default, type: .info, username, password)
// Perform login logic here
performLogin(username, password)
// Clear text fields after login
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;
// Log user credentials as information
logger.i('Username: $username, Password: $password');
// Perform login logic here
performLogin(username, password)
// Clear text fields after login
_usernameController.clear();
_passwordController.clear();
// Optionally, you can navigate to another screen or perform other actions after successful login
}
}
Recommendation
- When saving PII/PHI locally, make sure they're encrypted and encryption keys are stored in the KeyChain.
- Access to the PII information should be protected by biometric authentication.
- If applicable, avoid caching PII information locally, instead, query it from the backend servers.
- Securely delete PII/PHI when there is no longer a business need for its retention on the device.
- Consider encrypting and/or hashing PHI/PII data before saving it on the device.
- Provide users with way to withdraw consent for holding their PHI/PII data.
- Do not cache PII/PHI data.
- Minimize the use of third party libraries and APIs that access user data.
- Some jurisdictions may require you to provide a privacy policy for accessing personal information.
- If logging PII information is necessary, set the logging level to debug so that PII doesn't in production application logs.
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)
// Assuming you have EditText fields for username and password in your layout
// with ids 'usernameEditText' and 'passwordEditText' respectively
loginButton.setOnClickListener {
val username = usernameEditText.text.toString()
val password = passwordEditText.text.toString()
// Log the user credentials
Log.d("UserCredentials", "Username: $username, Password: $password")
// Perform login logic here
performLogin(username, password)
// Clear the EditText fields after login
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()
// Do any additional setup after loading the view.
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text, !username.isEmpty,
let password = passwordTextField.text, !password.isEmpty else {
// Handle empty username or password
return
}
// Log user credentials
os_log_debug("Username: %@, Password: %@", log: .default, type: .info, username, password)
// Perform login logic here
performLogin(username, password)
// Clear text fields after login
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;
// Log user credentials as information
logger.d('Username: $username, Password: $password');
// Perform login logic here
performLogin(username, password)
// Clear text fields after login
_usernameController.clear();
_passwordController.clear();
// Optionally, you can navigate to another screen or perform other actions after successful login
}
}
Links
- Practices for Protecting Electronic Restricted Data: A Quick Reference
- CWE-200: Information Exposure
- CWE-359: Exposure of Private Information ("Privacy Violation")
Standards
- 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