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 น่าจะพอ ถ้าต้องการแถวและการค้นหา ให้เปลี่ยนไปใช้ฐานข้อมูล

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

Choose your deployment path
Deploy to AppMaster Cloud or your own AWS, Azure, or Google Cloud setup.
Deploy Now

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

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

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

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

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

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

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

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

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

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

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

Go beyond simple app builders
Build native iOS and Android apps that support real device capabilities.
Create Mobile

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

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

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

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

  • 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 การลบเดียวกัน

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

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

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

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

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

สัญญาณเตือนที่ใหญ่ที่สุดคือ 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) ลดการโจมตีแบบสมัครเล่น แต่ผู้โจมตีที่มุ่งมั่นจะหลบได้ ให้ใช้สัญญาณเหล่านี้เป็นอินพุตความเสี่ยง: จำกัดการเข้าถึงออฟไลน์, ขอการยืนยันตัวใหม่, และบันทึกเหตุการณ์

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

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

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

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

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

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

Create secure internal tools
Replace spreadsheets with an admin panel and workflows your team can maintain without heavy coding.
Build Internal Tool

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

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

  • 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 ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม