CognitoをIPアドレスでアクセス制限する(AWS WAF対応!)

まとめ

方法①:Cognitoアドバンスドセキュリティ

  • 「0.0.0.0/0」の代わりに「0.0.0.0/1」+「128.0.0.0/1」
    • IPv6も対応されました
  • IP制限のためだけには割高感

※こちらは想定する挙動にならなかったため、方法から外します。(後述)


方法②:Cognito認証前Lambdaトリガー

  • API(AWSのcheckipなど)でIP情報を取得してCognitoに渡す
  • 料金割安だが、その分自前コーディングが必要


方法③:AWS WAF <- new!!

  • 2022年8月11日対応されました!
  • 一番簡単に設定が可能
  • IPv6も設定可能


目的

  • APIエンドポイントでの認可時にIP制限するのではなく、Cognitoユーザープールでの認証時にIP制限したい
    • = 認証すらさせない


①Cognitoアドバンスドセキュリティ

概要

想定する挙動にならなかった(「常にブロック」に全IPのCIDR、「常に許可」に該当IPを入れてアクセスしてみてもブロックされた)ため、方法から外しますが試した方法だけ載せておきます。「常に許可」より「常にブロック」が優先的に効いてしまうようです。

  • 「常にブロック」に全IPのCIDR、「常に許可」に該当IPを入れることでIP制限が可能
    • 「常にブロック」に「0.0.0.0/0」を入れるとエラーになる
    • CIDRの工夫でクリア
      • 「常にブロックに全IP」=「0.0.0.0/1」+「128.0.0.0/1」
    • IPv6のCIDRはエラーになります
      • 現在はIPv6にも対応されています
      • 上記ブロック設定例はIPv4用の設定になります


  • アドバンスドセキュリティは、ユーザ単位で追加料金がかかる
    • IP制限のためだけに使うには割高感もある


詳細は公式ドキュメントにも記載があります。

docs.aws.amazon.com


使用例

※Cognitoコンソール等で、アドバンスドセキュリティ機能をオンにする必要があります。

CognitoAPIのInitiateAuthのパラメータに以下の追加が必要になります。

// Request Parameters for InitiateAuth 
 "UserContextData": { 
    "EncodedData": "string"
 }


HTML例

<HEAD>
...
<script src="https://amazon-cognito-assets.<region>.amazoncognito.com/amazon-cognito-advanced-security-data.min.js"></script>
</HEAD>


javascript例(InitiateAuthパラメータ)

...
...
var encodedData = AmazonCognitoAdvancedSecurityData.getData(username, userPoolId, userPoolClientId);
...
...
var params = {
    AuthFlow: 'USER_PASSWORD_AUTH',
    ClientId: userPoolClientId,
    AuthParameters: {
        'USERNAME' : username,
        "PASSWORD": password
    },
    UserContextData: {
        'EncodedData': encodedData
    }
};



②Cognito認証前Lambdaトリガー

概要

  • 「Cognito -> 認証前Lambdaトリガー」のパラメータ(event)にはIP情報はない


  • 「クライアント -> Cognito(InitiateAuth)」のClientMetadataというパラメータに自由なキー・値が入れられる(それがLambdaトリガーに渡せる)
    • そこにIP情報を入れてCognitoにPostを投げる
    • すると認証前Lambda側へのリクエストのvalidationDataにClientMetadataの情報が入る
      • event["request"]["validationData"]



使用例

※認証前LambdaトリガーとするLambdaを事前に作成し、Cognitoコンソールの「トリガー」の「認証前」にてLambdaをCognitoに紐付ける必要があります。

javascript例(InitiateAuthパラメータ)

var params = {
        AuthFlow: 'USER_PASSWORD_AUTH',
        ClientId: userPoolClientId,
        AuthParameters: {
            'USERNAME' : username,
            "PASSWORD": password
        },
        ClientMetadata: {
            'IpAddress': '192.168.0.15' //実際には、APIなどからIP取得して格納した変数を使う
        }
    };


認証前Lambdaトリガー例(Python)

def lambda_handler(event, context):

    # IPホワイトリスト
    # 必要があれば配列に
    # 本来ならSSM Parameter StoreやDynamoDBなどに貯めてそこから取ってくる
    ip = '192.168.0.15' 

    if ( event["request"]["validationData"]["IpAddress"] == ip ) :
        print('success from this IP address')
    else :
        print('failed by ipadress')
        raise Exception("Cannot authenticate users from this IP address")
    return event


方法③:AWS WAF

概要

AWSサービスでIP制限といえばWAFというくらい代表的なサービスですが、今までCognitoには対応していませんでした。

それがなんと、2022年8月11日、CognitoにWAFが対応されました!

aws.amazon.com


使用例

※ここではAWSコンソールでの操作方法を記載しますが、CloudFormationでIP制限のためのWAFを作成する方法はこちらをご覧ください。

go-to-k.hatenablog.com


※また、CDKで同じくIP制限のためのWAFを作成する方法も記事にしていますので、良かったらご覧ください。

go-to-k.hatenablog.com


IPセット作成

まずIPのホワイトリストとして、IPセットというものを先に作ります。

AWSコンソールから「WAF & Shield」に行き、左タブより「IP Sets」を選択し、右上の「Create IP set」をクリックします。


「IP set name」には任意のIPセット名、そして「Region」にはアタッチするCognitoと同じリージョンを選択してください。

また、「IP version」には許可したいIPの種類(IPv4 or IPv6)を、「IP addresses」には許可したいIPのCIDRを指定してください。


WebACL作成

ここでWebACLという、WAFそのものを作成し、先程作成したIPセットを割り当てます。

左タブから「Web ACLs」を選択し、右上の「Create web ACL」をクリックしましょう。


まず名前などを入力してください。「Resource type」はCognito User Poolsと書かれている「Regional resources」の方を選択しましょう。

Regionは、アタッチしたいCognitoと同じリージョンを選択します。


その下の「Associated AWS resources」にて、アタッチするCognitoを選択します。

まず「Add AWS resources」をクリックしたあと、「Amazon Cognito User Pools」を選択しましょう。すると下の検索欄でCognitoのユーザープールが一覧で出てくるので、アタッチしたいものを選択してください。

そして「Next」を押して次の画面「Step 2」へ進みます。


次に、先程作成したIPセットを、アクセスを許可するホワイトリストとして選択します。

まず、「Add rules」の「Add my own rules and rule groups」を選択しましょう。


出てきた画面の一番最初「Rule type」では「IP set」を選択し、「Name」には任意の名前を入れます。

その下の「IP set」では先程作成したIPセットを選択し、「Source IP address」、Actionには「Allow」を選択し、右下の「Add rule」をクリックします。

これによって、IPセットをホワイトリストとして使用できます。


すると先程のStep 2の画面(「Add rules」の「Add my own rules and rule groups」を選択した画面)に戻ります。

ここでは、一番下の「Default action」では「Block」を選択しましょう。これにより、「デフォルトのアクセスはブロックされ、IPセットに当てはまるアクセスは許可される」というようなアクセスの挙動、つまりIP制限を実現することができます。


そしてStep 3,4ではそのままの状態で「Next」を選択して大丈夫です。


最後にStep 5画面にて確認をして、一番下の「Create web ACL」をクリックします。


これでIP制限のためのWAF(WebACL)を作成し、Cognitoにアタッチすることができました。


まとめ(再掲)

方法①:Cognitoアドバンスドセキュリティ

  • 「0.0.0.0/0」の代わりに「0.0.0.0/1」+「128.0.0.0/1」
    • IPv6も対応されました
  • IP制限のためだけには割高感

※こちらは想定する挙動にならなかったため、方法から外します。(後述)


方法②:Cognito認証前Lambdaトリガー

  • API(AWSのcheckipなど)でIP情報を取得してCognitoに渡す
  • 料金割安だが、その分自前コーディングが必要


方法③:AWS WAF

  • 2022年8月11日対応されました!
  • 一番簡単に設定が可能
  • IPv6も設定可能