信頼できないクライアントとの検索アプリケーションの使用
検索ユースケースのためのフロントエンドアプリケーションを構築する際、検索結果を返すための主なアプローチは2つあります:
- 1. クライアント(ユーザーのブラウザ)がアプリケーションのバックエンドにAPIリクエストを行い、バックエンドがElasticsearchにリクエストを行います。Elasticsearchクラスターはエンドユーザーに公開されません。
- 2. クライアント(ユーザーのブラウザ)が検索サービスに直接APIリクエストを行います。この場合、Elasticsearchクラスターはクライアントに到達可能です。
このガイドでは、2番目のアプローチを取る際のベストプラクティスについて説明します。具体的には、Search Application Search APIに直接リクエストを行うフロントエンドアプリと検索アプリケーションの使用方法を説明します。
このアプローチにはいくつかの利点があります:
- フロントエンドアプリケーションとElasticsearchの間にパススルークエリシステムを維持する必要がありません
- Elasticsearchへの直接リクエストは、応答時間を短縮します
- クエリ設定は1か所で管理されます:Elasticsearch内の検索アプリケーション設定
以下の内容をカバーします:
役割制限を持つElasticsearch APIキーの使用
フロントエンドアプリケーションがElasticsearchに直接APIリクエストを行うことができる場合、実行できる操作を制限することが重要です。この場合、フロントエンドアプリケーションはSearch Application Search APIのみを呼び出すことができる必要があります。これを確実にするために、役割制限を持つElasticsearch APIキーを作成します。役割制限は、役割が有効であるべき条件を指定するために使用されます。
以下のElasticsearch APIキーは、Search Application Search APIを介してのみwebsite-product-search
検索アプリケーションにアクセスできます:
Python
resp = client.security.create_api_key(
name="my-restricted-api-key",
expiration="7d",
role_descriptors={
"my-restricted-role-descriptor": {
"indices": [
{
"names": [
"website-product-search"
],
"privileges": [
"read"
]
}
],
"restriction": {
"workflows": [
"search_application_query"
]
}
}
},
)
print(resp)
Js
const response = await client.security.createApiKey({
name: "my-restricted-api-key",
expiration: "7d",
role_descriptors: {
"my-restricted-role-descriptor": {
indices: [
{
names: ["website-product-search"],
privileges: ["read"],
},
],
restriction: {
workflows: ["search_application_query"],
},
},
},
});
console.log(response);
コンソール
POST /_security/api_key
{
"name": "my-restricted-api-key",
"expiration": "7d",
"role_descriptors": {
"my-restricted-role-descriptor": {
"indices": [
{
"names": ["website-product-search"],
"privileges": ["read"]
}
],
"restriction": {
"workflows": ["search_application_query"]
}
}
}
}
indices.name は検索アプリケーションの名前であり、基盤となるElasticsearchインデックスではありません。 |
|
restriction.workflows は具体的な値search_application_query に設定する必要があります。 |
ワークフローレストリクションを指定することが重要です。これがないと、Elasticsearch APIキーは_search
を直接呼び出し、任意のElasticsearchクエリを発行できます。これは信頼できないクライアントを扱う際には安全ではありません。
コンソール-結果
{
"id": "v1CCJYkBvb5Pg9T-_JgO",
"name": "my-restricted-api-key",
"expiration": 1689156288526,
"api_key": "ztVI-1Q4RjS8qFDxAVet5w",
"encoded": "djFDQ0pZa0J2YjVQZzlULV9KZ086enRWSS0xUTRSalM4cUZEeEFWZXQ1dw"
}
エンコードされた値は、Authorizationヘッダーで直接使用できます。以下はcURLを使用した例です:
シェル
curl -XPOST "http://localhost:9200/_application/search_application/website-product-search/_search" \
-H "Content-Type: application/json" \
-H "Authorization: ApiKey djFDQ0pZa0J2YjVQZzlULV9KZ086enRWSS0xUTRSalM4cUZEeEFWZXQ1dw" \
-d '{
"params": {
"field_name": "color",
"field_value": "red",
"agg_size": 5
}
}'
expiration
が存在しない場合、デフォルトでElasticsearch APIキーは期限切れになりません。APIキーは、APIキー無効化APIを使用して無効にできます。
役割制限を持つElasticsearch APIキーは、フィールドおよびドキュメントレベルのセキュリティも使用できます。これにより、フロントエンドアプリケーションが検索アプリケーションをクエリする方法がさらに制限されます。
検索アプリケーションでのパラメータ検証
検索アプリケーションは、検索テンプレートを使用してクエリをレンダリングします。テンプレートパラメータはSearch Application Search APIに渡されます。フロントエンドアプリケーションや信頼できないクライアントが使用するAPIの場合、厳格なパラメータ検証が必要です。検索アプリケーションは、Search Application Search APIが許可するパラメータを説明するJSONスキーマを定義します。
以下の例は、厳格なパラメータ検証を持つ検索アプリケーションを定義します:
Python
resp = client.search_application.put(
name="website-product-search",
search_application={
"indices": [
"website-products"
],
"template": {
"script": {
"source": {
"query": {
"term": {
"{{field_name}}": "{{field_value}}"
}
},
"aggs": {
"color_facet": {
"terms": {
"field": "color",
"size": "{{agg_size}}"
}
}
}
},
"params": {
"field_name": "product_name",
"field_value": "hello world",
"agg_size": 5
}
},
"dictionary": {
"properties": {
"field_name": {
"type": "string",
"enum": [
"name",
"color",
"description"
]
},
"field_value": {
"type": "string"
},
"agg_size": {
"type": "integer",
"minimum": 1,
"maximum": 10
}
},
"required": [
"field_name"
],
"additionalProperties": False
}
}
},
)
print(resp)
Js
const response = await client.searchApplication.put({
name: "website-product-search",
search_application: {
indices: ["website-products"],
template: {
script: {
source: {
query: {
term: {
"{{field_name}}": "{{field_value}}",
},
},
aggs: {
color_facet: {
terms: {
field: "color",
size: "{{agg_size}}",
},
},
},
},
params: {
field_name: "product_name",
field_value: "hello world",
agg_size: 5,
},
},
dictionary: {
properties: {
field_name: {
type: "string",
enum: ["name", "color", "description"],
},
field_value: {
type: "string",
},
agg_size: {
type: "integer",
minimum: 1,
maximum: 10,
},
},
required: ["field_name"],
additionalProperties: false,
},
},
},
});
console.log(response);
コンソール
PUT _application/search_application/website-product-search
{
"indices": [
"website-products"
],
"template": {
"script": {
"source": {
"query": {
"term": {
"{{field_name}}": "{{field_value}}"
}
},
"aggs": {
"color_facet": {
"terms": {
"field": "color",
"size": "{{agg_size}}"
}
}
}
},
"params": {
"field_name": "product_name",
"field_value": "hello world",
"agg_size": 5
}
},
"dictionary": {
"properties": {
"field_name": {
"type": "string",
"enum": ["name", "color", "description"]
},
"field_value": {
"type": "string"
},
"agg_size": {
"type": "integer",
"minimum": 1,
"maximum": 10
}
},
"required": [
"field_name"
],
"additionalProperties": false
}
}
}
その定義を使用して、Search Application Search APIは次のパラメータ検証を実行します:
field_name
、field_value
、aggs_size
パラメータのみを受け入れますfield_name
は「name」、「color」、「description」の値のみを取ることが制限されていますagg_size
は用語集約のサイズを定義し、1
と10
の間の値のみを取ることができます
CORSとの連携
このアプローチを使用すると、ユーザーのブラウザがElasticsearch APIに直接リクエストを行います。Elasticsearchはクロスオリジンリソースシェアリング(CORS)をサポートしていますが、この機能はデフォルトで無効になっています。したがって、ブラウザはこれらのリクエストをブロックします。
これに対する2つの回避策があります:
ElasticsearchでCORSを有効にする
これは最も簡単なオプションです。次の内容をelasticsearch.yml
ファイルに追加してElasticsearchでCORSを有効にします:
Yaml
http.cors.allow-origin: "*" # ローカル開発のためにのみ制限のない値を使用してください
# 本番環境で特定のオリジン値を使用するには、`http.cors.allow-origin: "https://
<my-website-domain.example>"`のようにします
http.cors.enabled: true
http.cors.allow-credentials: true
http.cors.allow-methods: OPTIONS, POST
http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length, Authorization, Access-Control-Allow-Headers, Accept
Elastic Cloudでは、Elasticsearchユーザー設定を編集することでこれを行うことができます。
- 1. デプロイメントメニューから、編集ページに移動します。
- 2. Elasticsearchセクションで、ユーザー設定と拡張機能を管理を選択します。
- 3. 上記の設定でユーザー設定を更新します。
- 4. 変更を保存を選択します。
CORSをサポートするサーバーを介してリクエストをプロキシする
ElasticsearchでCORSを有効にできない場合、CORSをサポートするサーバーを介してリクエストをプロキシすることができます。これはより複雑ですが、実行可能なオプションです。