跳转至

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