セキュア・バイ・デザインを読み、DDDの値オブジェクト(value_object)を利用することで自然とセキュアなアプリケーションを作れることを学んだのでまとめます。
文字通りセキュリティに関する書籍なのですが、セキュリティエンジニアのような専門的な知識がなくとも、設計によって自然とセキュアなアプリケーションとなるように努めようという内容になります。
セキュリティを心配事として捉える
例えば以下のようなアプリケーションのユーザーストーリーがあるとします。
写真格納サービスのユーザーとして、私はログイン画面が欲しいです。そうすることで私は自身がアップロードした写真の一覧画面にアクセスできるようになります。
この機能を満たすようにログイン画面を実装し、一覧画面から写真のダウンロードができるようになったとして、それだけで機能として大丈夫でしょうか?
もし、別のユーザーがそれらの写真をダウンロードするリンクを何らかの方法で入手することができれば、そのリンクを直接呼び出すことで写真がダウンロードできてしまいます。
上記は少し極端な例かもしれませんが、セキュリティを機能
として見るのではなく、心配事
として見ることでユーザーストーリーを改善することができます。
写真格納サービスのユーザーとして、私は自身がアップロードした写真へのすべてのアクセスを認証によって保護したいです。それによって私がアップロードした写真の機密性が保護されます。
写真の機密性を担保したいという心配事に関心を移したため、より抽象的かつユーザーの意図することを正確に表現できるようになりました。
たしかに、チケットを作成する際に機能要件しか記載していなかったため、直リンクの防止、不正な値のバリデーションといったセキュリティが満たされていないことは身に覚えがあります。
ドメイン・プリミティブによるセキュリティ担保
読んでいる中で、一番費用対効果が大きく導入しやすいセキュリティ改善策として印象に残ったのがドメイン・プリミティブです。
DDDで言う値オブジェクトを拡張した概念であり、生成されるときに不変条件を確認し、存在自体が有効であることを保証されたものだそうです。
個人的には、値オブジェクトはそれ自体で正当性を保証できなければ意味がないと思うので、ほぼ同一の概念として読み進めました。(この記事でも同一のものとして扱います)
以下のようなUserクラスがあったとします。
class User
attr_reader :id, :name
def initialize id, name
@id = id
@name = name
end
end
このUserクラスをインスタンス化する際に渡されるname
に
<script>alert('hoge!!!')</script>
のようなスクリプトが入力されるとどうなるでしょうか?
このようないたずら程度のスクリプトならどうにかなるかもしれませんが、Cookieの値を読み取って送信するような悪質なスクリプトだと深刻な問題に発展するかもしれません。
では以下のようにXSS対策を組み込んでみましょう。
class User
attr_reader :id, :name
def initialize id, name
@id = id
@name = validate_for_xss(name)
end
end
たしかにこれでXSSへの対策は行えますが、以下のような問題が発生します。
- ビジネスロジックとセキュリティ、2つのことを常に考えなくてはならない
- すべての開発者がセキュリティ専門家と同レベルの知識を求められることになる
- 今後起こりうるすべての脆弱性を把握していることが前提となる
いずれも開発効率を悪化させますし、現実的な対策が困難です。
こういった問題に対しては、セキュリティよりも設計を中心に考え、ドメイン・プリミティブを利用することが有効です。
ドメイン分析を行い、ユーザー名について以下のような結論が出たとします。
- 半角英数字のみ使用可
- 長さは4〜40文字
Userクラスをドメイン・プリミティブを用いて再設計すると以下のようになります。
class User
attr_reader :id, :name
def initialize id, name
@id = id
# stringではなくドメイン・プリミティブであるUserNameクラスを利用する
@name = UserName.new(name)
end
end
class UserName
attr_reader :value
def initialize value
# 半角英数字のみ含まれており、長さが4~40文字であることを確認する(実装は省略)
validate(value)
@value = value
end
end
このように設計することで、仮にユーザー名に
<script>alert('hoge!!!')</script>
と入力されても有効な値でないためUserNameクラスが自動的にユーザー登録を防いでくれます。
注目すべきは、セキュリティについて考えるまでもなく、自然とセキュアなアプリケーションとなっている
ことです。
先に挙げた問題点についても以下のように解決できています。
- ビジネスロジックの実装、セキュリティの保証を同時に行える
- セキュリティの専門家でなくても安全なコードを書けるようになる
- 気がつかないうちにセキュリティに関する多くのバグを取り除けるようになる
まとめ
設計によってセキュリティを改善する手段がまとめられた良書でした。
セキュリティに関する専門知識がなくても読むことができ、DDDについての知識を拡充することができます。
なぜDDDなのか?というメリットも語られており、チームメンバーにそのメリットを紹介する際にも使えると思います。
値オブジェクトによるセキュリティ担保は比較的導入しやすい設計改善策なのでプロジェクトで実施していきます。