13 ส.ค. 2568·อ่าน 2 นาที

เช็คลิสต์การจัดเก็บอย่างปลอดภัยใน Kotlin สำหรับ tokens คีย์ และ PII

เช็คลิสต์การจัดเก็บอย่างปลอดภัยใน Kotlin เพื่อช่วยเลือกระหว่าง Android Keystore, EncryptedSharedPreferences และการเข้ารหัสฐานข้อมูลสำหรับ tokens คีย์ และ PII

เช็คลิสต์การจัดเก็บอย่างปลอดภัยใน Kotlin สำหรับ tokens คีย์ และ PII

สิ่งที่คุณพยายามปกป้อง (อธิบายง่าย ๆ)

การจัดเก็บอย่างปลอดภัยในแอปธุรกิจหมายถึงอย่างเดียว: ถ้ามีคนได้โทรศัพท์ (หรือไฟล์ของแอป) พวกเขายังไม่ควรอ่านหรือใช้ซ้ำสิ่งที่คุณบันทึกไว้ได้ นั่นรวมถึงข้อมูลที่พักอยู่บนดิสก์ และความลับที่รั่วผ่านการสำรองข้อมูล บันทึกข้อผิดพลาด (logs) รายงานการชน หรือเครื่องมือดีบัก

การทดสอบคิดง่าย ๆ: คนแปลกหน้าอาจทำอะไรได้บ้างถ้าเปิดโฟลเดอร์เก็บข้อมูลของแอปคุณ? ในหลายแอป ของมีค่าที่สุดไม่ใช่รูปหรือการตั้งค่า แต่เป็นสตริงสั้น ๆ ที่ปลดล็อกการเข้าถึง

การเก็บข้อมูลบนเครื่องมักจะมี session tokens (เพื่อให้ผู้ใช้คงสถานะล็อกอิน), refresh tokens, API keys, คีย์การเข้ารหัส, ข้อมูลส่วนบุคคล (PII) เช่น ชื่อและอีเมล, และข้อมูลธุรกิจที่แคชไว้เพื่อใช้งานแบบออฟไลน์ (คำสั่งซื้อ ตั๋ว หรือบันทึกลูกค้า)

ตัวอย่างความล้มเหลวในโลกจริง:

  • อุปกรณ์สูญหายหรือถูกขโมยและโทเค็นถูกคัดลอกเพื่อแอบอ้างตัวผู้ใช้
  • มัลแวร์หรือแอป "ผู้ช่วย" อ่านไฟล์ท้องถิ่นบนอุปกรณ์ที่รูทแล้วหรือใช้ช่องโหว่การเข้าถึง
  • การสำรองข้อมูลอัตโนมัติย้ายข้อมูลแอปไปที่ที่คุณไม่ได้วางแผน
  • บิลด์สำหรับดีบักบันทึกโทเค็น เขียนลงในรายงานการชน หรือลดการตรวจสอบความปลอดภัย

นั่นเป็นเหตุผลที่ "แค่เก็บใน SharedPreferences" ไม่พอสำหรับสิ่งที่ให้การเข้าถึง (tokens) หรือตีความว่าทำร้ายผู้ใช้และบริษัทได้ (PII). SharedPreferences ธรรมดาเหมือนการเขียนความลับลงบนโพสต์-อิทในแอป: สะดวก แต่ก็อ่านได้ง่ายหากมีคนเข้าถึง

จุดเริ่มต้นที่เป็นประโยชน์คือระบุชื่อแต่ละรายการที่เก็บและถามสองคำถาม: มันปลดล็อกอะไรหรือไม่ และจะเป็นปัญหาไหมถ้ามันรั่วไหล? ส่วนที่เหลือ (Keystore, EncryptedSharedPreferences, ฐานข้อมูลเข้ารหัส) ตามมาจากคำตอบนั้น

จัดประเภทข้อมูลของคุณ: tokens, keys, และ PII

การจัดเก็บอย่างปลอดภัยจะง่ายขึ้นเมื่อคุณไม่ปฏิบัติต่อ "ข้อมูลที่อ่อนไหว" ทั้งหมดเหมือนกัน เริ่มจากการลิสต์สิ่งที่แอปเก็บและจะเกิดอะไรขึ้นถ้ามันรั่วไหล

Tokens ไม่เหมือนรหัสผ่าน Access token และ refresh token ถูกออกแบบมาเพื่อเก็บไว้เพื่อให้ผู้ใช้คงสถานะล็อกอิน แต่ก็ยังเป็นความลับที่มีมูลค่าสูง รหัสผ่านไม่ควรถูกเก็บไว้เลย หากต้องมีการล็อกอิน เก็บเฉพาะสิ่งที่จำเป็นสำหรับเซสชัน (โดยปกติคือ tokens) และพึ่งพาเซิร์ฟเวอร์ในการตรวจสอบรหัสผ่าน

Keys เป็นชั้นข้อมูลที่ต่างออกไป API keys, signing keys, และคีย์เข้ารหัสสามารถปลดล็อกระบบทั้งระบบได้ หากมีคนสกัดออกมาจากอุปกรณ์ พวกเขาสามารถทำการละเมิดอัตโนมัติในระดับกว้าง กฎที่ดี: ถ้าค่าหนึ่งสามารถใช้ภายนอกแอปเพื่อแอบอ้างแอปหรือถอดรหัสข้อมูล ให้ถือว่ามีความเสี่ยงสูงกว่าท็อคเค็นของผู้ใช้

PII คือข้อมูลที่สามารถระบุตัวบุคคล: อีเมล เบอร์โทร ที่อยู่บ้าน บันทึกลูกค้า บัตรประจำตัวประชาชน ข้อมูลสุขภาพหรือการเงิน ฟิลด์ที่ดูไม่เป็นอันตรายก็กลายเป็นข้อมูลอ่อนไหวเมื่อรวมกัน

ระบบป้ายกำกับที่ใช้งานได้จริง:

  • Session secrets: access token, refresh token, session cookie
  • App secrets: API keys, signing keys, encryption keys (พยายามหลีกเลี่ยงการวางไว้บนอุปกรณ์เมื่อเป็นไปได้)
  • User data (PII): รายละเอียดโปรไฟล์ ตัวระบุ เอกสาร ข้อมูลสุขภาพหรือการเงิน
  • Device and analytics IDs: advertising ID, device ID, install ID (ยังถือว่าอ่อนไหวตามนโยบายหลายอย่าง)

Android Keystore: เมื่อใดควรใช้

Android Keystore เหมาะเมื่อคุณต้องปกป้องความลับที่ไม่ควรออกจากอุปกรณ์ในรูปแบบ plaintext มันเป็นตู้เซฟสำหรับคีย์คริปโต ไม่ใช่ที่เก็บฐานข้อมูลสำหรับข้อมูลของคุณ

สิ่งที่มันทำได้ดี: สร้างและถือคีย์ที่ใช้สำหรับการเข้ารหัส การถอดรหัส การลงลายมือชื่อ หรือการตรวจสอบ โดยทั่วไปคุณจะเข้ารหัสโทเค็นหรือข้อมูลออฟไลน์ที่อื่น แล้วใช้คีย์ใน Keystore เพื่อปลดล็อก

คีย์ที่สนับสนุนฮาร์ดแวร์: ความหมายที่แท้จริง

ในอุปกรณ์หลายรุ่น คีย์ใน Keystore อาจได้รับการสนับสนุนโดยฮาร์ดแวร์ นั่นหมายความว่าการดำเนินการกับคีย์เกิดขึ้นภายในสภาพแวดล้อมที่ป้องกันและวัสดุของคีย์ไม่สามารถสกัดออกมาได้ ซึ่งลดความเสี่ยงจากมัลแวร์ที่อ่านไฟล์แอป

การมีฮาร์ดแวร์แบบนี้ไม่ได้รับประกันในทุกอุปกรณ์ และพฤติกรรมแตกต่างตามรุ่นและเวอร์ชัน Android ออกแบบเผื่อสถานการณ์ที่การดำเนินการกับคีย์อาจล้มเหลว

การกั้นด้วยการยืนยันตัวผู้ใช้

Keystore สามารถกำหนดให้ต้องมีการยืนยันผู้ใช้ก่อนใช้คีย์ นั่นคือวิธีผูกการเข้าถึงกับไบโอเมตริกซ์หรือข้อมูลประจำเครื่อง ตัวอย่างเช่น คุณสามารถเข้ารหัสโทเค็นสำหรับส่งออกและถอดรหัสได้เฉพาะหลังผู้ใช้ยืนยันด้วยลายนิ้วมือหรือ PIN

Keystore เหมาะเมื่อคุณต้องการคีย์ที่ไม่สามารถส่งออกได้ ต้องการการอนุมัติด้วยไบโอเมตริกซ์หรือข้อมูลประจำเครื่องสำหรับการกระทำที่อ่อนไหว และต้องการความลับเฉพาะอุปกรณ์ที่ไม่ซิงก์หรือไม่รวมอยู่ในการสำรองข้อมูล

วางแผนรับกับปัญหา: คีย์อาจถูกยกเลิกหลังจากเปลี่ยนหน้าจอล็อก การเปลี่ยนไบโอเมตริกซ์ หรือเหตุการณ์ด้านความปลอดภัย คาดหวังความล้มเหลวและมีแผนสำรองที่ชัดเจน: ตรวจจับคีย์ที่ไม่ถูกต้อง ล้างบลอบที่เข้ารหัส และขอให้ผู้ใช้ลงชื่อเข้าใช้ใหม่

EncryptedSharedPreferences: เมื่อไหร่ถึงพอ

EncryptedSharedPreferences เป็นค่าเริ่มต้นที่ดีสำหรับชุดความลับขนาดเล็กในรูปแบบคีย์-ค่า มันคือ "SharedPreferences ที่ถูกเข้ารหัส" ดังนั้นใครจะมาเปิดไฟล์แล้วอ่านค่าไม่ได้ง่าย ๆ

เบื้องหลังมันใช้ master key ในการเข้ารหัสและถอดรหัสค่า คีย์หลักนั้นถูกปกป้องโดย Android Keystore ดังนั้นแอปของคุณจะไม่เก็บคีย์การเข้ารหัสแบบ plaintext

โดยทั่วไปพอเพียงสำหรับรายการเล็ก ๆ ที่อ่านบ่อย เช่น access และ refresh token, session ID, device ID, ธงสภาพแวดล้อม หรือสถานะเล็ก ๆ เช่น เวลาซิงค์ล่าสุด เหมาะกับชิ้นส่วนข้อมูลผู้ใช้เล็ก ๆ เท่านั้นถ้าจำเป็นจริง ๆ แต่ไม่ควรเป็นที่ทิ้ง PII

มันไม่เหมาะกับข้อมูลขนาดใหญ่หรือมีโครงสร้าง หากคุณต้องการรายการออฟไลน์ การค้นหา หรือการสอบถามตามฟิลด์ (customers, tickets, orders) EncryptedSharedPreferences จะช้าและไม่สะดวก จุดนั้นคุณควรไปหาฐานข้อมูลเข้ารหัส

กฎง่าย ๆ: ถ้าคุณสามารถลิสต์ทุกคีย์ที่เก็บได้บนหน้าจอเดียว EncryptedSharedPreferences น่าจะพอ ถ้าต้องการแถวและการค้นหา ให้เปลี่ยนไปใช้ฐานข้อมูล

การเข้ารหัสฐานข้อมูล: เมื่อจำเป็น

Ship login and sessions faster
Use pre-built authentication so you can focus on secure storage rules and app behavior.
Add Auth

การเข้ารหัสฐานข้อมูลสำคัญเมื่อคุณเก็บมากกว่าการตั้งค่านิดหน่อย หากแอปของคุณเก็บข้อมูลธุรกิจในเครื่อง ให้ถือว่ามันอาจถูกสกัดจากโทรศัพท์ที่หายเว้นแต่คุณปกป้องมัน

ฐานข้อมูลเหมาะเมื่อคุณต้องการการเข้าถึงแบบออฟไลน์กับเรกคอร์ด แคชท้องถิ่นเพื่อประสิทธิภาพ ประวัติ/ตราประทับ หรือบันทึกและไฟล์แนบขนาดยาว

วิธีการเข้ารหัสสองแบบที่พบบ่อย

การเข้ารหัสทั้งไฟล์ฐานข้อมูล (มักแบบ SQLCipher) เข้ารหัสไฟล์ทั้งหมดตามที่เก็บไว้ แอปของคุณเปิดด้วยคีย์ นี่คิดง่ายเพราะไม่ต้องจำว่าคอลัมน์ไหนถูกปกป้อง

การเข้ารหัสที่ชั้นแอปในฟิลด์ เข้ารหัสเฉพาะฟิลด์ก่อนเขียนแล้วถอดรหัสหลังอ่าน ทำงานได้ถ้าส่วนใหญ่ของเรกคอร์ดไม่อ่อนไหว หรือถ้าคุณพยายามรักษาโครงสร้างฐานข้อมูลเดิมโดยไม่เปลี่ยนไฟล์

การแลกเปลี่ยน: ความลับเทียบกับการค้นหาและการเรียง

การเข้ารหัสทั้งฐานข้อมูลซ่อนทุกอย่างบนดิสก์ แต่เมื่อปลดล็อกแล้วแอปสามารถสอบถามปกติได้

การเข้ารหัสฟิลด์ปกป้องคอลัมน์เฉพาะ แต่คุณจะสูญเสียการค้นหาและการเรียงลำดับที่ง่าย การเรียงชื่อตามนามสกุลที่ถูกเข้ารหัสจะไม่ทำงานได้ดี และการค้นหาต้องเป็น "ค้นหาหลังถอดรหัส" (ช้า) หรือ "เก็บดัชนีเพิ่มเติม" (ซับซ้อนขึ้นและมีความเสี่ยงในการรั่ว)

พื้นฐานการจัดการคีย์

คีย์ฐานข้อมูลไม่ควรถูกฝังหรือส่งมากับแอป รูปแบบที่ใช้กันทั่วไปคือสร้างคีย์ฐานข้อมูลแบบสุ่ม แล้วเก็บไว้ในรูปแบบที่ห่อ (wrapped) ซึ่งเข้ารหัสด้วยคีย์ที่เก็บใน Android Keystore เมื่อออกจากระบบ คุณสามารถลบคีย์ที่ห่อไว้และถือว่าฐานข้อมูลท้องถิ่นทิ้งได้ หรือจะเก็บไว้ถ้าแอปต้องทำงานออฟไลน์ข้ามเซสชัน

วิธีเลือก: เปรียบเทียบเชิงปฏิบัติ

Turn data models into real apps
Model data in PostgreSQL visually, then generate working APIs and clients.
Start Building

คุณไม่ได้เลือก "ตัวเลือกที่ปลอดภัยที่สุด" โดยรวม แต่เลือกตัวเลือกที่ปลอดภัยพอและเหมาะกับการใช้งานข้อมูลของแอป

คำถามที่ช่วยให้เลือกได้ถูกต้อง:

  • ข้อมูลอ่านบ่อยแค่ไหน (เปิดแอปทุกครั้งหรือไม่บ่อย)?
  • ข้อมูลมีขนาดเท่าไหร่ (ไม่กี่ไบต์หรือนับพันเรกคอร์ด)?
  • จะเกิดอะไรขึ้นถ้ามันรั่ว (แค่รำคาญ มีค่าใช้จ่าย หรือต้องแจ้งตามกฎหมาย)?
  • จำเป็นต้องเข้าถึงแบบออฟไลน์ ค้นหา หรือเรียงลำดับหรือไม่?
  • มีข้อกำหนดด้านความสอดคล้อง (การเก็บรักษา การตรวจสอบ กฎการเข้ารหัส) หรือไม่?

แมปที่ใช้ได้จริง:

  • Tokens (OAuth access และ refresh tokens) มักอยู่ใน EncryptedSharedPreferences เพราะมีขนาดเล็กและอ่านบ่อย
  • วัสดุคีย์ ควรอยู่ใน Android Keystore เมื่อเป็นไปได้เพื่อลดโอกาสที่ถูกคัดลอกออกจากอุปกรณ์
  • PII และข้อมูลธุรกิจออฟไลน์ มักต้องเข้ารหัสฐานข้อมูลเมื่อเก็บมากกว่าบางฟิลด์หรือจำเป็นต้องกรอง/ค้นหา

ข้อมูลผสมเป็นเรื่องปกติในแอปธุรกิจ รูปแบบปฏิบัติคือสร้าง data encryption key (DEK) แบบสุ่มสำหรับฐานข้อมูลหรือไฟล์ท้องถิ่น เก็บเฉพาะ DEK ที่ห่อไว้ด้วยคีย์ที่มาจาก Keystore และหมุนคีย์เมื่อจำเป็น

ถ้าไม่แน่ใจ ให้เลือกเส้นทางที่ปลอดภัยและเรียบง่าย: เก็บน้อยลง หลีกเลี่ยงการเก็บ PII แบบออฟไลน์ถ้าไม่จำเป็น และเก็บคีย์ไว้ใน Keystore

ขั้นตอนทีละขั้นตอน: นำไปใช้ในแอป Kotlin

เริ่มจากเขียนลงว่าแต่ละค่าที่จะเก็บบนอุปกรณ์มีอะไรบ้างและเหตุผลที่ต้องเก็บ นี่เป็นวิธีเร็วสุดจะป้องกันการเก็บแบบ "กันไว้ดีกว่า"

ก่อนเขียนโค้ด ให้กำหนดกฎ: แต่ละไอเท็มอยู่ได้นานเท่าไร ควรถูกแทนที่เมื่อไร และ "ล็อกเอาต์" หมายถึงอะไร โทเค็นเข้าถึงอาจหมดเวลาใน 15 นาที refresh token อาจนานกว่า และ PII ออฟไลน์อาจต้องมีกฎ "ลบหลัง 30 วัน"

การนำไปใช้ที่ยังดูแลได้:

  • สร้าง wrapper เดียว "SecureStorage" เพื่อให้ส่วนอื่นของแอปไม่ต้องสัมผัส SharedPreferences, Keystore, หรือฐานข้อมูลโดยตรง
  • วางแต่ละไอเท็มในที่ที่เหมาะ: tokens ใน EncryptedSharedPreferences, คีย์การเข้ารหัสปกป้องด้วย Android Keystore, และชุดข้อมูลออฟไลน์ใหญ่ในฐานข้อมูลเข้ารหัส
  • จัดการความล้มเหลวอย่างตั้งใจ หากการจัดเก็บอย่างปลอดภัยล้มเหลว ให้ล้มแบบปิด (fail closed) อย่ากลับไปเก็บแบบ plaintext โดยเงียบ ๆ
  • เพิ่มการวินิจฉัยโดยไม่รั่วข้อมูล: บันทึกประเภทเหตุการณ์และรหัสข้อผิดพลาด อย่าบันทึก tokens คีย์ หรือรายละเอียดผู้ใช้
  • ผูกเส้นทางการลบ: logout, การลบบัญชี, และ "clear app data" ควรไหลเข้า routine การลบเดียวกัน

จากนั้นทดสอบกรณีที่น่าเบื่อซึ่งมักทำให้การจัดเก็บอย่างปลอดภัยล้มเหลวในโปรดักชัน: การคืนค่าจากแบ็กอัพ การอัปเกรดจากเวอร์ชันเก่า การเปลี่ยนการตั้งค่าล็อกหน้าจอ การย้ายไปเครื่องใหม่ ตรวจสอบให้แน่ใจผู้ใช้ไม่ติดอยู่ในลูปที่ข้อมูลที่เก็บไม่สามารถถอดรหัสได้แต่แอปยังพยายามซ้ำ

สุดท้าย เขียนบันทึกการตัดสินใจในหน้าเดียวให้ทีมทั้งทีมตามได้: เก็บอะไรไว้ที่ไหน ระยะเวลาเก็บ และจะเกิดอะไรขึ้นเมื่อถอดรหัสล้มเหลว

ข้อผิดพลาดทั่วไปที่ทำให้การจัดเก็บล้มเหลว

Build the app behind your checklist
Build a business app with backend, web, and native mobile without starting from scratch.
Try AppMaster

ความล้มเหลวส่วนใหญ่ไม่ใช่เพราะเลือกไลบรารีผิด แต่เกิดจากทางลัดเล็ก ๆ ที่เงียบ ๆ คัดลอกความลับไปยังที่ที่คุณไม่ได้ตั้งใจ

สัญญาณเตือนที่ใหญ่ที่สุดคือ refresh token (หรือ token ยาว) ถูกเก็บเป็น plaintext ที่ไหนสักแห่ง: SharedPreferences, ไฟล์, แคชชั่วคราว, หรือคอลัมน์ฐานข้อมูล ท็อคเค็นนั้นสามารถมีชีวิตยืนยาวกว่ารหัสผ่านได้หากผู้โจมตีได้แบ็กอัพ ดัมพ์อุปกรณ์รูท หรือชิ้นงานดีบัก

ความลับยังรั่วผ่านการมองเห็นไม่ใช่แค่การจัดเก็บ การบันทึก header เต็มรูปแบบในคำขอ การพิมพ์โทเค็นระหว่างดีบัก หรือการแนบบริบทช่วยเหลือไปกับรายงานการชนและเหตุการณ์วิเคราะห์สามารถเปิดเผยข้อมูลรับรองนอกอุปกรณ์ได้ จงถือว่า logs เป็นสาธารณะ

การจัดการคีย์ก็เป็นช่องว่างบ่อยครั้ง การใช้คีย์เดียวกับทุกอย่างเพิ่มขนาดความเสียหาย การไม่หมุนคีย์ทำให้การละเมิดเก่ายังใช้ได้ มีแผนสำหรับการเวอร์ชันคีย์ การหมุน และการจัดการข้อมูลที่เข้ารหัสเก่า

อย่าลืมทางออกนอก "ตู้นิรภัย"

การเข้ารหัสไม่หยุดการแบ็กอัพบนคลาวด์จากการคัดลอกข้อมูลท้องถิ่น มันไม่หยุดการจับภาพหน้าจอหรือการบันทึกหน้าจอไม่ให้จับ PII ไม่หยุดบิลด์ดีบักที่ตั้งค่าหละหลวม หรือฟีเจอร์ส่งออก (CSV/share) ที่รั่วไหลฟิลด์สำคัญ การใช้คลิปบอร์ดยังสามารถรั่วรหัสครั้งเดียวหรือหมายเลขบัญชีได้

นอกจากนี้ การเข้ารหัสไม่แก้ปัญหา authorization ถ้าแอปโชว์ PII หลังผู้ใช้ล็อกเอาต์หรือเก็บแคชที่เข้าถึงได้โดยไม่ต้องยืนยัน นั่นคือบั๊กควบคุมการเข้าถึง ล็อก UI ลบแคชที่อ่อนไหวตอน logout และตรวจสอบสิทธิ์ก่อนแสดงข้อมูลปกป้อง

รายละเอียดเชิงปฏิบัติ: วงจรชีวิต ล็อกเอาต์ และกรณีชายขอบ

การจัดเก็บอย่างปลอดภัยไม่ใช่แค่วางความลับ มันคือพฤติกรรมของพวกมันตามเวลา: เมื่อแอปหลับ เมื่อผู้ใช้ล็อกเอาต์ และเมื่ออุปกรณ์ล็อก

สำหรับ token วางแผนวงจรชีวิตเต็มรูปแบบ Access token ควรมีอายุสั้น Refresh token ควรถูกปฏิบัติเหมือนรหัสผ่าน ถ้า token หมดอายุ ให้รีเฟรชเงียบ ๆ ถ้ารีเฟรชล้มเหลว (ถูกเพิกถอน รหัสผ่านเปลี่ยน อุปกรณ์ถูกลบ) หยุดการลองซ้ำและบังคับให้ลงชื่อเข้าใช้ใหม่ รองรับการเพิกถอนฝั่งเซิร์ฟเวอร์ การเก็บท้องถิ่นไม่มีทางช่วยได้หากคุณไม่เพิกถอนข้อมูลรับรองที่ถูกขโมย

ใช้ไบโอเมตริกซ์เพื่อยืนยันตัวใหม่ ไม่ใช่สำหรับทุกอย่าง กระตุ้นเมื่อการกระทำมีความเสี่ยงจริง (ดู PII, ส่งออกข้อมูล, เปลี่ยนรายละเอียดจ่ายเงิน, แสดงรหัสครั้งเดียว) อย่าแสดงคำขอทุกครั้งที่เปิดแอป

เมื่อ logout ให้เข้มงวดและคาดเดาได้:

  • เคลียร์สำเนาในหน่วยความจำก่อน (tokens ที่แคชใน singleton, interceptor, หรือ ViewModel)
  • ลบ tokens และสถานะเซสชันที่เก็บไว้ (รวม refresh tokens)
  • ลบหรือเพิกถอนคีย์เข้ารหัสท้องถิ่นถ้าดีไซน์รองรับ
  • ลบบันทึก PII ออฟไลน์และการตอบกลับ API ที่แคชไว้
  • ปิดงาน background ที่อาจดึงข้อมูลกลับมา

กรณีชายขอบสำคัญในแอปธุรกิจ: หลายบัญชีบนเครื่องเดียว, โปรไฟล์งาน, backup/restore, การโอนเครื่อง, และการ logout แบบบางส่วน (เปลี่ยนบริษัท/workspace แทนออกจากระบบทั้งหมด) ทดสอบ force stop, อัปเกรด OS, และการเปลี่ยนเวลาเพราะความเบี้ยวของเวลาอาจทำให้การหมดอายุทำงานผิดพลาด

การตรวจจับการดัดแปลงเป็นการแลกเปลี่ยน จุดเช็คพื้นฐาน (บิลด์ debuggable, ธง emulator, สัญญาณรูทง่าย ๆ, คำตัดสิน Play Integrity) ลดการโจมตีแบบสมัครเล่น แต่ผู้โจมตีที่มุ่งมั่นจะหลบได้ ให้ใช้สัญญาณเหล่านี้เป็นอินพุตความเสี่ยง: จำกัดการเข้าถึงออฟไลน์, ขอการยืนยันตัวใหม่, และบันทึกเหตุการณ์

เช็คลิสต์ด่วนก่อนปล่อย

Support offline work safely
Create offline-friendly apps for orders, tickets, and notes with clear sync and logout behavior.
Build Offline

ใช้สิ่งนี้ก่อนรีลีส โฟกัสจุดที่การจัดเก็บล้มเหลวในแอปธุรกิจจริง

  • ถือว่าอุปกรณ์อาจเป็นศัตรู. ถ้าผู้โจมตีมีอุปกรณ์รูทหรืออิมเมจเต็มของอุปกรณ์ พวกเขาอ่าน tokens, คีย์, หรือ PII จากไฟล์แอป, preferences, logs, หรือภาพหน้าจอได้หรือไม่? ถ้าคำตอบคือ "อาจจะ" ให้ย้ายความลับไปยังการป้องกันด้วย Keystore และเก็บ payload ให้เข้ารหัส
  • ตรวจสอบการสำรองข้อมูลและการย้ายเครื่อง. เก็บไฟล์อ่อนไหวให้อยู่ห่างจาก Android Auto Backup, การสำรองข้อมูลบนคลาวด์, และการโอนเครื่อง หากการคืนค่าคีย์หายไปทำให้ไม่สามารถถอดรหัสได้ ให้วางแผนการกู้คืน (ขอให้ผู้ใช้ลงชื่อเข้าใหม่และดาวน์โหลดอีกครั้งแทนที่จะพยายามถอดรหัส)
  • ค้นหา plaintext บนดิสก์โดยไม่ได้ตั้งใจ. หาชั่วคราว ไฟล์แคช HTTP รายงานการชน เหตุการณ์วิเคราะห์ และแคชภาพที่อาจมี PII หรือ tokens ตรวจสอบการบันทึกขณะดีบักและ JSON dumps
  • กำหนดอายุและหมุน. Access token ควรสั้น Refresh token ควรถูกปกป้อง และเซสชันฝั่งเซิร์ฟเวอร์ควรถูกเพิกถอนได้ กำหนดการหมุนคีย์และพฤติกรรมเมื่อ token ถูกปฏิเสธ (ล้างข้อมูล, ลงชื่อเข้าใหม่, ลองครั้งเดียว)
  • พฤติกรรมเมื่อติดตั้งใหม่และเปลี่ยนเครื่อง. ทดสอบถอนติดตั้งแล้วติดตั้งใหม่ แล้วเปิดแบบออฟไลน์ ถ้าคีย์ Keystore หาย แอปควรล้มอย่างปลอดภัย (ลบข้อมูลที่เข้ารหัส, แสดงหน้าล็อกอิน, หลีกเลี่ยงการอ่านบางส่วนที่ทำให้สถานะเสียหาย)

การทดสอบเร็ว ๆ คือการทดสอบ "วันแย่ ๆ": ผู้ใช้ล็อกเอาต์ เปลี่ยนรหัสผ่าน คืนค่าจากแบ็กอัพไปยังเครื่องใหม่ แล้วเปิดแอปบนเครื่องที่ออฟไลน์ ผลลัพธ์ควรคาดเดาได้: ข้อมูลถอดรหัสได้สำหรับผู้ใช้ที่ถูกต้อง หรือถูกลบและดาวน์โหลดใหม่หลังล็อกอิน

ตัวอย่างสถานการณ์: แอปธุรกิจที่เก็บ PII ออฟไลน์

Test your “bad day” flow
Validate token storage, logout wipes, and key failure recovery in a small proof of concept.
Start a POC

สมมติแอปการขายภาคสนามที่ใช้ในพื้นที่สัญญาณไม่ดี ตัวแทนล็อกอินเช้าหนึ่งครั้ง เรียกดูลูกค้าที่มอบหมายแบบออฟไลน์ เพิ่มบันทึกการประชุม แล้วซิงค์ทีหลัง นี่คือจุดที่เช็คลิสต์หยุดเป็นทฤษฎีและเริ่มป้องกันการรั่วจริง

การแยกเชิงปฏิบัติ:

  • Access token: สั้น ๆ เก็บใน EncryptedSharedPreferences
  • Refresh token: ปกป้องเข้มงวดขึ้นและกั้นการเข้าถึงผ่าน Android Keystore
  • Customer PII (ชื่อ เบอร์ โทร ที่อยู่): เก็บในฐานข้อมูลท้องถิ่นที่เข้ารหัส
  • บันทึกออฟไลน์และไฟล์แนบ: เก็บในฐานข้อมูลเข้ารหัส โดยให้ระมัดระวังเป็นพิเศษกับการส่งออกและการแชร์

ตอนนี้เพิ่มสองฟีเจอร์แล้วความเสี่ยงเปลี่ยนไป

ถ้าเพิ่ม "จำฉันไว้" refresh token จะกลายเป็นทางกลับเข้าบัญชี ให้ปฏิบัติเหมือนรหัสผ่าน ขึ้นกับผู้ใช้ คุณอาจต้องขอปลดล็อกอุปกรณ์ (PIN/แพทเทิร์น/ไบโอเมตริกซ์) ก่อนถอดรหัส

ถ้าเพิ่มโหมดออฟไลน์ คุณจะปกป้องไม่ใช่แค่เซสชัน แต่รายการลูกค้าทั้งหมดซึ่งมีมูลค่าในตัวเอง นั่นส่วนใหญ่จะผลักดันให้ใช้การเข้ารหัสฐานข้อมูลพร้อมกฎการล็อกเอาต์ที่ชัดเจน: ลบ PII ท้องถิ่น เหลือเฉพาะสิ่งที่จำเป็นสำหรับการล็อกอินครั้งถัดไป และยกเลิกการซิงค์พื้นหลัง

ทดสอบบนอุปกรณ์จริง ไม่ใช่แค่อีมูเลเตอร์ อย่างน้อยตรวจสอบพฤติกรรมล็อก/ปลดล็อก การติดตั้งซ้ำ การสำรอง/คืนค่า และการแยกโปรไฟล์ผู้ใช้หรือโปรไฟล์งาน

ก้าวต่อไป: ทำให้เป็นนิสัยของทีม

การจัดเก็บอย่างปลอดภัยทำงานได้เมื่อเป็นนิสัย เขียนนโยบายการจัดเก็บสั้น ๆ ที่ทีมทำตามได้: อะไรไปที่ไหน (Keystore, EncryptedSharedPreferences, ฐานข้อมูลเข้ารหัส), อะไรห้ามเก็บ, และอะไรต้องลบเวลา logout

ทำให้เป็นส่วนหนึ่งของงานประจำวัน: definition of done, code review, และการเช็กก่อนปล่อย

เช็กลิสต์เบา ๆ สำหรับผู้ตรวจ:

  • แต่ละไอเท็มที่เก็บถูกติดป้าย (token, วัสดุคีย์, หรือ PII)
  • ทางเลือกการจัดเก็บได้รับการอธิบายในคอมเมนต์โค้ด
  • การล็อกเอาต์และการสลับบัญชีลบข้อมูลที่ถูกต้อง (และเฉพาะข้อมูลนั้น)
  • ข้อผิดพลาดและบันทึกไม่พิมพ์ความลับหรือ PII เต็มรูปแบบ
  • มีคนรับผิดชอบนโยบายและอัปเดตให้ทันสมัย

ถ้าทีมของคุณใช้ AppMaster (appmaster.io) เพื่อสร้างแอปธุรกิจและส่งออกซอร์ส Kotlin สำหรับไคลเอนต์ Android ให้ใช้แนวทาง SecureStorage wrapper เดียวกันเพื่อให้โค้ดที่สร้างและโค้ดที่เขียนเองปฏิบัติตามนโยบายเดียวกัน

เริ่มจาก POC เล็ก ๆ

สร้าง POC เล็ก ๆ ที่เก็บ auth token หนึ่งอันและข้อมูล PII หนึ่งรายการ (เช่น เบอร์โทรลูกค้าที่ต้องใช้แบบออฟไลน์) แล้วทดสอบการติดตั้งใหม่ อัปเกรด ล็อกเอาต์ การเปลี่ยนการล็อกหน้าจอ และการล้างข้อมูลแอป ขยายต่อเมื่อพฤติกรรมการลบถูกต้องและทำซ้ำได้

คำถามที่พบบ่อย

What’s the simplest “safe default” for storing tokens and user data in a Kotlin business app?

เริ่มจากการระบุอย่างชัดเจนว่าคุณเก็บค่าอะไรและทำไมต้องเก็บไว้ในเครื่อง ใส่ความลับเซสชันขนาดเล็กเช่น access และ refresh token ลงใน EncryptedSharedPreferences เก็บคีย์เชิงคริปโตใน Android Keystore และใช้ฐานข้อมูลเข้ารหัสสำหรับบันทึกธุรกิจแบบออฟไลน์หรือ PII เมื่อมีมากกว่าบางฟิลด์หรือจำเป็นต้องค้นหา/กรอง

Why isn’t plain SharedPreferences OK for tokens or PII?

SharedPreferences แบบธรรมดาเก็บค่าเป็นไฟล์ที่ในบางกรณีสามารถอ่านได้จากการสำรองข้อมูลของอุปกรณ์ การเข้าถึงไฟล์บนเครื่องที่รูทแล้ว หรือเศษข้อมูลจากการดีบัก ถ้าค่านั้นเป็น token หรือ PII การเก็บไว้เหมือนการตั้งค่าทั่วไปทำให้มันง่ายต่อการคัดลอกและใช้นอกแอป

When should I use Android Keystore instead of just encrypting a file myself?

ใช้ Android Keystore เมื่อต้องการสร้างและเก็บคีย์คริปโตที่ไม่ควรถูกดึงออกมา คุณมักจะใช้คีย์เหล่านั้นเพื่อเข้ารหัสข้อมูลอื่น ๆ (tokens, คีย์ฐานข้อมูล, ไฟล์) และสามารถกำหนดให้ต้องยืนยันตัวผู้ใช้ (ไบโอเมตริกซ์หรือข้อมูลประจำเครื่อง) ก่อนใช้งานคีย์ได้

What does “hardware-backed” Keystore actually protect me from?

หมายความว่าการดำเนินการกับคีย์สามารถเกิดขึ้นในฮาร์ดแวร์ที่ป้องกัน ทำให้วัสดุของคีย์ยากต่อการสกัด แม้ว่าผู้โจมตีจะอ่านไฟล์แอปได้ก็ตาม อย่าคาดหวังว่าจะมีฮาร์ดแวร์แบบนี้เสมอไปหรือทำงานเหมือนกันทุกอุปกรณ์; ออกแบบให้รับมือกับความล้มเหลวและมีแนวทางกู้คืนเมื่อคีย์ไม่ใช้ได้

Is EncryptedSharedPreferences enough for most apps?

โดยปกติพอเพียงสำหรับชุดคีย์-ค่าเล็ก ๆ ที่อ่านบ่อย เช่น access/refresh token, session ID และค่าระดับสถานะเล็ก ๆ EncryptedSharedPreferences ไม่เหมาะกับข้อมูลขนาดใหญ่ ระเบียนเชิงโครงสร้าง หรือข้อมูลที่ต้องค้นหา/กรอง เช่น รายชื่อลูกค้า ตั๋ว หรือคำสั่งซื้อ

When do I need database encryption instead of encrypted preferences?

ควรใช้ฐานข้อมูลเข้ารหัสเมื่อคุณเก็บข้อมูลธุรกิจหรือ PII ในเครื่องในระดับที่มากขึ้น ต้องการการค้นหา/กรอง/การจัดเรียง หรือต้องเก็บประวัติการใช้งานแบบออฟไลน์ นั่นช่วยลดความเสี่ยงที่อุปกรณ์หายแล้วข้อมูลลูกค้าทั้งหมดหรือบันทึกจะหลุดออกมา ในขณะที่แอปยังสามารถทำงานออฟไลน์ได้ด้วยนโยบายคีย์ที่ชัดเจน

Should I use full database encryption or encrypt only specific fields?

การเข้ารหัสทั้งไฟล์ฐานข้อมูล (full database encryption) ปกป้องไฟล์ทั้งหมดที่เก็บบนดิสก์และทำให้ง่ายต่อการคิดเหตุผลเพราะไม่ต้องตามว่าคอลัมน์ไหนถูกปกป้อง ส่วนการเข้ารหัสเฉพาะช่องข้อมูล (field encryption) เหมาะกับบางคอลัมน์ แต่ทำให้การค้นหาและการเรียงลำดับยากและเสี่ยงที่จะรั่วผ่านดัชนีหรือฟิลด์อนุพันธ์

What’s a practical way to manage encryption keys without hardcoding anything?

สร้างคีย์ฐานข้อมูลแบบสุ่ม แล้วเก็บไว้เฉพาะในรูปแบบ "ห่อ" (wrapped) ที่ถูกเข้ารหัสด้วยคีย์ที่เก็บใน Android Keystore อย่าใส่คีย์ลงไปในโค้ดหรือส่งมากับแอป และกำหนดพฤติกรรมเมื่อผู้ใช้ล็อกเอาต์หรือคีย์หมดอายุ (บ่อยครั้ง: ลบคีย์ที่ห่อไว้และถือว่าข้อมูลท้องถิ่นทิ้งได้)

What should my app do when Keystore keys get invalidated and decryption fails?

คีย์อาจถูกยกเลิกใช้งานเพราะการเปลี่ยนการล็อกหน้าจอ การเปลี่ยนไบโอเมตริกซ์ เหตุการณ์ด้านความปลอดภัย หรือการย้ายเครื่อง ตั้งรับกับกรณีเหล่านี้โดยตรวจจับความล้มเหลวในการถอดรหัส ลบข้อมูลที่เข้ารหัสหรือฐานข้อมูลท้องถิ่นอย่างปลอดภัย และขอให้ผู้ใช้ลงชื่อเข้าใช้ใหม่ แทนที่จะพยายามซ้ำหรือกลับไปเก็บแบบ plaintext

What are the most common mistakes that still leak secrets even if I use encryption?

การรั่วไหลส่วนใหญ่เกิดนอก 'ตู้นิรภัย': บันทึก (logs), รายงานการชน (crash reports), เหตุการณ์วิเคราะห์, การพิมพ์ขณะดีบัก, แคช HTTP, ภาพหน้าจอ และคลิปบอร์ด อย่าถือว่าบันทึกเป็นส่วนตัว ห้ามบันทึก tokens หรือ PII เต็มรูปแบบ ปิดเส้นทางการส่งออกโดยไม่ตั้งใจ และทำให้การล็อกเอาต์ลบทั้งข้อมูลที่เก็บและสำเนาในหน่วยความจำ

ง่ายต่อการเริ่มต้น
สร้างบางสิ่งที่ น่าทึ่ง

ทดลองกับ AppMaster ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม