おれさまラボ

実際に手を動かして理解を深めるブログ。

読書メモ:Building Secure & Reliable Systems - Chapter 4

はじめに

GoogleのSRE本第3弾『Building Secure & Reliable Systems』に関する読書メモです。今回は Chapter 4 を読んでみました。自分の理解を噛み砕いて書いているので、しっかり本意を理解したい方は原書を読むことをおすすめします。

Chapter 3 のメモはこちらからどうぞ。

第4章の全体像

第4章は開発プロジェクトではどのような点を考慮して進めなければならないのか、またセキュリティと信頼性はいつ、どのようにして考慮されるべきなのかが紹介されています。全体像を簡単に1枚のスライドにまとめてみました。

f:id:naoto408:20200523125559p:plain

既存システムへの信頼性とセキュリティの追加

通常、既存のシステムに対して信頼性とセキュリティをボルトオン(追加)することは困難です。依存関係が絡み合い、仕様を認知しづらいシステムの場合、可用性が低下し、セキュリティに影響を及ぼすようなバグが発生しやすくなります。そのため、付け焼き刃的な対応では通用せず、大幅な設計変更や大規模なリファクタリングを必要とし、莫大なコストが発生する可能性が高くなります。

また、セキュリティと信頼性が失われることで、組織や会社全体に深刻な影響を及ぼす可能性があります。また、ユーザーや顧客の信頼を失い、場合によっては廃業する可能性もあります。

だからこそ、開発プロジェクトの初期の計画段階から、セキュリティと信頼性の要件と、それに対応する設計上のトレードオフを検討することが重要となります。

開発の流れ

一般に、開発プロジェクトは以下のステップを踏みます。

  1. コンセプト
  2. 要件定義・設計
  3. 実装
  4. リリース

1. コンセプト

プロダクトイメージを練る段階です。どのようなプロダクトが必要とされているか、ターゲット層はどこなのかなど、戦略を練ります。

2. 要件定義・設計

コンセプトが見えてきたらシステム開発のための要件を定義します。洗い出された要件をもとに、システムを設計します。

機能要件

プロダクトを構成するために必要な機能を洗い出します。とくに、プロダクトがプロダクトたるために必要不可欠な要件を重要機能要件といいます。

非機能要件

特定の機能ではなく、システムの一般的な属性や動作に焦点をあてた要件を洗い出します。たとえば、

  • 誰がそのデータにアクセスできるのか
  • サービスレベル目標(SLO)は何か
  • ある閾値以上の負荷がかかったシステムはどうなるか

などの要件です。本書で扱っている「セキュリティ」や「信頼性」も非機能要件に含まれます。

システムの創発的特性

各々の機能単体では現れない特性が、システムとして各々の機能が連携することではじめて現れる特性のことを「創発的特性(emergent property)」といいます。システム設計にあたっては、機能レベルのミクロな視点だけでなく、一歩引いてシステムやサービスを見渡すマクロな視点も必要となります。とくに、セキュリティや信頼性の多くが創発的特性をもつため、機能要件の実装と相互に影響し合う傾向があります。システム設計時にはマクロな視野をもって創発的特性を見極めることが必要です。

制約

要件が明確になってくると、開発コストや運用コストの制約、アプリケーションの設計や実装に関する制約、プロジェクト期間に関する制約など、さまざまな制約も明らかになってきます。

もちろん、セキュリティや信頼性に関する制約も出てきます。たとえば、機密情報を扱うシステムであればそれらのユーザーデータを保護するに足りうるセキュリティ要件が必要とされます。

⚠ 要件定義で注意するポイント①

要件や制約の中にはトレードオフの関係となるものがあります。それらのバランスをとることは難しいですが、良いプロダクトを作るには、適切なバランスを見つけることが必要です。

たとえば、「セキュリティの要件」と「開発速度の要件」はトレードオフの関係にあります。小規模なチームほど、開発速度を重視するあまりセキュリティを無視することを正当化しがちです。しかし、良いシステムをつくるためにはセキュリティを開発初期段階から考慮することが極めて重要です。

インターネットプロトコル(IP、TCPDNS、BGP)がわかりやすい例です。インターネット黎明期には信頼性は重要な要件でしたが、いまよりもずっとユーザー数が少なかったですし、悪意を持ったユーザーの数も同様に少なかったため、セキュリティはあまり重要視されていませんでした。

そのため、インターネットの基本プロトコルには送信元を認証したり改ざんを検知するための仕組みが実装されていません。セキュリティが重要視される現代においては、HTTPSIPsecのように、プロトコルを拡張したり、新たなプロトコルを実装することによってセキュリティを高めています。しかしながら、これらの安全なプロトコルを普及させることがいかに困難かはみなさんがよくご存知のとおりです。

アジャイル開発を用いる場合、何よりも優先されるのは開発速度です。このようにして開発されたプロダクトではセキュリティの考慮が甘かったり、技術的負債を抱えていたりする可能性が高くなります。

Google's アドバイス

開発の初期段階で、セキュリティと信頼性に関する検討行い、そのためのコストを投資しましょう。開発初期段階にセキュリティと信頼性を考慮しておけば、サービスの安定運用につながります。

また、成熟したCI/CDの仕組みのもとでは、コードやプロジェクトの健全性、保守性、長期的かつ持続的なプロジェクトの速度などの品質も向上する傾向にあります。つまり、頻繁な変更に対して開発者に要求される努力は僅かなものですむため、プロダクトのライフサイクル全体における総工数を削減することにもつながります。

Googleには、セキュリティと信頼性のベストプラクティスを提供するフレームワークを開発するチームがあります。このフレームワークに従うことで、本質的により安全で信頼性の高いシステムが実現するようになっています。このようなフレームワークを用意しておくことで、とくに大規模な組織であれば、多くのプロジェクトにまたがって初期コストを償却できるため、スケールメリットを享受できます。

逆に、セキュリティと信頼性を後まわしにするとリスクとコストの増加につながりやすいことが分かっています。

テストの自動化が不十分で、デプロイ時の手動ステップに依存し、リリースサイクルが長い開発ワークフローは、プロジェクトが複雑化するにつれて、最終的にプロジェクトを泥沼化させる傾向があります。

⚠ 要件定義で注意するポイント②

機密性の高いデータを扱うシステムでは、ユーザーデータを保護するに足りうるセキュリティ要件が必要とされます。しかしながら、このセキュリティ要件を満たすシステムを1から開発するには莫大なコストがかかります。開発コストを最小限に留めつつ、セキュリティも妥協しない方法を探る必要があります。

Google's アドバイス

機密性の高いデータを扱うためにサードパーティのサービスプロバイダを使用しましょう。

機密性の高いデータに関するセキュリティ上の懸念を軽減する最善の方法は、そもそもデータを保持しないことです。サードパーティのサービスプロバイダを利用することで、セキュリティが強固なサービスプロバイダ上に機密性の高いデータを閉じ込めることで、開発システム側のセキュリティ要件を緩めることができます。

ただし、サードパーティのサービスプロバイダを使用する場合にもいくつかリスクがあります。

まず、コストです。多くは従量課金制なので、あまりにも多くのトランザクションが発生する場合には自前でシステムを用意したほうが安くなる傾向があります。

次に、エンジニアリングに関するリスクです。サードパーティシステムをプロダクトに組み込むにはサービスプロバイダが独自に用意したAPIを使わなくてはなりません。APIの仕様変更はコントロールできないため、変更に追従するエンジニアを抱えておく必要があります。APIの独自性が高いほどこのリスクは高くなります。REST+JSONXMLSOAP、またはgRPCのようなオープンなプロトコルを使用してAPIを公開している場合は、それらを選択したほうが良いでしょう。

ベンダの信頼性に関するリスクもあります。つまり、サードパーティのサービスプロバイダの選定にあたっては、機能要件だけでなく、機密性の高いデータを預けても問題ないベンダなのかどうかを見極める必要があります。これは、複雑な契約上の問題、規制上の問題、責任上の問題があるため、場合によっては弁護士を含めて検討する必要があるでしょう。

最後に、脆弱性や依存関係の問題です。システムは可能な限りシンプルであるべきです。人間が理解しづらいシステムでは、コンポーネントの依存関係が認知しづらく、脆弱性や事故の原因が生まれやすくなります。サードパーティのサービスプロバイダを導入することで機密性の高いデータに関するセキュリティリスクが低減する一方、コンポーネントの接続ポイントが増えるため、依存関係が生まれ、接続ポイントが脆弱性にもなり得ます。

リスクを理解した上で、サードパーティのサービスプロバイダをうまく活用しましょう。

3. 実装

機能要件、非機能要件を満たすコードを書きます。

💡 機能要件とソースコード、テストコードの間には強い相関関係がうまれます。そのため、機能要件は認知しやすい傾向があります。一方、非機能要件は特定するのが難しいケースが多いため、認知しづらい傾向にあります。

4. リリース

要件を実装したシステムをリリースします。リリース前にはデザインレビューを経て、品質に問題ないことを確認します。

Google's アドバイス

Googleでは開発者のためのデザイン文書テンプレートと開発フレームワークを用意しています。

開発者はこれらに従って開発をすすめることで、信頼性とセキュリティに関する要件をもれなく満たすことができます。

  • 拡張性
    • システムはどのように拡張しますか?データサイズの増加(該当する場合)とトラフィックの増加(該当する場合)の両方を考慮してください。
    • 現在のハードウェアの状況を考慮してください。リソースの追加には思った以上に時間がかかるかもしれませんし、プロジェクトには高すぎるかもしれません。初期に必要なリソースは?高い利用率を想定して計画する必要がありますが、必要以上のリソースを使用するとサービスの拡張が妨げられることに注意してください。
  • 冗長性と信頼性
    • ローカルデータの損失や一時的なエラー(一時的な停止など)をシステムがどのように処理するか、また、それぞれがシステムにどのような影響を与えるかについて説明してください。
      • データのバックアップが必要なシステムやコンポーネントは?データはどのようにバックアップされますか?データはどのように復元されますか?データが失われてから復元されるまでの間に何が起こるか?部分的な損失の場合、サービスを継続できますか?
      • バックアップの欠損部分だけをサービングデータストアに復元できますか?
  • 依存関係の考察
    • 他のサービスへの依存関係が一定期間利用できない場合はどうなりますか?
    • アプリケーションを起動するためには、どのサービスを実行しなければならないのでしょうか?DNS を使って名前を解決したり、ローカル時間をチェックしたりするような微妙な依存関係も忘れてはいけません。
    • アプリケーションが起動していない場合に実行できないシステムのブロッキングなど、依存性サイクルを導入していませんか?疑問がある場合は、依存しているシステムを所有しているチームとユースケースについて話し合ってください。
  • データの完全性
    • データストアのデータ破損や損失はどのようにして発見されますか?
    • どのようなデータ損失の原因(ユーザーエラー、アプリケーションのバグ、ストレージプラットフォームのバグ、サイト/レプリカの災害)が検出されますか?
    • これらのタイプの損失に気づくまでにどのくらいの時間がかかりますか?
    • これらのタイプの損失から回復するための計画は何ですか?
  • SLA要件
    • アプリケーションのサービスレベル保証を監査および監視するために、どのようなメカニズムを採用していますか?
    • 記載されている信頼性のレベルをどのように保証できますか?
  • セキュリティとプライバシーへの配慮
    • 私たちのシステムは定期的に攻撃を受けます。この設計に関連する潜在的な攻撃について考え、それがもたらす最悪の影響を、各攻撃を防止または緩和するために実施している対策と一緒に説明してください。
    • 既知の脆弱性や安全でない可能性のある依存関係をリストアップしてください。
    • 何らかの理由で、あなたのアプリケーションにセキュリティやプライバシーへの配慮がない場合は、その旨と理由を明示してください。

引用:Building Secure & Reliable Systems, p47 Google’s Design Document Template

まとめ

  • 安全で信頼性の高いシステムを設計・構築することは容易ではありません。とくにセキュリティと信頼性は、開発と運用のワークフロー全体の創発的な特性であるため設計・構築が難しいことを理解しなければなりません。
  • 設計プロセスでは、セキュリティ、信頼性、機能要件の間で多くのトレードオフが発生しやすいです。しかし、初期段階で投資しておけば、サービスの運営を安定して行うことができるようになる可能性が大幅に高まります。
  • 逆に怠れば、サービスがダウンする可能性があります。場合によってはビジネスそのものを失う可能性もあります。そうならないために、総力を上げて早い段階からセキュリティと信頼性を高めるために対応する必要があります。(=先に頑張っておけば後でらくできる)
  • 優れた計画と慎重な設計を行えば、セキュリティ、信頼性、機能要件の3つをすべて満たすことができるケースは多いです。また、システムの耐用年数に渡って、総工数を削減することができます。開発の初期段階からセキュリティ、信頼性を考慮することは無駄ではないということです。

以上