어뷰징 방지

iOS App Attest: 앱 위변조와 봇을 막기 위한 Apple의 무결성 검증 기술

왈왈디 2026. 6. 22. 01:33
728x90

들어가며

모바일 서비스에서 회원가입 보상, 리워드 적립, 쿠폰 지급과 같은 기능을 운영하다 보면 반드시 만나게 되는 문제가 있다.

  • 앱을 리버스 엔지니어링해서 API를 직접 호출하는 경우
  • 탈옥(Jailbreak) 환경에서 앱을 변조하는 경우
  • 스크립트나 매크로로 대량 계정을 생성하는 경우
  • 앱을 실행하지 않고 서버 API만 호출하는 경우

이러한 공격을 막기 위해 Apple은 App Attest라는 무결성 검증 기능을 제공한다.

App Attest를 활용하면 서버는 다음을 검증할 수 있다.

“이 요청이 실제 App Store에서 배포된 우리 앱에서 발생한 것인가?”

이번 글에서는 App Attest의 개념부터 실제 구현 방법까지 자세히 알아본다.


App Attest란?

App Attest는 Apple이 제공하는 DeviceCheck 프레임워크의 기능 중 하나다.

앱이 실행되면 Apple 서버로부터 암호학적으로 검증 가능한 키(Key)를 발급받고, 이후 서버는 해당 키를 이용하여 요청이 실제 앱에서 생성되었는지 검증할 수 있다.

즉,

클라이언트
    ↓
Apple App Attest
    ↓
암호학적 증명 생성
    ↓
서버 검증

구조로 동작한다.


왜 필요한가?

일반적으로 서버는 다음 정보들을 받아 클라이언트를 식별한다.

  • Device ID
  • UUID
  • 광고 ID(IDFA)
  • 쿠키
  • User-Agent

하지만 이런 값들은 쉽게 조작할 수 있다.

예를 들어 공격자는

  • Charles
  • Proxyman
  • Burp Suite
  • Frida
  • Objection

등의 도구를 이용해 API 요청을 복제할 수 있다.

반면 App Attest는 Apple이 발급한 개인키(Private Key)를 기반으로 서명을 수행하기 때문에 공격자가 단순히 HTTP 요청만 복사해서는 우회하기 어렵다.


App Attest 동작 원리

전체 흐름은 다음과 같다.

1. 앱 실행

2. App Attest Key 생성

3. Attestation 생성

4. 서버 검증

5. 정상 기기로 등록

6. API 호출 시 Assertion 생성

7. 서버 검증

Step 1. Key 생성

앱은 최초 실행 시 App Attest Key를 생성한다.

let service = DCAppAttestService.shared

let keyId = try await service.generateKey()

생성된 keyId는 기기를 식별하는 고유 키 역할을 한다.

예시:

F3A9D7B4-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Step 2. Attestation 생성

서버는 클라이언트에게 Challenge를 전달한다.

예:

{
  "challenge": "abc123"
}

클라이언트는 이를 SHA256 해시한 후 Attestation을 생성한다.

let challengeHash = SHA256.hash(data: challengeData)

let attestation = try await service.attestKey(
    keyId,
    clientDataHash: Data(challengeHash)
)

결과:

attestationObject

가 생성된다.


Step 3. 서버 검증

클라이언트는 다음 정보를 서버에 전송한다.

{
  "keyId": "...",
  "attestation": "...",
  "challenge": "..."
}

서버는 Apple의 공개키 체인을 이용해 검증한다.

검증 항목:

  • Apple이 발급한 키인가?
  • Bundle ID가 일치하는가?
  • Team ID가 일치하는가?
  • Challenge가 일치하는가?
  • 위조되지 않았는가?

검증 성공 시:

기기 등록 완료

상태가 된다.


Assertion이란?

Attestation은 최초 등록 시에만 수행한다.

이후 매 API 호출마다 Attestation을 수행하면 성능 비용이 크다.

그래서 App Attest는 Assertion이라는 개념을 제공한다.

Attestation = 최초 등록

Assertion = 이후 요청 검증

Assertion 생성

서버가 nonce를 전달한다.

{
  "nonce": "xyz789"
}

클라이언트는 nonce를 서명한다.

let assertion = try await service.generateAssertion(
    keyId,
    clientDataHash: Data(hash)
)

Assertion 검증

서버는 저장된 공개키를 사용하여 검증한다.

검증 성공 시

이 요청은 등록된 실제 앱에서 생성되었다.

라고 판단할 수 있다.


실제 구현 아키텍처

실무에서는 보통 아래 구조를 사용한다.

┌─────────────┐
│ iOS App     │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ App Attest  │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ AP Server   │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ API Server  │
└─────────────┘

AP Server는

  • Key 등록
  • Attestation 검증
  • Assertion 검증

역할을 담당한다.

API Server는

"이 기기는 신뢰 가능한가?"

만 확인하면 된다.


서버에 저장해야 하는 정보

예시 테이블

CREATE TABLE app_attest_keys (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    device_id VARCHAR(255),
    key_id VARCHAR(255),
    public_key TEXT,
    created_at DATETIME
);

저장 항목:

  • user_id
  • key_id
  • public_key
  • 등록 시각

App Attest만으로 충분할까?

아니다.

App Attest는 매우 강력하지만 만능은 아니다.

다음과 함께 사용하는 것이 일반적이다.

Android

  • Play Integrity API

Device Intelligence

  • 디바이스 지문(Device Fingerprint)
  • Emulator 탐지
  • Root/Jailbreak 탐지

계정 보호

  • Phone Verification
  • Email Verification
  • Rate Limiting

행동 분석

  • Fraud Score
  • Velocity Check
  • Device Cluster 분석

App Attest 도입 시 주의사항

1. 모든 기기에서 지원되지 않는다

App Attest는

  • iOS 14+
  • Secure Enclave 지원 기기

에서만 동작한다.

따라서 예외 처리가 필요하다.

if DCAppAttestService.shared.isSupported {
    // App Attest 사용
}

2. 네트워크 실패 고려

최초 등록 과정에서 Apple 서버와 통신이 필요하다.

따라서

  • 재시도
  • 타임아웃
  • 백오프

처리가 필요하다.


3. 서버 검증 구현 난이도가 높다

Apple은 검증 로직을 직접 제공하지 않는다.

서버에서

  • CBOR 파싱
  • X.509 인증서 검증
  • COSE 검증

을 구현해야 한다.

보통은 오픈소스 라이브러리를 활용한다.


결론

App Attest는 iOS에서 제공하는 가장 강력한 앱 무결성 검증 기술 중 하나다.

이를 도입하면 서버는 단순히 “로그인된 사용자”가 아니라

“실제 App Store에서 배포된 우리 앱이 생성한 요청”

인지를 검증할 수 있다.

특히 다음과 같은 서비스에서 효과가 크다.

  • 리워드 앱
  • 포인트 서비스
  • 쿠폰 서비스
  • 금융 서비스
  • 게임
  • 대규모 회원가입 이벤트

다만 App Attest는 단독 솔루션이 아니라 보안 체계의 한 축으로 보는 것이 중요하다.

실무에서는 App Attest + Play Integrity API + Device Intelligence + Fraud Score를 조합하여 다계층 방어 체계를 구축하는 것이 일반적이다.

추가로 AP랩 관점에서 “Play Integrity API와 App Attest 비교”, “캐시워크 같은 리워드 앱에서 App Attest를 어떻게 적용할 것인가”, “서버 검증 코드(Node.js/NestJS 예제)“까지 포함한 실무 버전으로 확장할 수도 있습니다.

728x90