boto3からの解放。
python3の標準ライブラリのみで
AWSサービスを取り扱うには
小板橋 由誉
2022/1/27
Who am I ?
小板橋 由誉 / Yoshitaka Koitabashi
 KDDI株式会社 アジャイル開発部 ソフトウェア技術2G
所属チーム
 AR/VRなどxR向けの
コンテンツ管理システム(CMS)の開発
好きなAWSサービス
 AWS App Runner
ある日・・・
 ある日こんな要望が舞い降りてきました。
 「python3の標準ライブラリのみでAWSサービスに対して操作
(今回は、S3に対するCRUD操作-GetとPostに限定)できるようにして欲しいな。」
 AWSの各サービスをさわる方法は、いくつかあります。
 AWSマネジメントコンソールからの操作
 AWS SDKや、AWS CLIなどのAWSツールを使う方法
 blabla・・
AWSの各サービスをさわるには?
 Boto3というのは、AWS SDK for Pythonの別称であり、
Pythonを介してAWSを操作するためのライブラリ
 Boto3を入れるのは、簡単でpipでインストールできる
 Boto3の由来ちょっと気になって調べてみたら、アマゾン川に生息する
アマゾンカワイルカにちなんでつけたよう
https://github.com/boto/boto3/issues/1023
 ドキュメント:
(https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart
.html#installation)
Boto3 is 何?
Boto3は便利
• 各AWS内のサービスについての
操作ドキュメントがしっかりしている
• 各methodも使いやすい
こんな便利なboto3を使用しないで、
どうやってPythonからAWSの各サービスをさわればいいのーーー?
 boto3を使わないとなると、まずやらなければならないのはboto3が
何を隠蔽してくれているのかを確認する必要がある。
 まあ、単純に言えばBoto3がどんなREST APIのリクエストを
投げているのかを確認すればよい
 そこで、Boto3でDEBUGログを出力してあげると
簡単にREST APIのリクエストの内容が確認できます。
Boto3のDEBUGログ
DEBUGログの設定
boto3.set_stream_loggerでログ出力の設定ができる。
DEBUGログのデモ
REST APIのリクエストの中身が分かったので、
ここからBoto3を使用せずに、S3に対する
CRUD操作-GetとPostをしていきます
 まず、AWSへのAPIリクエストを行う場合
(AWS SDKや、AWS CLIなどのAWSツールを使わない場合)、
リクエストの署名するためのコードを含める必要があります。
 基本的には、この署名を気にすることはありません。
 AWS SDK、AWS CLIなどのAWSツールは、
ツール設定時に指定するアクセスキーを使用して
APIリクエストに自動で署名します
S3に対するGet処理
そもそも何で署名が必要なの??
 簡単に言えば、署名によってリクエストの
セキュリティ確保をしたい為です。
 AWSのリクエストに対する署名では次の点でセキュリティの確保を
行なっています。
署名が必要な理由
 リクエストのIDの確認
 署名により、有効なアクセスキーを持っている人がリクエストを送信したことを
確認できる
 送信中のデータの保護
 送信中のリクエストの改ざんを防ぐために、リクエストの要素からハッシュ値を
計算し、得られたハッシュ値をリクエストの一部として含めます。
 AWSがリクエストを受け取ると、同じ情報を使用してハッシュを計算し、
リクエストに含まれているハッシュ値と比較します。
ハッシュ値が一致しない場合、AWSはリクエストを拒否します。
=> Canonical Request
この時、HTTP Authorization ヘッダーを使用します。
 潜在的なリプレイ攻撃の防止
 リクエストに含まれるタイムスタンプの5分以内にAWSに到達する必要がある。
その条件を満たさない場合、AWSはリクエストを拒否します。
セキュリティ確保項目
つまりこの項目をboto3を使わないなら、
自分で実装しないといけない
やってやりましょう
 署名のバージョン
 AWSでは、署名バージョン4と署名バージョン2がサポートされている。
AWS CLI, AWS SDKは、署名バージョン4をサポートする
すべてのサービスに対して自動で署名バージョン4を使用します。
 なので今回は、使用する署名のバージョンは署名バージョン4にします。
実装前の確認
S3におけるCanonical Requestでは何が必要??
参照元:
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/API/sig-
v4-header-based-auth.html#canonical-request
 Canonical Requestでは、次のStepで署名の検証が行われる。
 ①: 署名するための文字列を決める。
 ②: 署名キーを使用して、署名する文字列のHMAC-SHA256ハッシュを
計算する。
 ③: s3は認証されたリクエストを受信すると、署名を計算しリクエストで
指定した署名と比較する。
 そのため、s3と同じ方法で署名を計算する必要がある
=> ここで、署名のために合意された形式でリクエストを送信するプロセスは、
正規化と呼ばれます。
言語化すると
実装じゃ
①:Canonical Requestの作成
まず、下記にあるのがS3におけるcanonical requestのフォーマット
 HTTPMethod : GET/PUT/HEAD/DELETE等のHTTPメソッドの1つです。
 CanonicalURI : URIのURIエンコードバージョンです。
ドメイン名に続く「/」で始まり、文字列末尾まで、
または疑問符文字( '?')までのすべてを指定します。
 CanonicalQueryString : URIエンコーディングされたクエリパラメータを指定します。
 CanonicalHeaders : リクエストヘッダーとその値のリストです。
個々のヘッダー名と値のペアは、改行文字( " n")で区切られます。
ヘッダー名は小文字にする必要があります。
また、CanonicalHeadersは下記のものを必ず含めなければなりません。
 HTTPホストヘッダー
 Content-Typeヘッダーがリクエストに存在する場合は、追加
 リクエストに含める予定のx-amz-*ヘッダーも追加。たとえば、一時的なセキュリティ
クレデンシャルを使用している場合は、リクエストにx-amz-security-tokenを含める
必要があります。
Canonical Requestの詳細(説明割愛)
 SignedHeaders : アルファベット順にソートされたセミコロンで
区切られた小文字のリクエストヘッダー名のリストです。 リスト内のリクエス
トヘッダーは、CanonicalHeaders文字列に含めたものと同じヘッダーです。
 HashedPayload : リクエストペイロードのSHA256ハッシュの16進値です。
 ちなみに、GETリクエストを使用してオブジェクトを取得する場合、空の文字列ハッ
シュを計算します。
Canonical Requestの詳細(説明割愛)
CanonicalHeadersだけ説明
CanonicalHeaders : リクエストヘッダーとその値のリスト
• また、CanonicalHeadersは下記のものを必ず含めなければなりません。
• HTTPホストヘッダー
• Content-Typeヘッダーがリクエストに存在する場合は、
追加リクエストに含める予定のx-amz-*ヘッダーも追加。
たとえば、一時的なセキュリティクレデンシャルを使用している場合
は、リクエストにx-amz-security-tokenを含める必要がある。
↑Cognitoの認証tokenとかが該当
CanonicalHeadersのサンプル
②: 署名する文字列を作成
• AWS4-HMAC-SHA256: ハッシュアルゴリズムHMAC-SHA256を
使用していることを示します。
• timeStamp : ISO8601形式の現在のUTC時刻を入れます。
• Scope : 結果の署名を特定の日付、AWSリージョン、およびサービス名を
連結したものを入れます。
結果の署名は、特定の地域および特定のサービスでのみ機能し、
署名は指定された日付から7日間有効です。
 AWS署名バージョン4では、AWSアクセスキーを使用してリクエストに署
名する代わりに、最初に特定のリージョンとサービスを対象と
する署名キーを作成します。
③: 署名を計算
S3の特定のbucketに対してGetするサンプルコード
ここまでがGetの実装
次からは、POSTの実装
 S3に対するアップロードの処理で気をつけなければならないのは、アッ
プロードするデータの種類を固定させるわけにはいかない
 複合データ型(=multipart)を扱える、multipart/form-dataとい
う形式でアップロードしなければならない。
S3に対するPost処理
 multipart/form-dataは、複数の種類のデータを一度に扱える形式。
 気をつけなければならないのは、
下記のようにboundaryをコンテンツの境界を示す文字列として
入れてあげなければならない。
multipart/form-dataでの送信
あれ・・・
そういえばmultipartでの送信って、
Python3の標準ライブラリで簡単にできるっけ?
 今回は、Python3の標準ライブラリのみを使用して、
s3に対してアップロードしたい。
 外部ライブラリで有名なrequestsというものがありますが、
こちらを使うと簡単にmultipart/form-data形式の送信ができますが、今
回はurllibで頑張って実装していきます。
ちなみにrequestを使うと、1行でできる笑
requests.post(url, data=form, files={"file": file}, verify=verify)
ということで・・
urllibでmultipart/form-data送信する
Postの実装をまとめると
あとは、作成したクラスを使用しS3に対してmultipart/form-dataで
アップロードを実行してあげれば完成
まとめ
PythonでAWSサービスを扱う時可能なら、
boto3を使うべき
コード等はQiitaに掲載中

Boto3からの解放。python3の標準ライブラリのみでawsサービスを取り扱うには