【Snowflake】STAGE利用による意図せぬ情報漏洩を防げ! PRESIGNED_URLの制限
本記事で参考になるケース
- 内部STAGE, 外部STAGEの利用により発行可能になってしまう
GET_PRESIGNED_URL
に何らかの制限したい
記事の概要
- 前提として内部STAGEの
GET_PRESIGNED_URL
を制限する方法はなさそう
追記: 追加の設定によりNWポリシーを適用させることができる!私の見落としです!(記事末尾に記載) - 外部STAGEにおいてGCS(GC)を選択した場合も制限する方法はない
- 外部STAGEにおいてS3(AWS)を選択した場合詳細な制御が可能である
※Azureは未調査..
今回の課題
- 今回の問題はSnowflake上でユーザに対してNetwork Policyを設定した場合でも、そのユーザが実行した
SELECT GET_PRESIGNED_URL
により発行される署名付きURLにはデフォルトではNetwork PolicyのIP制限がかからないということ-
これは
- ユーザはSTAGEに大量の機密データを出力及びそのファイルに対する署名付きURLを発行することが可能である
- 上記の署名付きURLを知っている人間は「誰でも(発行したユーザ以外でも)どこからでも認証不要で」そのURLを用いデータをダウンロード/持ち出しできてしまう
ということである.
※署名付きURLに有効期限があるとしても場合によっては危険である.
-
GET_PRESIGNED_URL
の制御
AWS S3を外部STAGEに用いたSnowflake側では制御ができないように見える. したがってAWS側で制御を行う.
行うことは非常にシンプルであり、AWSのポリシーを用い制御する.
-
GET_PRESIGNED_URL
によって発行されたURLを常時無効にする方法
外部STAGE作成の際に作成したSTORAGE INTEGRATION
に設定したAWS IAM ROLEのポリシーに署名付きURL発行を制限するDeny Statementを追記する
Snowflake内
CREATE STORAGE INTEGRATION test_snowflake_stage
TYPE = EXTERNAL_STAGE
STORAGE_PROVIDER = S3
ENABLED = true
STORAGE_AWS_ROLE_ARN = 'arn:aws:iam::XXXXXXXX:role/ROLE_NAME'
STORAGE_ALLOWED_LOCATIONS = ('s3://BUCKET_NAME/');
上記のAWS IAM ROLEはarn:aws:iam::XXXXXXXX:role/ROLE_NAME
である.
このRoleのPolicyに下記のようにDeny...not equel s3:authType": "REST-HEADER
を追記する.
AWS内IAM Role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
},
{
"Effect": "Deny",
"Action": "s3:*",
"Resource": "arn:aws:s3:::BUCKET_NAME/*",
"Condition": {
"StringNotEquals": {
"s3:authType": "REST-HEADER"
}
}
}
]
}
検証
CREATE STAGE test
STORAGE_INTEGRATION = test_snowflake_stage
URL = 's3://BUCKET_NAME/';
COPY INTO @test/test
from (select 1)
file_format = (TYPE = CSV COMPRESSION = GZIP FIELD_DELIMITER = '\t' SKIP_HEADER = 1 );
SELECT GET_PRESIGNED_URL(@test,'test_0_0_0.csv.gz');
発行されたURLを利用した場合
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>XXXXXXXXXXXXXXXX</RequestId>
<HostId>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</HostId>
</Error>
無事拒否される
GET_PRESIGNED_URL
によって発行された署名付きURLを特定のIPから利用した時のみ有効(ダウンロード可能)にする方法
発展: Snowflakeの下記はAWS S3を外部STAGEにした場合を示す.
引き続きSnowflakeのSTORAGE INTEGRATION
に設定したAWSのIAM Rolearn:aws:iam::XXXXXXXX:role/ROLE_NAME
のPolicyを下記のように編集する.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
},
{
"Effect": "Deny",
"Action": "s3:*",
"Resource": "arn:aws:s3:::BUCKET_NAME/*",
"Condition": {
"StringNotEquals": {
"s3:authType": "REST-HEADER"
},
"NotIpAddress": {
"aws:SourceIp": [
"署名付きURLを用いてダウンロードできる接続元IP" // "123.456.789.101/32"
]
},
}
}
]
}
まとめ
SnowflakeにおいてSTAGEは非常に便利でありよく使うものになっている. 特にSnowflake in Streamlitなどでは必須で利用されるものであるが、利用の仕方によっては基盤の要件に合わないセキュリティ上の問題を作り出してしまう可能性がある.
この点は注意しておく必要があるかと思われます.
GET_PRESIGNED_URL
のIP制限
追記 内部STAGEのネットワークルールを使用するネットワークポリシーでAWS内部ステージへのアクセスを制限するかどうかを設定
alter account set enforce_network_rules_for_internal_stages = true;
これで適応されます.
ただし、注意事項がいくつか
・ネットワークルールを使用しないネットワークポリシーには影響しない(ネットワークルールをちゃんと作ってね!)
・外部ステージでAWS S3を利用した時のようなGET_PRESIGNED_URL
経由のみ利用できるIPの範囲を広げる/狭めるという細かな制御は難しそう?
Snowlfake データクラウドのユーザ会 SnowVillage のメンバーで運営しています。 Publication参加方法はこちらをご参照ください。 zenn.dev/dataheroes/articles/db5da0959b4bdd
Discussion