How to secure the Partner Activities (1)

บทนำ (Overview)

“Partner activities”  เป็น “Activities” ที่สมารถถูกเรียกใช้งานจากเฉพาะ “Application” ระบุไว้เท่านั้น เป้าหมายก็เพื่อสามารถให้โปรแกรมต่างขององค์กรสามารถใช้ข้อมูลหรือฟังก์ชันการทำงานร่วมกันได้อย่างปลอดภัยระดับของความเสี่ยงและมาตรการป้องกันของการใช้งาน “Activities” ให้ปลอดภัยนั้น ขึ้นอยู่กับการนำไปใช้งาน ซึ่งเราสามารถที่จะจำแนกได้เป็น 4 ประเภทดังต่อไปนี้

เงื่อนไข ประเภท อธิบาย
สำหรับทุก “Application” Public Activity เป็น “Activity” ที่สามารถถูกเรียกใช้งานจาก Application ที่ติดตั้งอยู่ในเครื่องเดียวกัน
เฉพาะ “Application” อนุญาต Partner Activity เป็น “Activity” ที่สามารถเรียกใช้งานเฉพาะ “Application” อนุญาตเท่านั้น
เฉพาะ “Application” ภายใน In-house Activity เป็น “Activity” ที่สามารถใช้งานเฉพาะ Application ที่พัฒนาเฉพาะองค์กรเดียวกันเท่านั้น
ไม่อนุญาต “Application” อื่น Private Activity เป็น “Activity” ที่ไม่สามารถเรียกใช้งานจาก “Application” อื่น ๆ ที่ติดตั้งอยู่ในเครื่องกัน ซึ่งเป็น “Activity” ที่มีความปลอดภัยสูงสุด

ความเสี่ยง (Risks)

อย่างไรก็ตามการใช้งาน Partner Activities อาจมีความเสี่ยงที่โปรแกรมประสงค์ร้ายสามารถที่จะอ่าน “Intent” ที่เรียกใช้งาน “Partner Activities” โดยเฉพาะอย่างยิ่งถ้าภายใน “Intent” มีข้อมูลสำคัญบรรจุอยู่ (Sensitive Information)

การควบคุมความปลอดภัย (Control Activities)

ลำดับ การควบคุม สาเหตุ
ขั้นตอนการสร้าง “Activity”
1 ไม่ระบุใช้งาน “taskAffinity” ตรวจสอบให้แน่ใจว่า “android:taskAffinity” จะต้องไม่มีอยู่ใน “AndroidManifest.xml” ไฟล์ หรือให้ตั้งค่าเป็นค่าตั้งต้น (Default) เพื่อเรียกใช้งาน “Package Name” เป็น “Root tasks” เพื่อป้องกันการอ่านข้อมูลสำคัญ (Sensitive information) ที่อยู่ ใน “Intent” จาก “Application” ประสงค์ร้าย
2 ไม่ระบุใช้งาน “launchMode” ตรวจสอบให้แน่ใจว่า ค่า “launchMode” จะต้องไม่มีอยู่ใน “AndroidManifest.xml” ไฟล์ หรือให้ตั้งค่าเป็นค่าตั้งต้น (Default) ซึ่งก็คือ “Standard” เพื่อป้องกันการอ่านข้อมูลสำคัญ (Sensitive information) ที่อยู่ ใน “Intent” จาก “Application” ประสงค์ร้าย
3 จะต้องไม่ตั้ง “Intent Filter” แต่ “Explicit Activities” สามารถตั้งค่า “Exported” เป็น “True” ได้ ตรวจสอบให้แน่ใจว่า จะต้องไม่ตั้ง “Intent filter” แต่สามารถตั้งค่า “Exported” ของ “Explicit Activities” เป็น “True” เนื่องจาก “Activities” ต้องสามารถเรียกใช้งานจาก “Application” ที่ระบุไว้
4 มีการตรวจสอบว่า “Certificate“ ของ “Application“ ที่ร้องขอใช้งานตรงกับ “White list“ จัดเตรียมไว้ ตรวจสอบให้แน่ใจว่า มีการตรวจสอบว่า “Certificate“ ของ “Application“ ที่ร้องขอใช้งานตรงกับ “White list“ จัดเตรียมไว้
5 จัดการข้อมูลนำเข้าอย่างปลอดภัย (Input validation) ตรวจสอบให้แน่ใจว่า ข้อมูลนำเข้ามีการตรวจสอบ (Input validation) อย่างปลอดภัยดังนี้

  • ข้อมูลนำเข้าจะต้องถูกรูปแบบ (Valid Format) เช่น “String” ไม่รวมอักขระพิเศษเป็นต้น ตามที่ตกลงกันไว้ (Business requirement)
  • ขนาดข้อมูลนำเข้า (Value range)
6 ข้อมูลจะต้องถูกส่งกลับไปเฉพาะ “Partner Application” เท่านั้น ตรวจสอบให้แน่ใจว่า ข้อมูลจะถูกส่งกลับไปเฉพาะ “Partner Application” เท่านั้น

การสอบทานโปรแกรม (Source code review)

privateActivity03

ขั้นตอนที่ 1 การตั้งค่าใน AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.itselectlab.android.activity.partneractivity" >

    <application android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" >
        
        <!-- สำหรับ Partner Activity -->
        <!-- Control Activity 1: android:taskAffinity จะต้องไม่มีอยู่ใน AndroidManifest.xml ไฟล์ -->
        <!-- Control Activity 2: android:taskAffinity จะต้องไม่มีอยู่ใน AndroidManifest.xml ไฟล์ หรือถ้ามีจะต้องตั้งเป็น android:launchMode="standard"-->
        <!-- Control Activity 3: จะต้องไม่ตั้ง “Intent Filter” แต่ “Explicit Activities” สามารถตั้งค่า “Exported” เป็น “True” ได้ -->
        <activity android:name=".PartnerActivity" android:exported="true" />
        
    </application>
</manifest>

ขั้นตอนที่ 2 สร้าง “Partner Activity”

public class PartnerActivity extends Activity {
	
	// Control Activity 4: มีการตรวจสอบว่า “Certificate“ ของ “Application“ ที่ร้องขอใช้งานตรงกับ “White list“ จัดเตรียมไว้
	private static PkgCertWhitelists sWhitelists = null;
	private static void buildWhitelists(Context context) {
		boolean isdebug = Utils.isDebuggable(context);
		sWhitelists = new PkgCertWhitelists();
		
                // เราจะสร้าง Whitelist ของ Application ที่เราอนุญาตโดยใช้ certificate hash value
		sWhitelists.add("org.itselectlab.android.activity.partneruser", isdebug ?
			// Certificate hash value of "androiddebugkey" in the debug.keystore.
    			"0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255" :
			// Certificate hash value of "partner key" in the keystore.
    			"1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A");
		
		// Register the other partner applications in the same way.
	}
	private static boolean checkPartner(Context context, String pkgname) {
		if (sWhitelists == null) buildWhitelists(context);
		return sWhitelists.test(context, pkgname);
	}
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // Control Activity 4: มีการตรวจสอบว่า “Certificate“ ของ “Application“ ที่ร้องขอใช้งานตรงกับ “White list“ จัดเตรียมไว้
        if (!checkPartner(this, getCallingActivity().getPackageName())) {
        	Toast.makeText(this, "Requesting application is not a partner application.", Toast.LENGTH_LONG).show();
        	finish();
        	return;
        }
        
        // Control Activity 5: ข้อมูลนำเข้ามีการตรวจสอบ (Input validation) อย่างปลอดภัย
        // จากตัวอย่างข้างล่าง
        // - ข้อมูลนำเข้าจะต้องถูกรูปแบบ (Valid Format) = กำหนดเป็น String
        // - ขนาดข้อมูลนำเข้า (Value range)
        Toast.makeText(this, "Accessed by Partner App", Toast.LENGTH_LONG).show();
    }
    
	public void onReturnResultClick(View view) {

		// Control Activity 6: ข้อมูลจะต้องถูกส่งกลับไปเฉพาะ “Partner Application” เท่านั้น
		Intent intent = new Intent();
		intent.putExtra("RESULT", "Information for partner applications");
		setResult(RESULT_OK, intent);
		finish();
	}
}

ขั้นตอนที่ 3 Class ของ “PkgCertWhitelists”


public class PkgCertWhitelists {
	private Map<String, String> mWhitelists = new HashMap<String, String>();
	
	public boolean add(String pkgname, String sha256) {
		if (pkgname == null) return false;
		if (sha256 == null) return false;
		
		sha256 = sha256.replaceAll(" ", "");
		if (sha256.length() != 64) return false;	// SHA-256 -> 32 bytes -> 64 chars
		sha256 = sha256.toUpperCase();
		if (sha256.replaceAll("[0-9A-F]+", "").length() != 0) return false;	// found non hex char
		
		mWhitelists.put(pkgname, sha256);
		return true;
	}
	
	public boolean test(Context ctx, String pkgname) {
		// Get the correct hash value which corresponds to pkgname.
		String correctHash = mWhitelists.get(pkgname);
		
		// Compare the actual hash value of pkgname with the correct hash value.
		return PkgCert.test(ctx, pkgname, correctHash);
	}
}

ขั้นตอนที่ 4 Class ของ “PkgCert”

public class PkgCert {

	public static boolean test(Context ctx, String pkgname, String correctHash) {
		if (correctHash == null) return false;
		correctHash = correctHash.replaceAll(" ", "");
		return correctHash.equals(hash(ctx, pkgname));
	}

    public static String hash(Context ctx, String pkgname) {
    	if (pkgname == null) return null;
    	try {
    		PackageManager pm = ctx.getPackageManager();
			PackageInfo pkginfo = pm.getPackageInfo(pkgname, PackageManager.GET_SIGNATURES);
			if (pkginfo.signatures.length != 1) return null;	// Will not handle multiple signatures.
			Signature sig = pkginfo.signatures[0];
			byte[] cert = sig.toByteArray();
			byte[] sha256 = computeSha256(cert);
			return byte2hex(sha256);
		} catch (NameNotFoundException e) {
			return null;
		}
    }

    private static byte[] computeSha256(byte[] data) {
    	try {
			return MessageDigest.getInstance("SHA-256").digest(data);
		} catch (NoSuchAlgorithmException e) {
			return null;
		}
    }

    private static String byte2hex(byte[] data) {
    	if (data == null) return null;
    	final StringBuilder hexadecimal = new StringBuilder();
    	for (final byte b : data) {
    		hexadecimal.append(String.format("%02X", b));
    	}
    	return hexadecimal.toString();
    }
}

เอกสารอ้างอิง (Referrences)

ใส่ความเห็น