はじめに
最近AWS Chaliceを利用したPython+AWS lamba+AWS API Gatewayが便利そうでしたので、実際に利用して簡単なWebサービス(匿名掲示板)を作成してみました。作成したWebサービスや、ソースコードは下記で参照できます。
作成したWebサービス
ソースコード
基本設計
今回作成したサービスの要件
主な要件は下記としました。- AWSのサーバーレスな機能で完結すること
複数のプラットフォームを利用すると、管理も開発も格段に面倒になりますのでAWSに統一します。また、インフラの管理が必要になるRDS等は使用しません。 - ログイン不要で読み書き可能な掲示板を提供すること
スレッドを立てて書き込む、昔ながらの掲示板を想定しました。 - スレッドを立て、スレッドに対して書き込みが行えること
- 最低限のセキュリティを考慮すること
実際に利用することを想定するならセキュリティは切っても切り離せません。 - 必ずしも自動化やChaliceの機能をフルで利用する必要はないこと
Cloud Formation等、自動化の手段は色々ありますが、自動化そのものに気を遣う必要が出てくるため、今回は基本的に利用しないことを想定します。Chaliceの機能でも、小回りが利かないところは利用しません。
URL構造
URLの構造は以下のようにしました。パス | メソッド | 概要 |
/ | GET | スレッド一覧の表示 |
/ | POST | 新しいスレッドの作成 |
/{num}/ | GET | スレッドの中身の表示 |
/{num}/ | POST | スレッドへの書き込み |
セキュリティ
最低限行うセキュリティとして、下記を実装します。ユーザーの書き込み時(スレッド作成を含む)
- CSRF対策
- HTMLタグのエスケープ処理
リスク分析
ちなみに、上記の対策は、下記のようなリスク分析に基づきます。リスク内容 | 対策 |
データベースインジェクションが発生するような書き込みを行われ、データベースへの不正操作が発生する。 | プレースホルダーを利用する。 |
CSRF攻撃が発生し、不正な書き込みが行われる。 | CSRF対策として、ランダムな文字列をFormに組み込む。 |
HTMLタグが書き込まれ、閲覧者のブラウザ上で不正なスクリプトが実行される。 | HTMLタグは全てエスケープ処理する。 |
データ構造
データベースはDynamoDBを利用し、下記のようなデータを保存します。大量のアクセスがあることは想定しないため、パーティションキーやソートキーは分かりやすさを優先しています(性能を優先する場合、1文字目が適度にばらけるようにしましょう)。パーティションキー | ソートキー | データ | 概要 |
index | thread | index:number | スレッドのIDを採番する |
index | {number} | index:number | 対象スレッドの書き込みに対するIDを採番する |
threadlist | {number} | title:string,date:string,disable:bool | スレッド一覧を保持する |
thread_{number} | {number} | body:string,writer:string,date:string,ipaddr:string,disable:bool | スレッドへの書き込みを保持する |
csrf | {string} | expired:string,ip:string,ttl:number | CSRF用のランダム文字列。IPアドレスとセットでチェックする。TTLを設定しておき、自動的に削除されるようにする。 |
実装と解説
ここから実装の中でポイントとなるところを解説します。全体はソースコードを確認してください。
DynamoDBにテーブルの作成
DynamoDBにテーブルを作成します。普通にboto3を利用して作成しましょう。
なお、本スクリプトではテーブル作成後にTTLを有効化しています。
TTLを有効にしておくと、指定された時刻を過ぎたデータがバッチで削除されるようになります。
今回は、CSRF対策のランダム文字列がアクセスされる度に生成されるため、これらを自動削除するために設定しています。なお、TTLに設定された時刻を過ぎてもすぐに削除されるとは限らないため、「削除されている時もあれば、削除されていない時もある」と言う前提で構築が必要です。
また、テーブル作成を開始してから、実際に利用できるようになるまで多少時間がかかるため、Waitを入れて生成が完了するのを待つようにしています。
AWSのregionやアクセスキーに関するエラーが出た場合、環境に値を設定してください。ソースコードに直接埋め込む方式もありますが、今回は採用していません。
app.pyの初期化部分
Chalice以外にboto3、Jinja2の初期化を行っています。ChaliceはREST APIを前提に作られていますが、普通にWebページを出力させることも可能です。
また、DynamoDBを直接操作する関数群は無いため、boto3を利用します。
テスト部分
テストはpytestとpytest-chaliceを基本的に利用します。ただし、そのままだとDynamoDB部分のモックが存在せず、本番環境に接続しようとします。
そのため、conftest.pyで、app.pyをインポートする前に、DynamoDBのモックをmotoで作成しています。
テンプレートファイルとアクセス権
HTMLを出力するためのテンプレートファイルは、「/chalicelib/templates」以下に保存するようにします。「/charlicelib」以外に設置されたファイルはデプロイされません。また、プログラムの実行はファイルの所有者でもグループでも無いユーザーで実行されるため、「その他」に実行権限や読み込み権限が無いと、Permission Errorで実行が失敗します。
IAMロール
Chaliceはlambdaの実行に必要なIAMロールを自動生成してくれますが、boto3に関するIAMロールは基本的に生成しないと思ってください。そのため、「.chalice」以下にpolicyのjsonを設置し、デプロイは「chalice deploy --no-autogen-policy」で実行します。
policyはアクセス対象となるDynamoDBのARN等を記載する必要があるため、修正が必要です。
0 件のコメント :
コメントを投稿