iOSアプリにおけるユーザー中心セキュリティモデルの構築

この講演では、アプリのシークレットキーが漏洩してしまったときや、サーバーがハッキングされたときでも問題が怒らないセキュリティを構築する方法について説明します。一意のユーザーの暗号キー(またはパスワード)が持続するセキュリティは安全です。ユーザーが知っているシークレットを信頼の元にすることは、セキュリティモデルに関連してアプリが「薄く」なり、リスクや開発者の苦痛を軽減する究極の方法です。

このtry!Swiftの講演では、Anastasiiaは、薄く明確なセキュリティレイヤーシステムと、__クライアント/サーバーシステムでの適用性__について議論しています。そして、もちろん、__ATSの最新の変更__についてもです。


イントロダクション (00:00)

Anastasiiaです。ここ数年、私はモバイルセキュリティについて話してきました。この講演では、例として実世界のアプリをカバーし、リスクモデルと脅威モデルを理解します。ほとんどの制御がモバイル開発者の手元にあるセキュリティシステムを作成します。サーバーの障害が発生しても、このモデルでは新たなリスクが発生します。勇気を出してください。セキュリティの話ですが、ちょっと複雑な話になるかもしれません。

なぜセキュリティについてもっと考えなければならないのですか?ユーザーは物事は起こるだけの魔法の世界に住んでおり、すぐに安全だと思ってしまいます。しかし、Pegasusを覚えていますか?リンクを押すだけでiPhoneを脱獄させます。これは珍しいケースで何もすることはできませんが、アプリでセキュリティを実装することに努力すべきではない、ということではありません。適切なセキュリティ戦略を実装していなければ、アタッカーはPegasusを必要としません。その場合、アプリからより簡単にデータを取得します。

なぜセキュリティについてもっと考えなければならないか? (00:55)

ほとんどのユーザーは、どのように守られているかは気にせず、機密データをアプリに信頼しています。優れたセキュリティシステムを構築することは私たちの責任です。しかしセキュリティ界隈の人々はセキュリティのレンズを通して世界を見ている傾向があります。ほとんどのセキュリティの話は、アプリを球状の牛※と見なすようないくつかの方法に注意を払います。セキュリティーそのものを、X、Y、Zなどとしないで…。そうではありません。球状の牛を育てているわけではなく、実際のユーザーのために実際のアプリをつくっており、これがなぜセキュリティが適切な方法で実装されるべきなのかを議論する理由です​。※球状の牛(Spherical cow)…複雑な現象を単純化しすぎたせいで、実際の現象に則さなくなったことの例え。

リスクモデル & 脅威モデル (03:56)

アプリを構築する際には、ユーザーの問題から始め、スクリーンフローの計画、機能の追加、最後にコードの作成に移ります。セキュリティについても、結構似ています。

セキュリティは、リスクモデルと脅威モデルから始まります。その後、セキュリティの方法と実装に移り、最終的にライブラリを選択してコードを記述します。最も一般的な質問である、「どのライブラリを使用すべきか」というのはシーケンスの最後です。アプリが特定の脅威モデルと攻撃者に対して安全であるかどうかを考える必要があります。そこから、すべてのリスクと対策を引き出すのです。

先程信頼について話しました。 信頼モデルとは何でしょう? 信頼モデルは誰の秘密をあなたのシステムで信頼していますかということです。私たちの場合は簡単です。すべてが壊れていて、誰も信じていません。ユーザー以外は。

実世界のアプリ (04:15)

実際のアプリには、ヒーローが必要です。Hectorは私たちのヒーローです。彼は私たちのユーザーです。 Hectorがアプリをインストールするとすぐに、彼はシステムに信頼を置きました。保護する機密性の高いデータ(パスワードや個人の写真など)がある場合があります。Hectorは妄想ユーザーだと思います。彼は自分のデータを保護したいと考えています。私たちは彼のようなユーザーのためのアプリを作成します。

サンプルアプリは旅行のアプリです 。パスポートや保険を持っているなら、普通はバックアップがあります。どこかを旅行しているときに、このバックアップを安全な方法で保管したいと考えています。

これはアプリケーションアーキテクチャです。ユーザーが写真を撮ると、アプリはネットワーク経由でサーバーに送信し、サーバーは写真を一部のストレージに保存します。 簡単です

脅威モデルはそんなに詳しいものではありません。遠隔脱獄、ゴムホース暗号解読など、多くの可能性のある脅威を想像することができます。残念ながら、それらをすべて処理することはできません。

私たちがコントロールしている脅威だけを残しましょう。 T1とT4は、データが盗難または変更される可能性があることを意味します。 T2は、ネットワークがインターセプトされる可能性があることを意味します。 T3は、ネットワークが傍受され、データがリダイレクトされる可能性があることを意味します。 あらゆる脅威に対し、対策を講じます 。 1つ1つ、やっていきましょう!

T1/T4脅威 (07:20)

T1とT4はどちらも、データを盗んだり改ざんしようとするアタッカーに関するものです。あなたはiPhone Bugsのようなアプリを使って簡単にiPhone上で行うことができます。サーバー側では、さらに簡単です。対称暗号化を使用してユーザーのデータを暗号化する必要があります。ユーザーにはシークレットがあり、そのシークレットを使用してデータを暗号化します。ユーザーがシークレットを知らずにデータを復号化したい場合、ユーザーはそれを行うことはできません。あるいは少なくとも、ユーザーはトレースなしでそれを行うことはできません。

T2脅威 (08:06)

T2は、パッシブな中間者で、アタッカーが送信中にデータを収集します。 CharlesやBurbのようなプロキシを使ってエミュレートするのは簡単ですが、それがなくても簡単です。 トランスポートを話す ということは、 非対称暗号化 を意味し、双方の身元を確実に話しています。ただし、非対称暗号化を使用して一時的な対称キーを1つだけ取得する方が安全です。

あなたは、”perfect forward secrecy(完全前方秘匿性)” という言葉、つまりPFSを知っているかもしれません。これは、データがセッションに分割されていることを意味し、セッションごとに一時的な鍵が導出されます。 1つのキーが漏洩した場合は、1つのセッションのデータだけを復号化することができます。それをすべて解読することはできません。

T3脅威 (09:05)

T3はアクティブな中間者です。それは、アタッカーがトラフィックを傍受するだけでなく、リダイレクトしてサーバーになりすますことです。たとえば、偽物のSSL証明書を使用してSSL接続を危険にさらす可能性のあるゴールデンシールドファイアウォールを使用して、中国と名前を付けることができます。また、非対称暗号化についても言及していますが、今度は認証ピンを使用した非対称暗号化について説明しています。

アプリでSSLピン設定を既に実装されているかもしれません。つまり、アプリはパブリックサーバー証明書を内部に保存し、ネットワーク接続ごとに保存された証明書とサーバーの証明書を比較します。証明書が異なる場合、このネットワーク接続は拒否されます。

保護する方法 (10:13)

スライド42では、保護する方法の一覧を用意しました。

  • 対称暗号
  • 一時的な鍵を使用した非対称暗号
  • 認証ピンを使用した非対称暗号

これが暗号化についてです。暗号化とはキーを意味し、キーは信用を意味します。

Trust & Relationship Model (10:38)

私たちの信頼モデルに戻りましょう。私たちは誰を信頼していますか? ユーザーのみですね 。アプリに戻ります。

  • ユーザーは写真を撮る
  • 写真をアプリに追加する
  • アプリは対称暗号化を使用して写真を暗号化し、コンテナにラップする
  • アプリとサーバーが安全なセッションを開始し、一時的な鍵に同意し、コンテナをこれらのキーにドロップする
  • アプリはサーバーにそれを送信します
  • サーバーはコンテナーを展開し、暗号化された写真を保管します。

暗号化されたバージョンのみを保存することに注意してください。

このスキームで私がこれまでに話していないコンポーネントが1つあります。クライアントとサーバー、クライアントとトランスポート、またはこれらのすべての間に信頼関係がない場合があります。すべての暗号化に失敗した場合でも、サーバーを正当に検証する方法があります。

ZKP - ゼロ知識証明 (11:59)

ゼロ知識証明は、2つの当事者間の事前共有シークレットを比較する簡単な数学的プロトコルです.2つの当事者は、このシークレットをネットワーク経由で送信することはなく、シークレット自体も、このシークレットから派生したハッシュも送信しません。 ZKPは、サーバーに実際にデータがあることを保証するのに役立ちます。

ZKPの詳細はこの素晴らしいブログで読むことができます。魔法ではないゼロ知識証明プロトコルです。

データモデル (12:50)

スライド50では、データモデルがどのように見えるかを示しています。ユーザーパスワードがあり、KDFを使ったパスワードから秘密鍵を生成します。KDFは鍵導出関数で、もうひとつの簡単な数学的関数です。普通の文字列を暗号的に強い鍵に変換します。

それから写真があり、これも機密データです。プライベートであるべきです。暗号化しましょう。

2つのメタデータがあります。

  • メタデータM1は、基本的にタイムスタンプと、写真のCRCです。
  • メタデータM2は、写真の名前で、アプリがユーザーに入力を求めます。

メタデータはパブリックで、追加のチェックに使用します。また、アプリとサーバーの両方がキーペアを生成します。プライベート+パブリック鍵を使用し、アプリの内部に公開サーバ鍵があることを忘れないでください。

スライド51では、私たちのスキームがどのようになっているのかを見ることができます。本当のパスワードは、ユーザーの心の中にだけあります。次に、秘密鍵、機密データ、2つのメタデータ、およびモバイル鍵ペアとサーバー鍵ペアがあります。

これがトラスト層です。すべてがユーザーのシークレットから導かれています。ユーザーのシークレットを得るためにパスワードを使用します。ユーザーの対称暗号化のシークレットを使用します。また、キーをいくつかの対称コンテナに暗号化し、これらのキーをトランスポート層の暗号化に使用します。すべてがパスワードから派生しています。

アプリのフロー (14:55)

アプリのフローを見てみましょう。スライドにはわかりやすいようにいくつかの擬似コードが含まれています。私はThemisというライブラリの暗号プリミティブを使用しました。

  • SCell - 対称暗号化のための安全なセル、対称コンテナの生成
  • SSession - トランスポート暗号化のための安全なセッション
  • SComparator - セキュアコンパレータはZKPプロトコルの実装

あなたは好きなライブラリを使うことができます。しかし、私が知っている唯一のZKP実装はThemisライブラリの中にあります。 1つ1つ始めましょう。

サーバーに画像を送る

  • データの準備
    1. アプリはユーザにパスワードを求め、パスワードから秘密鍵を生成し、画像を暗号化します。EncData = SCell_wrap(SD, SK, Context=M2)
    2. 暗号化したデータを保存し、メタデータを取得し、ユーザーに画像の名前を求め、保存します。EncData, M1, M2
    3. メモリ上から元画像を取り除きます、SD, SK
  • データの転送 4.アプリは、秘密鍵とサーバの公開鍵を使用して、サーバへの安全なセッション接続を開始します。 Session = SSession(Priv(MKP), Pub(SKP)) 5.暗号化された情報を送信すると、アプリケーションサーバーは一時的なキーを1つ取り出し、既に暗号化されたコンテナを暗号化し、暗号化された写真とメタデータをサーバーに送信します、EncData, M1, M2
  • セッションの終了
    1. サーバーからOKを受信します。
    2. アプリがセッションを終了します。つまり、次回、別の写真を送信したい場合は、別のセッションを開始し、別の一時的なキーを生成します。
    3. セッションを終了し、このデータを同期させてマークします, EncData

サーバーからの画像の読み込み

  • 安全なセッション接続を初期化する
    1. アプリの秘密鍵とサーバーの公開鍵を使って起動します。
    2. アプリがこのサーバーに実際に写真が含まれていることを確認する必要があるため、最も難しい部分です。サーバーでZKPの手順を開始します。基本的に、メタデータM1をサーバに送信し、サーバにメタデータM2を有することを証明するように要求し、このメタデータをネットワークを介して送信しません。アプリケーションがタイムスタンプ + CRCをサーバーに送信し、サーバーに写真の名前があるかどうかを尋ねることを意味します。サーバーは、ZKPプロトコルを使用して送信することなく、写真の名前があることを証明する必要があります。この証明が成功した場合にのみ、アプリはサーバーに写真の送信を依頼します。 ZKPが失敗した場合、サーバーが写真を持っていることを証明できないため、続行しないでください。
  • データ転送
    1. 暗号化されたデータEncDataを受信します。
  • データの復号化
    1. アプリケーションが写真を受け取ると、それは写真がまだ暗号化されていることを記憶しているので、パスワードを解読するようにユーザーに求めます。 SD = SCell_unwrap(EncData, SK, Context=M2)
    2. 最後に写真をユーザーに見せてください。

このセキュリティ概念の理解と実装に役立つSwiftのサンプルコードがいくつかあります。

リンク:

キーの生成


// Generating EC keys
guard let keyGeneratorEC: TSKeyGen = 
TSKeyGen(algorithm: .EC) else {
    print("Error occurred while initializing object keyGeneratorEC")
    return
}

let privateKeyEC: NSData = keyGeneratorEC.privateKey
let publicKeyEC: NSData = keyGeneratorEC.publicKey

対称暗号化


let masterKeyData: NSData = self.generateMasterKey()
guard let cellSeal: TSCellSeal = TSCellSeal(key: masterKeyData) else {
    print("Error occurred while initializing object cellSeal", #function)
    return
}
let message: String = "All your base are belong to us!"
let context: String = "For great justice"

var encryptedMessage: NSData = NSData()
do {
    // context is optional parameter and may be ignored
    encryptedMessage = try cellSeal.wrapData(message.dataUsingEncoding(NSUTF8StringEncoding), context: context.dataUsingEncoding(NSUTF8StringEncoding))
    print("encryptedMessages = \(encryptedMessage)")
} catch let error as NSError {
    print("Error occurred while encrypting \(error)", #function)
    return
}

対称復号化


let masterKeyData: NSData = self.generateMasterKey()
guard let cellSeal: TSCellSeal = TSCellSeal(key: masterKeyData) else {
    print("Error occurred while initializing object cellSeal", #function)
    return
}
let message: String = "All your base are belong to us!"
let context: String = "For great justice"

do {
    let decryptedMessage: NSData = try cellSeal.unwrapData(encryptedMessage, context: context.dataUsingEncoding(NSUTF8StringEncoding))
    let resultString: String = String(data: decryptedMessage, encoding: NSUTF8StringEncoding)!
    print("decryptedMessage = \(resultString)")
} catch let error as NSError {
    print("Error occurred while decrypting \(error)", #function)
    return
}

セッションの初期化


guard let clientIdData: NSData = kClientId.dataUsingEncoding(NSUTF8StringEncoding), let clientPrivateKey: NSData = NSData(base64EncodedString: kClientPrivateKey, options: .IgnoreUnknownCharacters) else {
    print("Error occurred during base64 encoding", #function)
    return
}

self.transport = Transport()
self.transport?.setupKeys(kServerId, serverPublicKey: kServerPublicKey)
self.session = TSSession(userId: clientIdData, privateKey: clientPrivateKey, callbacks: self.transport)

暗号化/復号化のセッションメッセージ


var encryptedMessage: NSData
do {
    guard let wrappedMessage: NSData = try
    self.session?.wrapData(message.dataUsingEncoding(NSUTF8StringEncoding)) else {
        print("Error occurred during wrapping message ", #function)
        return
    }
    encryptedMessage = wrappedMessage
} catch let error as NSError {
    print("Error occurred while wrapping message \(error)", #function)
    completion(data: nil, error: error)
    return
}

//...

do {
    guard let decryptedMessage: NSData = try self.session?.unwrapData(data),
        let resultString: String = String(data: decryptedMessage, encoding: NSUTF8StringEncoding)
else {
        throw NSError(domain: "com.example", code: -3, userInfo: nil)
    }
    completion(data: resultString, error: nil)
} catch let error as NSError {
    print("Error occurred while decrypting message \(error)", #function)
    completion(data: nil, error: error)
    return
}

より強くする (19:00)

さらに解読を難しくする方法があります。たとえば、アプリには1つのパスワードだけでなく、すべての写真に1つのパスワードを使用します。または、トランスポート層暗号化を行い、トランスポート層にさらに多くのチェックを追加することもできます。

Apple セキュリティプラクティス (19:32)

あなたは何を覚えておくべきしょうか? Appleは、セキュリティの安全性など、優れたセキュリティ対策を施しています。ほとんどの場所でHTTPSを使用し、すべての暗号を削除する必要があります。

Nabla のブログ記事は、システムを動かすために、変更する必要があることを正確に説明しています。プライバシー設定、アドレス帳、Home Kit、House Kitなどにアクセスするための新しい要件です。

また、Black Hat conferenceのスライドも確認してみてください。AppleのIvanが深刻なセキュリティ上の問題について話しました。

最後に、私が指摘したいことは、Appleが私たちの話と同じ原則に従っているということです。

まとめ (20:55)

  • ユーザーの信頼は実装が簡単で(8段階か7段階)、ハッキングが複雑です。
  • リスクモデルと脅威モデルから始めて、システム全体としてのセキュリティを考えている場合 のみ 発生します。別々のメソッドのセットとしてではありません。

  • 新しいアプリについて考えて、これらのスライドを読む必要があります。
  • 完璧な転送秘密、SSL Pinning、ZKPについての簡単なアイディアを載せた私の他のスライドを読んでみてもいいかもしれません。
  • 最新のWWDCビデオを見るといいと思います。昨年に公開された、WWDCのセキュリティとプライバシーに関する3つのビデオがあります。 Appleは実際にそれを強制しています。
  • Appleのトランスポートセキュリティに関する素晴らしいブログ記事を見るといいと思います。プライバシー設定ZKP、またはペガサススパイウェアがあります。

セキュリティを構築すること、アプリにセキュリティを追加すること、さらにはどのライブラリを使用すべきかについてご質問がある場合は、私に連絡してください。

参考資料


Anastasiia Voitova

Anastasiia Voitova

Anastasiia is a software engineer working at Stanfy. She’s been building iOS applications for several years, participating in the full application lifecycle: from gathering business demands and cost estimation, through UX prototyping, to developing and long-term supporting. Often building both client and server sides and sharing her knowledge with the community from both sides of barricades.

She went into computer security and cryptography when she was invited to fix a few lines of code in an iOS port of a cryptographic library, and ended up taking over all of iOS development and some general mobile ideology part of the project. She physically lives in Kyiv, Ukraine, and spends her time online tweeting as @vixentael.

Transcribed by Sandra Sanchez-Roige