หมวดหมู่ OWASP: MASVS-CODE: คุณภาพโค้ด
ภาพรวม
ไม่บ่อยนักที่จะได้เห็นแอปพลิเคชันที่มีฟังก์ชันการทำงานซึ่งทำให้ผู้ใช้ทำสิ่งต่อไปนี้ได้ โอนข้อมูลหรือโต้ตอบกับอุปกรณ์อื่นๆ โดยใช้ความถี่วิทยุ (RF) การสื่อสารหรือการเชื่อมต่อผ่านสายเคเบิล เทคโนโลยีที่ใช้กันมากที่สุดใน Android เพื่อวัตถุประสงค์นี้ ได้แก่ บลูทูธคลาสสิก (Bluetooth BR/EDR), บลูทูธพลังงานต่ำ (BLE), Wi-Fi P2P, NFC และ USB
เทคโนโลยีเหล่านี้มักใช้ในแอปพลิเคชันที่คาดว่าจะ สื่อสารกับอุปกรณ์สมาร์ทโฮม, อุปกรณ์ตรวจสอบสุขภาพ, สาธารณะ ตู้ขนส่งสินค้า เครื่องชำระเงิน และอุปกรณ์อื่นๆ ที่ใช้ระบบ Android
การสื่อสารระหว่างเครื่องจักรกับเครื่องจักรก็เหมือนกับช่องทางอื่นๆ ที่อาจถูกโจมตีเพื่อทำลายขอบเขตความน่าเชื่อถือที่กำหนดไว้ระหว่างอุปกรณ์ 2 เครื่องขึ้นไป เทคนิคต่างๆ เช่น การสวมบทบาทเป็นอุปกรณ์สามารถนำมาใช้ประโยชน์ได้โดย ผู้ใช้ที่ไม่ประสงค์ดีให้โจมตีการสื่อสารนี้เป็นจำนวนมาก
Android สร้าง API เฉพาะสำหรับการกำหนดค่าเครื่องไปยังเครื่องคอมพิวเตอร์ ให้นักพัฒนาซอฟต์แวร์เข้าถึงได้
ควรใช้ API เหล่านี้อย่างระมัดระวังเนื่องจากข้อผิดพลาดขณะดำเนินการสื่อสาร โปรโตคอลอาจทำให้ข้อมูลผู้ใช้หรืออุปกรณ์เปิดเผยข้อมูลที่ไม่ได้รับอนุญาต บุคคลที่สาม ในกรณีที่ร้ายแรงที่สุด ผู้โจมตีอาจควบคุมอุปกรณ์อย่างน้อย 1 เครื่องจากระยะไกลได้ ซึ่งจะทำให้เข้าถึงเนื้อหาในอุปกรณ์ได้อย่างเต็มที่
ผลกระทบ
ผลกระทบอาจแตกต่างกันไปตามเทคโนโลยีระหว่างอุปกรณ์ที่ใช้ใน แอปพลิเคชัน
การใช้งานหรือการกำหนดค่าช่องทางการสื่อสารแบบเครื่องต่อเครื่องที่ไม่ถูกต้องอาจทำให้อุปกรณ์ของผู้ใช้เสี่ยงต่อการพยายามสื่อสารที่ไม่ปลอดภัย ซึ่งอาจทำให้อุปกรณ์มีช่องโหว่ต่อ การโจมตีเพิ่มเติม เช่น แทรกกลางการสื่อสาร (MiTM), การแทรกคำสั่ง, DoS หรือ การโจมตีแบบแอบอ้างเป็นบุคคลอื่น
ความเสี่ยง: การดักฟังข้อมูลที่ละเอียดอ่อนผ่านช่องทางไร้สาย
เมื่อใช้งานกลไกการสื่อสารระหว่างเครื่องคอมพิวเตอร์ โปรดระมัดระวัง ควรคำนึงถึงทั้งเทคโนโลยีที่ใช้และประเภทของข้อมูล ที่ควรส่ง แม้ว่าการเชื่อมต่อแบบใช้สายจะปลอดภัยกว่าสำหรับงานดังกล่าวเนื่องจากต้องใช้การเชื่อมต่อทางกายภาพระหว่างอุปกรณ์ที่เกี่ยวข้อง แต่โปรโตคอลการสื่อสารที่ใช้ความถี่วิทยุ เช่น บลูทูธคลาสสิก, BLE, NFC และ Wi-Fi P2P นั้นสามารถถูกดักฟังได้ ผู้โจมตีอาจแอบอ้างเป็นบุคคลอื่นได้ ของเทอร์มินัลหรือจุดเข้าใช้งานที่เกี่ยวข้องในการแลกเปลี่ยนข้อมูล การสกัดกั้น การสื่อสารผ่านอากาศ ซึ่งทำให้เข้าถึงผู้ใช้ที่มีความละเอียดอ่อนได้ นอกจากนี้ แอปพลิเคชันที่เป็นอันตรายซึ่งติดตั้งในอุปกรณ์ หากได้รับอนุญาต สิทธิ์รันไทม์เฉพาะการสื่อสารอาจเรียกข้อมูลกลับมาได้ ข้อมูลที่แลกเปลี่ยนระหว่างอุปกรณ์ต่างๆ ด้วยการอ่านบัฟเฟอร์ข้อความของระบบ
การผ่อนปรนชั่วคราว
หากแอปพลิเคชันกำหนดให้มีการแลกเปลี่ยนข้อมูลที่ละเอียดอ่อนระหว่างเครื่องกับเครื่อง ผ่านช่องทางไร้สาย และโซลูชันการรักษาความปลอดภัยในชั้นแอปพลิเคชัน เช่น ควรติดตั้งในโค้ดของแอปพลิเคชัน ซึ่งจะช่วยป้องกันไม่ให้ผู้โจมตีดักรับข้อมูลในช่องทางการสื่อสารและดึงข้อมูลที่แลกเปลี่ยนในรูปแบบข้อความธรรมดา ดูแหล่งข้อมูลเพิ่มเติมได้ในเอกสารประกอบวิทยาการเข้ารหัส
ความเสี่ยง: การแทรกข้อมูลที่เป็นอันตรายแบบไร้สาย
ช่องทางการสื่อสารแบบไร้สายระหว่างอุปกรณ์กับอุปกรณ์ (บลูทูธคลาสสิก, BLE, NFC, Wi-Fi P2P) อาจถูกแทรกแซงโดยใช้ข้อมูลที่อันตราย มีทักษะเพียงพอ ผู้โจมตีสามารถระบุโปรโตคอลการสื่อสารที่ใช้งานอยู่และแทรกแซง ขั้นตอนการแลกเปลี่ยนข้อมูล ตัวอย่างเช่น การแอบอ้างเป็นปลายทางหนึ่ง เพย์โหลดที่สร้างขึ้นมาโดยเฉพาะ การเข้าชมที่เป็นอันตรายประเภทนี้อาจทำให้ ฟังก์ชันการทำงานของแอปพลิเคชัน และในกรณีที่เลวร้ายที่สุด จะทำให้เกิดการ ของแอปพลิเคชันและอุปกรณ์ หรือส่งผลให้เกิดการโจมตี เช่น DoS, คำสั่ง การแทรก หรือการเทคโอเวอร์อุปกรณ์
การลดปัญหา
Android มี API ที่มีประสิทธิภาพให้กับนักพัฒนาซอฟต์แวร์ในการจัดการ การสื่อสารระหว่างเครื่องกับเครื่อง เช่น บลูทูธแบบคลาสสิก, BLE, NFC และ Wi-Fi P2P ซึ่งควรใช้ร่วมกับตรรกะการตรวจสอบข้อมูลที่ติดตั้งใช้งานอย่างรอบคอบเพื่อล้างข้อมูลที่มีการแลกเปลี่ยนระหว่างอุปกรณ์ 2 เครื่อง
โซลูชันนี้ควรติดตั้งใช้งานที่ระดับแอปพลิเคชันและควรมีการตรวจสอบที่ยืนยันว่าข้อมูลมีความยาว รูปแบบ และเพย์โหลดที่ถูกต้องซึ่งแอปพลิเคชันตีความได้หรือไม่
ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างตรรกะการตรวจสอบข้อมูล ติดตั้งใช้งานแล้ว จากตัวอย่างสําหรับนักพัฒนาซอฟต์แวร์ Android สําหรับการใช้ข้อมูลบลูทูธ การเปลี่ยนเครื่อง:
Kotlin
class MyThread(private val mmInStream: InputStream, private val handler: Handler) : Thread() {
private val mmBuffer = ByteArray(1024)
override fun run() {
while (true) {
try {
val numBytes = mmInStream.read(mmBuffer)
if (numBytes > 0) {
val data = mmBuffer.copyOf(numBytes)
if (isValidBinaryData(data)) {
val readMsg = handler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1, data
)
readMsg.sendToTarget()
} else {
Log.w(TAG, "Invalid data received: $data")
}
}
} catch (e: IOException) {
Log.d(TAG, "Input stream was disconnected", e)
break
}
}
}
private fun isValidBinaryData(data: ByteArray): Boolean {
if (// Implement data validation rules here) {
return false
} else {
// Data is in the expected format
return true
}
}
}
Java
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
if (numBytes > 0) {
// Handle raw data directly
byte[] data = Arrays.copyOf(mmBuffer, numBytes);
// Validate the data before sending it to the UI activity
if (isValidBinaryData(data)) {
// Data is valid, send it to the UI activity
Message readMsg = handler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1,
data);
readMsg.sendToTarget();
} else {
// Data is invalid
Log.w(TAG, "Invalid data received: " + data);
}
}
} catch (IOException e) {
Log.d(TAG, "Input stream was disconnected", e);
break;
}
}
}
private boolean isValidBinaryData(byte[] data) {
if (// Implement data validation rules here) {
return false;
} else {
// Data is in the expected format
return true;
}
}
ความเสี่ยง: การแทรกข้อมูลที่เป็นอันตรายผ่าน USB
การเชื่อมต่อ USB ระหว่าง 2 อุปกรณ์อาจเป็นเป้าหมายของผู้ใช้ที่เป็นอันตรายซึ่งสนใจที่จะขัดขวางการสื่อสาร ในกรณีนี้ ลิงก์ทางกายภาพ ประกอบด้วยชั้นความปลอดภัยเพิ่มเติม เนื่องจากผู้โจมตีจำเป็นต้องได้รับ ผ่านสายที่เชื่อมต่อขั้วปลายสายไฟเพื่อให้สามารถดักฟัง เวกเตอร์การโจมตีอื่นแสดงโดยอุปกรณ์ USB ที่ไม่น่าเชื่อถือ อุปกรณ์อาจเสียบอยู่ ไม่ว่าจะโดยตั้งใจหรือไม่ตั้งใจ
หากแอปพลิเคชันกรองอุปกรณ์ USB โดยใช้ PID/VID เพื่อเรียกใช้ฟังก์ชันการทำงานบางอย่างในแอป ผู้โจมตีอาจดัดแปลงข้อมูลที่ส่งผ่านช่องทาง USB โดยการแอบอ้างเป็นอุปกรณ์ที่ถูกต้อง การโจมตีประเภทนี้อาจทำให้ผู้ใช้ที่เป็นอันตรายส่งการกดแป้นพิมพ์ไปยังอุปกรณ์หรือเรียกใช้กิจกรรมของแอปพลิเคชัน ซึ่งในกรณีที่ร้ายแรงที่สุดอาจนำไปสู่การเรียกใช้โค้ดจากระยะไกลหรือการดาวน์โหลดซอฟต์แวร์ที่ไม่ต้องการ
การลดปัญหา
ควรใช้ตรรกะการตรวจสอบระดับแอปพลิเคชัน ตรรกะนี้ควร กรองข้อมูลที่ส่งผ่าน USB ตรวจสอบว่าความยาว รูปแบบ และเนื้อหา ตรงกับกรณีการใช้งานของแอปพลิเคชัน ตัวอย่างเช่น เครื่องวัดฮาร์ตบีตไม่ควร สามารถส่งคำสั่งกดแป้นพิมพ์
นอกจากนี้ หากเป็นไปได้ ควรคำนึงถึงการจำกัด จำนวนแพ็กเก็ต USB ที่แอปพลิเคชันรับได้จากอุปกรณ์ USB ช่วงเวลานี้ ช่วยป้องกันอุปกรณ์ที่เป็นอันตรายไม่ให้ทำการโจมตีอย่าง "เป็ดยาง"
การตรวจสอบนี้ทำได้โดยการสร้างชุดข้อความใหม่เพื่อตรวจสอบเนื้อหาบัฟเฟอร์ เช่น เมื่อ bulkTransfer
Kotlin
fun performBulkTransfer() {
// Stores data received from a device to the host in a buffer
val bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.size, 5000)
if (bytesTransferred > 0) {
if (//Checks against buffer content) {
processValidData(buffer)
} else {
handleInvalidData()
}
} else {
handleTransferError()
}
}
Java
public void performBulkTransfer() {
//Stores data received from a device to the host in a buffer
int bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.length, 5000);
if (bytesTransferred > 0) {
if (//Checks against buffer content) {
processValidData(buffer);
} else {
handleInvalidData();
}
} else {
handleTransferError();
}
}
ความเสี่ยงเฉพาะ
ส่วนนี้จะรวบรวมความเสี่ยงที่ต้องใช้กลยุทธ์การบรรเทาความเสี่ยงที่ไม่ใช่มาตรฐานหรือได้รับการบรรเทาในระดับ SDK บางระดับ และอยู่ที่นี่เพื่อความสมบูรณ์
ความเสี่ยง: บลูทูธ - เวลาในการค้นพบที่ไม่ถูกต้อง
ตามที่ไฮไลต์ไว้ในเอกสารประกอบเกี่ยวกับบลูทูธสำหรับนักพัฒนาแอป Android ขณะกำหนดค่าอินเทอร์เฟซบลูทูธภายในแอปพลิเคชัน การใช้เมธอด startActivityForResult(Intent, int)
เพื่อเปิดใช้การค้นพบอุปกรณ์และการตั้งค่า EXTRA_DISCOVERABLE_DURATION
เป็น 0 จะทําให้อุปกรณ์ค้นพบได้ ตราบใดที่แอปพลิเคชันทำงานอยู่เบื้องหลังหรือเบื้องหน้า ตามข้อมูลจำเพาะของบลูทูธแบบคลาสสิก อุปกรณ์ที่ค้นพบได้จะเผยแพร่การค้นพบที่เฉพาะเจาะจงอย่างต่อเนื่อง
ข้อความที่อนุญาตให้อุปกรณ์อื่นๆ เรียกดูข้อมูลในอุปกรณ์หรือเชื่อมต่อกับนั้น ใน
บุคคลที่สามที่ประสงค์ร้ายสามารถดักจับข้อความดังกล่าว และเชื่อมต่อ
ลงในอุปกรณ์ที่ใช้ Android เมื่อเชื่อมต่อแล้ว ผู้โจมตีจะดำเนินการต่อไปได้
เช่น การขโมยข้อมูล, DoS หรือการแทรกคำสั่ง
การลดปัญหา
ไม่ควรตั้งค่า EXTRA_DISCOVERABLE_DURATION
เป็น 0 หาก
ไม่ได้ตั้งค่าพารามิเตอร์ EXTRA_DISCOVERABLE_DURATION
โดยค่าเริ่มต้น Android จะ
ค้นพบอุปกรณ์ได้เป็นเวลา 2 นาที ค่าสูงสุดที่สามารถกำหนดสำหรับแอตทริบิวต์
พารามิเตอร์ EXTRA_DISCOVERABLE_DURATION
ยาว 2 ชั่วโมง (7,200 วินาที) ใช่เลย
แนะนำให้กำหนดระยะเวลาที่ค้นพบได้ให้สั้นที่สุด
ตามกรณีการใช้งานของแอปพลิเคชัน
ความเสี่ยง: NFC – ตัวกรอง Intent ที่โคลน
แอปพลิเคชันที่เป็นอันตรายสามารถลงทะเบียนตัวกรอง Intent เพื่ออ่านแท็ก NFC หรืออุปกรณ์ที่เปิดใช้ NFC ที่เฉพาะเจาะจง ตัวกรองเหล่านี้สามารถจำลองตัวกรองที่กำหนดโดย ที่ถูกต้องตามกฎหมาย ซึ่งจะทำให้ผู้โจมตีสามารถอ่านเนื้อหาได้ ของข้อมูล NFC ที่แลกเปลี่ยน โปรดทราบว่าเมื่อกิจกรรม 2 รายการระบุตัวกรอง Intent เดียวกันสําหรับแท็ก NFC ที่เฉพาะเจาะจง ระบบจะแสดงเครื่องมือเลือกกิจกรรม ดังนั้นผู้ใช้จะยังคงต้องเลือกแอปพลิเคชันที่เป็นอันตรายเพื่อให้การโจมตีสําเร็จ อย่างไรก็ตาม การรวม ตัวกรอง Intent ที่มีการปิดบังหน้าเว็บจริง (Cloaking) สถานการณ์นี้ยังคงเป็นไปได้ การโจมตีนี้มีความสําคัญเฉพาะในกรณีที่ข้อมูลที่แลกเปลี่ยนผ่าน NFC ถือว่ามีความละเอียดอ่อนสูง
การลดปัญหา
เมื่อใช้ความสามารถในการอ่าน NFC ภายในแอปพลิเคชัน คุณจะใช้ตัวกรอง Intent ร่วมกับระเบียนแอปพลิเคชัน Android (AAR) ได้ การฝัง ระเบียน AAR ภายในข้อความ NDEF จะให้ความมั่นใจอย่างมากว่ามีเพียง เริ่มต้นแอปพลิเคชันที่ถูกต้อง และกิจกรรมการจัดการ NDEF ที่เกี่ยวข้องแล้ว วิธีนี้จะป้องกันไม่ให้แอปพลิเคชันหรือกิจกรรมที่ไม่พึงประสงค์อ่านค่าสูง แท็กที่มีความละเอียดอ่อนหรือข้อมูลอุปกรณ์ที่แลกเปลี่ยนผ่าน NFC
ความเสี่ยง: NFC – ไม่มีการยืนยันข้อความ NDEF
เมื่ออุปกรณ์ Android ได้รับข้อมูลจากแท็ก NFC หรืออุปกรณ์ที่เปิดใช้ NFC ระบบจะเรียกใช้แอปพลิเคชันหรือกิจกรรมที่เฉพาะเจาะจงซึ่งกำหนดค่าให้จัดการข้อความ NDEF ที่มีอยู่ในแท็กโดยอัตโนมัติ ข้อมูลที่อยู่ในแท็กหรือได้รับจากอุปกรณ์จะแสดงในกิจกรรมอื่นๆ เพื่อทริกเกอร์การดำเนินการเพิ่มเติม เช่น การเปิดหน้าเว็บ ตามตรรกะที่ติดตั้งใช้งานในแอปพลิเคชัน
แอปพลิเคชันที่ไม่มีการตรวจสอบเนื้อหาข้อความ NDEF อาจทำให้ผู้โจมตีใช้อุปกรณ์ที่เปิดใช้ NFC หรือแท็ก NFC เพื่อแทรกเพย์โหลดที่เป็นอันตรายภายในแอปพลิเคชันได้ ซึ่งจะทำให้เกิดลักษณะการทำงานที่ไม่คาดคิดที่อาจส่งผลให้มีการดาวน์โหลดไฟล์ที่เป็นอันตราย การส่งคำสั่ง หรือ DoS
การผ่อนปรนชั่วคราว
ก่อนส่งข้อความ NDEF ที่ได้รับไปยังคอมโพเนนต์อื่นๆ ของแอปพลิเคชัน ข้อมูลภายในควรได้รับการตรวจสอบว่าอยู่ในรูปแบบที่ถูกต้อง และต้องมีฟิลด์ ข้อมูลที่คาดการณ์ วิธีนี้จะช่วยป้องกันไม่ให้ส่งข้อมูลที่อันตรายไปยังคอมโพเนนต์ของแอปพลิเคชันอื่นๆ โดยไม่มีการกรอง ซึ่งจะช่วยลดความเสี่ยงของลักษณะการทำงานที่ไม่คาดคิดหรือการโจมตีโดยใช้ข้อมูล NFC ที่ดัดแปลง
ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างลอจิกการตรวจสอบข้อมูลที่ติดตั้งใช้งานเป็น โดยมีข้อความ NDEF เป็นอาร์กิวเมนต์และดัชนีในอาร์เรย์ข้อความ เรานำพารามิเตอร์นี้ไปใช้ในตัวอย่างของนักพัฒนาแอป Android เพื่อรับข้อมูลจาก แท็ก NFC NDEF ที่สแกน:
Kotlin
//The method takes as input an element from the received NDEF messages array
fun isValidNDEFMessage(messages: Array<NdefMessage>, index: Int): Boolean {
// Checks if the index is out of bounds
if (index < 0 || index >= messages.size) {
return false
}
val ndefMessage = messages[index]
// Retrieves the record from the NDEF message
for (record in ndefMessage.records) {
// Checks if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
if (record.tnf == NdefRecord.TNF_ABSOLUTE_URI && record.type.size == 1) {
// Loads payload in a byte array
val payload = record.payload
// Declares the Magic Number that should be matched inside the payload
val gifMagicNumber = byteArrayOf(0x47, 0x49, 0x46, 0x38, 0x39, 0x61) // GIF89a
// Checks the Payload for the Magic Number
for (i in gifMagicNumber.indices) {
if (payload[i] != gifMagicNumber[i]) {
return false
}
}
// Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
if (payload.size == 13) {
return true
}
}
}
return false
}
Java
//The method takes as input an element from the received NDEF messages array
public boolean isValidNDEFMessage(NdefMessage[] messages, int index) {
//Checks if the index is out of bounds
if (index < 0 || index >= messages.length) {
return false;
}
NdefMessage ndefMessage = messages[index];
//Retrieve the record from the NDEF message
for (NdefRecord record : ndefMessage.getRecords()) {
//Check if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
if ((record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) && (record.getType().length == 1)) {
//Loads payload in a byte array
byte[] payload = record.getPayload();
//Declares the Magic Number that should be matched inside the payload
byte[] gifMagicNumber = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}; // GIF89a
//Checks the Payload for the Magic Number
for (int i = 0; i < gifMagicNumber.length; i++) {
if (payload[i] != gifMagicNumber[i]) {
return false;
}
}
//Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
if (payload.length == 13) {
return true;
}
}
}
return false;
}
แหล่งข้อมูล
- สิทธิ์รันไทม์
- คู่มือการเชื่อมต่อ
- ตัวอย่าง
- bulkTransfer
- วิทยาการเข้ารหัสลับ
- ตั้งค่าบลูทูธ
- พื้นฐาน NFC
- ระเบียนแอปพลิเคชัน Android
- ข้อมูลจำเพาะของบลูทูธแบบคลาสสิก