Javaでアプリケーションに柔軟なACLフレームワークを作成しようとしています。
多くのACLフレームワークは、ルールのホワイトリストに基づいて構築されており、ルールはowner:action:resourceの形式になります。例えば、
ルールは簡単にシリアライズ/データベースに永続化できるため、これは魅力的です。しかし、私のアプリケーションには複雑なビジネスロジックがあります。例えば、
最初に考えたとき、このような無限に複雑なルールを処理できるデータベーススキーマを考案するのは悪夢でしょう。したがって、私はそれらをコンパイル済みアプリケーションに「焼き付け」、ユーザーごとに評価してから、結果としてowner:action:resourceルールを生成する必要があるようです。評価。 ロジックをコンパイル済みアプリケーションに焼き付けないようにしたい。
そのため、ルールをpredicate:action:resourceの形式で表すことを考えていました。ここで、述語は、ユーザーが許可されるかどうかを決定するブール式です。述語は、JavaのRhinoエンジンで評価できるJavaScript式の文字列です。例えば、
return user.getDept() == 1 && user.seniority > 5;
そうすることで、述語を簡単にデータベースに永続化できます。
これは賢いですか?これはずさんなですか?これはギミックですか?これは過度に設計されたですか?これはsafeです(どうやら、JavaはRhinoエンジンをサンドボックス化できます)。
動的データを実装言語のインタープリターにパイプすることは、データ破損の可能性を悪意のあるアプリケーションの乗っ取りの可能性にエスカレートするため、通常は悪い考えです。つまり、作成コードインジェクション の脆弱性に陥っています。
あなたの問題はrules engineまたは多分domain-specific language(DSL)によって解決することができます。それらの概念を調べてください。車輪を再発明する必要はありません。
私はこれを行いましたが、しないことをお勧めします。
私がしたことは、すべてのビジネスロジックをLuaで記述し、そのLuaスクリプトをデータベースに格納することでした。アプリケーションが起動すると、スクリプトが読み込まれて実行されます。そうすれば、新しいバイナリを配布することなく、アプリケーションのビジネスロジックを更新できます。
私は常に、変更を行うときに常にバイナリを更新する必要があることを発見しました。一部の変更はLuaスクリプトにありましたが、必ず行う必要のある変更のリストがあるので、ほとんどの場合、バイナリに変更を加え、Luaスクリプトに変更を加える必要がありました。バイナリを常に配布することを避けることができるという私の想像力は、うまく行きませんでした。
私がはるかに役立つと感じたのは、バイナリの配布を容易にすることでした。私のアプリケーションは、起動時に更新を自動的にチェックし、ダウンロードして、更新をインストールします。したがって、ユーザーは常に、私がプッシュした最新のバイナリを使用しています。バイナリの変更とスクリプトの変更の間にほとんど違いはありません。もう一度やったら、アップデートをシームレスにするためにもっと努力したいと思います。
データベースにコードを含めないでください。しかし、データベースに関数名を含め、リフレクションを使用してそれらを呼び出すことで、同様のことを行うことができます。新しい条件を追加するときは、コードとデータベースに追加する必要がありますが、渡された条件とパラメーターを組み合わせて、非常に複雑な評価を作成できます。
つまり、番号付きの部門がある場合、UserDepartmentIsチェックとTodayIsAfterチェックを取得し、それらを組み合わせてDepartment = 2とToday> 03/15/2016にするのは簡単です。許可を終了できるようにTodayIsBeforeチェックを行いたい場合は、TodayIsBefore関数を記述する必要があります。
ユーザー権限についてはこれを行っていませんが、データ検証のために行っていますが、機能するはずです。
XACMLは、あなたが本当に求めているソリューションです。これは、アクセス制御のみに焦点を当てたタイプのルールエンジンです。 XACMLは、OASISによって定義された標準であり、3つの部分を定義します。
アーキテクチャは次のとおりです。
最初の使用例は次のとおりです。
/*
* All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
*
*/
policy departmentOne{
target clause department == 1
apply firstApplicable
/**
* All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
*/
rule allowFooBar1{
target clause resourceId=="FOOBAR-1" and seniority>=5 and actionId=="VIEW"
permit
}
rule denyOtherAccess{
deny
}
}
2番目の使用例は次のとおりです。
/*
* "All users in department 2, if the date is after 03/15/2016, can VIEW resource FOOBAR-2, else not authorized"
*
*/
policy departmentTwo{
target clause department == 1
apply firstApplicable
rule allowFooBar2{
target clause resourceId=="FOOBAR-1" and seniority>=5 and currentDate>"2016/03/15":date and actionId=="VIEW"
permit
}
rule denyOtherAccess{
deny
}
}
参照を使用して、両方のユースケースを1つのポリシーに結合できます。
policyset global{
apply firstApplicable
departmentOne
departmentTwo
}
これで完了です。
XACMLとALFAの詳細については、以下を参照してください。
ここで本当に必要なのは [〜#〜] xacml [〜#〜] です。それはほとんどあなたがまさにあなたが望むものをあなたに与えます。すべての役割が完全に分離された完全なアーキテクチャを必ずしも実装する必要はありません...アプリケーションが1つしかない場合は、おそらくPDPとPEPをアプリに統合して balana そして、PIPは既存のユーザーデータベースが何であってもです。
これで、アプリ内のどこかで何かを承認する必要がある場合、ユーザー、アクション、およびコンテキストを含むXACMLリクエストを作成します。XACMLエンジンは、作成したXACMLポリシーファイルに基づいて決定を行います。これらのポリシーファイルは、データベース、ファイルシステム、または構成を保持する場所に保存できます。 Axiomaticsには、生のXMLよりも読みやすいALFAと呼ばれるXACML XML表現に代わる素晴らしい代替手段と、ALFAポリシーからXACML XMLを生成するEclipseプラグインがあります。
私は現在の会社でこれを行い、結果に非常に満足しています。
私たちの式はjsで書かれており、ElasticSearchのクエリからユーザーが取得できる結果を制限するために使用します。
秘訣は、決定を下すために十分な情報が利用可能であることを確認することです。これにより、コードを変更せずに必要なパーマを実際に記述できると同時に、高速に保つことができます。
アクセス許可はシステムを攻撃する必要のないユーザーによって書き込まれるため、コードインジェクション攻撃を心配する必要はありません。そして、同じことがwhile(true)
の例のようなDOS攻撃にも当てはまります。システムの管理者はそれをする必要はありません、彼らは皆の許可を単に削除することができます...
XACMLのようなものは、組織の中央認証管理ポイントとして優れているようです。私たちのユースケースは少し異なります。クライアントには通常、それを実行するIT部門がありません。自己完結型のものが必要でしたが、できるだけ多くの柔軟性を維持しようとしました。