본 포스팅은 DroidKaigi 2017 ~ Android Security 最前線 을 기본으로 번역하여 작성했습니다
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
실제 슬라이드의 일본어
부분을 번역했다는 점 양해바랍니다. 일부 이미지는 원작자의 슬라이드의 일부 화면을 캡쳐한 이미지입니다.
Naoki Yano
YAHOO 주식회사 GAYO 주식회사
Android Framework / Application / CTS / Driver Engineer
Android 경력 3년
관련 경력 2년
Android Nougat 에는 Security 관련 업데이트가 가득!!
언제든지 사용할 수 있도록 준비해 두는 것이 중요!!
Using Scoped Directory Access
Direct Boot
Network Security Config
Key Attestation
APK Signature Scheme v2
Android 6.0 이전:
Android 7.0 :
신규 API
신규 API
// 권한이 필요한 storageVolume 취득
StorageManager sm = getSystemService(StorageManager.class);
StorageVolume sv = sm.getPrimaryStorageVolume();
// 사용자 승인을 얻기 위한 Intent를 생성
Intent i = sv.createAccessIntent(Environment.DIRECTORY_MUSIC);
// Intent를 StartActivityForResult에 던진다
startActivityForResult(i, REQUEST_CODE);
package android.os.storage;
public final class StorageVolume implements Parcelable {
…
public @Nullable Intent createAccessIntent(String directoryName) {
if ((isPrimary() && directoryName == null) ||
(directoryName != null && !Environment.isStandardDirectory(directoryName))) {
return null;
}
final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY);
intent.putExtra(EXTRA_STORAGE_VOLUME, this);
intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName);
return intent;
}
package android.os;
public class Environment {
public static final String[] STANDARD_DIRECTORIES = {
DIRECTORY_MUSIC,
DIRECTORY_PODCASTS,
DIRECTORY_RINGTONES,
DIRECTORY_ALARMS,
DIRECTORY_NOTIFICATIONS,
DIRECTORY_PICTURES,
DIRECTORY_MOVIES,
DIRECTORY_DOWNLOADS,
DIRECTORY_DCIM,
DIRECTORY_DOCUMENTS
};
package com.android.documentsui;
public class OpenExternalDirectoryActivity extends Activity {
…
public void onCreate(Bundle savedInstanceState) {
// 디렉토리가 지정되지 않은 경우 root 권한을 얻을
String directoryName = intent.getStringExtra(EXTRA_DIRECTORY_NAME );
if (directoryName == null) {
directoryName = DIRECTORY_ROOT;
}
// 권한 미획득인지 확인
final StorageVolume volume = (StorageVolume) storageVolume;
if (getScopedAccessPermissionStatus(getApplicationContext(), getCallingPackage(),
volume.getUuid(), directoryName) == PERMISSION_NEVER_ASK) {
// 사용자 확인 Dialog 표시
final int userId = UserHandle.myUserId();
if (!showFragment(this, userId, volume, directoryName)) {
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE
&& resultCode == Activity.RESULT_OK) {
// 2번째 이후는 다이얼로그가 나오지 않도록 설정
getActivity().getContentResolver().takePersistableUriPermission(data.getData(),
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// read, write
// data.getData()에 접근 권한을 얻은 디렉토리의 URI가 들어있다
}
}
Android 6.0 이전 :
Android 7.0 :
신규 API
// 구성 요소가 암호화를 지원하도록 Flag를 지정한다
<receiver android:name=".BootBroadcastReceiver"
android:exported="false"
android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
// ProtectedStorage에 저장
final Context deviceContext = getApplicationContext().createDeviceProtectedStorageContext();
deviceContext.moveSharedPreferencesFrom(context, PREFERENCES_NAME));
SharedPreferences sp = deviceContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
// LOCKED_BOOT_COMPLETED를 얻어 데이터를 얻는다
public void onReceive(Context context, Intent intent) {
boolean bootCompleted;
String action = intent.getAction();
if (BuildCompat.isAtLeastN()) {
bootCompleted = Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action);
} else {
bootCompleted = Intent.ACTION_BOOT_COMPLETED.equals(action);
}
if (!bootCompleted) {
return;
}
}
Android 6.0 이전 :
Android 7.0 :
ApplicationInfo ai = getApplicationInfo();
Log.i(TAG, “deviceProtectedDataDir: ” + ai.deviceProtectedDataDir);
# deviceProtectedDataDir: /data/user_de/0/com.yanokuro.directboot
감상
Android 7.0 :
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application ... >
<meta-data android:name="android.security.net.config"
android:resource="@xml/network_security_config" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<trust-anchors>
<certificates src="@raw/my_ca"/>
</trust-anchors>
</domain-config>
</network-security-config>
<trust-anchors>
// 앱이 가지고 있는 CA 인증서를 사용
<certificates src="@raw/my_ca"/>
// 시스템 기본 CA 인증서를 사용
<certificates src="system"/>
// 사용자가 추가한 CA 인증서를 사용
<certificates src="user"/>
</trust-anchors>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="@raw/debug_ca"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config usesCleartextTraffic="false">
<domain includeSubdomains="true">secure.example.com</domain>
</domain-config>
</network-security-config>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
...
</base-config>
<domain-config>
...
</domain-config>
<debug-overrides>
...
</debug-overrides>
</network-security-config>
<base-config usesCleartextTraffic="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<base-config usesCleartextTraffic="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
감상
각각 주의점은 있지만, 주의해서 사용하면 보다 안전한 앱을 만들 수 있겠어.
각 기능의 움직임을 제대로 이해하고 개발하자.
comments powered by Disqus
Subscribe to this blog via RSS.
LazyColumn/Row에서 동일한 Key를 사용하면 크래시가 발생하는 이유
Posted on 30 Nov 2024