이 문서는 현재 공개된 Markleaf Android(v2.x) 기준 보안 정책입니다.
Markleaf는 자체 네트워크 트래픽이 없는 로컬 우선 앱이다.
- Markleaf 자체에는
android.permission.INTERNET이 없다. - Markleaf는 자체 백엔드 서버가 없다.
- 사용자의 노트는 사용자의 명시적 export/share/외부 링크 열기/사용자 선택 폴더 미러 동기화 전까지 기기 밖으로 나가지 않는다.
다음은 현재 코드 상에서도, 향후 합의 없이 추가하는 것도 금지한다.
- API key / secret token / 사용자 계정 정보 저장
- 원격 서버로의 자동 업로드
- 백그라운드 네트워크 통신
- analytics SDK
- tracking SDK
- 광고 SDK
- 폐쇄형 크래시 리포팅 SDK
- 원격 설정 서비스
- 클로즈드 소스 바이너리 의존성
- 노트 본문/태그/메타데이터는 앱 전용 내부 Room DB(
<dataDir>)에 저장된다. - 첨부 이미지는 앱 전용 내부 저장소
<filesDir>/attachments/<noteId>/에 복사 보관된다. - 삭제는 즉시 영구 삭제 대신 휴지통으로 이동한다.
- 휴지통에서의 영구 삭제는 확인 다이얼로그를 거친다.
- 설정에서 화면 보안(
FLAG_SECURE) 토글로 최근 앱 미리보기/스크린샷을 차단할 수 있다.
Markleaf 에는 성격이 다른 잠금이 두 가지 있다.
- 앱 잠금(v2.16.0, #37) — 설정에서 켜면 앱을 열 때 기기 생체 인증(지문/얼굴)을 요구한다. 기본값은 꺼짐이다. 기기 자격 증명에 위임하며 Markleaf 가 따로 비밀을 보관하지 않는다.
- 잠금 노트(Locked space, v2.25.0, #155) — 선택한 노트를 앱 전용 패스코드 뒤로 숨긴다. 아래 내용은 이쪽에 대한 것이다.
Locked space 는 가시성 게이트다. 저장 암호화가 아니다.
- 잠긴 노트는 노트 목록, 검색, 태그 목록과 태그 개수, 백링크, 위젯, 폴더 미러 내보내기에서 제외된다. 노트 행에서 텍스트를 끌어오는 파생 경로 전부에
locked = 0필터가 걸려 있다. - 잠글 때 동기화 폴더에 이미 미러된 평문 파일을 지우고, 잠금을 풀면 다시 쓴다. 내보내기 차단만으로는 잠그기 전에 쓰인 파일이 남기 때문이다.
- Locked 화면은 전역 스크린샷 보호 설정과 무관하게
FLAG_SECURE를 올린다. - 외부 앱이 보낸 노트 열기 인텐트가 잠긴 노트를 지목하면 에디터가 아니라 패스코드 화면으로 보낸다.
- 패스코드를 4회 틀리면 30초 대기가 붙고 실패마다 두 배로 늘어 최대 5분까지 간다. 카운터와 마감 시각이 DataStore 에 있으므로 앱을 강제 종료해도 진행 중인 대기가 초기화되지 않는다.
- 노트 본문은 암호화되지 않는다. 잠긴 노트도 잠기지 않은 노트와 같은 앱 내부 Room DB 에 평문으로 있다. 기기가 루팅되었거나
<dataDir>를 직접 읽을 수 있는 상대에게 패스코드는 장애물이 아니다. 설정 화면의 안내 문구도 이 점을 명시한다. - 재시도 대기는 기기 시계를 앞으로 돌리면 사라진다. 마감을
System.currentTimeMillis기준 절대 시각으로 저장하기 때문이다. 의도한 절충이며 근거는.agent/decisions.md의 D063 에 있다 — 단조 시계(SystemClock.elapsedRealtime)로 바꾸면 이번에는 재부팅이 대기를 지우고, 양쪽을 모두 막으려면 서버나 TEE 가 필요한데 Markleaf 에는 INTERNET 권한이 없다. 이 대기는 어깨너머로 보거나 잠깐 빌린 기기에서 패스코드를 눌러 보는 상대를 늦추기 위한 것이지, 기기를 확보하고 설정을 열 수 있는 상대를 막기 위한 것이 아니다. 그런 상대에게는 위에 적은 평문 DB 라는 더 짧은 경로가 이미 있다. - Locked space 는 생체 인증으로 열리지 않는다. 기기 생체 인증을 통과할 수 있는 사람이 별도의 비밀로 가려 둔 노트까지 열게 되기 때문이다.
노트 본문 암호화는 docs/ROADMAP.md 의 명시적 비범위다. 검색·첨부·폴더 미러를 포함한 위협 모델과 마이그레이션 설계가 따로 승인되기 전까지 도입하지 않는다.
AndroidManifest.xml 의 <application> 요소에 android:allowBackup="false" 를 설정해 Android 자동 백업(Android Auto Backup)과 기기 간 전송(Device-to-Device transfer)에서 Markleaf 데이터를 제외한다.
근거:
- Markleaf는 "사용자가 직접 export/share 하기 전까지 데이터가 기기 밖으로 나가지 않는다" 를 약속한다. OS 차원의 자동 클라우드 백업이 사용자에게 활성화되어 있을 경우, 이 약속과 무언의 충돌이 발생할 수 있다.
- 보수적 기본값을 택해 Markleaf 데이터가 사용자 동의 없이 Google 드라이브 등으로 자동 업로드되지 않도록 한다.
- 사용자가 다중 기기 또는 백업이 필요한 경우, Markleaf는 명시적인 경로(Markdown export, ZIP/AAB 없이 표준
.md파일, SAF 폴더 미러 동기화) 를 제공한다.
android:dataExtractionRules 를 사용하지 않은 이유:
dataExtractionRules는 Android 12 이상에서 fine-grained 제어가 가능하지만, 모든 데이터 를 제외할 거라면allowBackup="false"가 더 단순하고 명시적이다.- minSdk = 26 인 Markleaf 입장에서 두 메커니즘을 동시에 관리하는 추가 표면적이 정책 이득보다 크다.
- 향후 부분적 백업(예: 사용자 설정만 백업, 노트는 제외) 같은 fine-grained 정책이 필요해지면 그 시점에
dataExtractionRules도입을 재검토한다.
다음 경로는 사용자의 명시적 행동에 의해 발생하며, 모두 OS 표준 UI(SAF, 시스템 공유 시트, ACTION_VIEW)를 거친다.
- 노트
.md내보내기 (단일/전체) - 시스템 공유 시트로 텍스트/파일 공유
- 본문 외부 링크를 사용자가 탭 → OS 기본 브라우저/앱으로 위임
- 사용자가 선택한 SAF 폴더로 노트
.md자동 미러 — 해당 폴더의 외부 동기화는 사용자가 선택한 외부 앱(Drive/Dropbox/Syncthing/OneDrive/NAS 등) 의 책임
위 경로에서 Markleaf는 네트워크 호출을 직접 수행하지 않는다.
다음 기능을 추가하기 전에는 별도 보안 검토가 필요하다.
- 직접 구현하는 클라우드 동기화 (지금은 SAF + 외부 앱에 위임)
- 직접 구현하는 백업 업로드
- 계정 로그인
- 직접 구현하는 WebDAV / Google Drive 클라이언트
- 노트 본문 종단 간 암호화
생체 인증과 앱 잠금은 이 목록에 있었으나 이미 구현되었다 — 생체 앱 잠금은 v2.16.0(#37), 잠금 노트는 v2.25.0(#155). 현재 동작과 한계는 위 앱 잠금과 잠금 노트에 있다.