Akamai x UNIQLOコラボTシャツに書かれたプログラムを解読してみる

Akamai x UNIQLOコラボのTシャツのデザインが、エンジニアからすると目を引くようなものだった。

Akamaiのリリース記事を読むと、

Akamai、ユニクロのチャリティTシャツプロジェクト「PEACE FOR ALL」に参画|アカマイ・テクノロジーズ合同会社のプレスリリース

このデザインに使われている「texture」というコードは、さまざまデジタル体験の裏側にある共通言語を表現しています。

と書かれており、どんなコードなのかさらに気になったので、解読してみることに。

写経

写真をみた感じGoっぽい感じがする。とりあえず写経してみる。

gist.github.com

ぱっと見でわかったことは

  • HTTPサーバっぽい
  • channelが多用されていそう
  • コードの右端が途切れてそう
  • Goっぽいけどセミコロンがたくさんある(1行に書くため?)

くらいだった。右端が切れていることからコピペしただけでは動かないことがわかる。 整合性が取れていなさそうに見受けられる部分もありそうなので、補うだけではダメで、若干の修正も必要そう。

解読

ということで、前後の文脈を頼りに整合性をとりながら、コンパイルが通るようにしたコードがこちら。1時間くらい掛かってしまった。

gist.github.com

何をするコードなのか

コンパイルは通るようにしたが、実行すると全てのgoroutineが停止してしまうため、panicする。 httpサーバっぽいコードだが、listenする処理などがないため、それはそうだろうという感じがする。

雰囲気から察するに /admin/status 2つのエンドポイントを持っていて、/admin

type controlMessage struct {
    Target string
    Count  int
}

このようなメッセージを受け取ったら何かの処理をするサーバのようだ。 /status は処理中かどうかがわかるようだ。

それ以上の隠された何かは特に見つけられなかったので、知っていたら誰か教えてください。

このコードからの学び

statusPollChannel := make(chan chan bool)

チャネルのチャネル(chan chan)という使い方は見たことがなかった(意識してないだけかもしれない)ので、確かにそういう使い方もあるかという学びになった。

何か特定の自分がリクエストしたものに対しての応答を待ち受けるような実装があれば使えそう。

ここまでやってから気づいたこと

Tシャツのハイライトされた文字だけ読むと、、ということなのか😇

感想

ここまでTシャツのデザインを凝視したことはなかったので、ユニクロに立ち寄った際にこのTシャツが売っていたら買おうと思う。

「一句」を判定するライブラリikku-goをリリースしました

作ったもの

ikku-go というライブラリを作ったので公開しました。 github.com

ロゴ画像(?)はStable Diffusionで生成しました。

575を検知したい

このライブラリは何気ない会話から575が生まれているという気づきを与えてくれます。

もともとはr7kamuraさんの ikku を使っていたのですが、最近はgoを使うことが多かったため、go版も欲しくなったのが実装理由です。 実装の大部分を参考にさせていただきました。 GitHub - r7kamura/ikku: Discover haiku from text.

使い方

非常に簡単で、引数にとった文字列から5-7-5の形になっている部分を抽出します。

オプションで別途ルールを渡すことで、短歌のような5-7-5-7-7の型式にも対応可能です。 https://pkg.go.dev/github.com/kurochan/ikku-go#ReviewerOptionRule

import (
    "fmt"

    "github.com/ikawaha/kagome-dict/ipa"
    ikku "github.com/kurochan/ikku-go"
)

func main() {
    r, err := ikku.NewReviewer(ipa.Dict())
    if err != nil {
        panic(err)
    }
    song := r.Find("ああ古池や蛙飛び込む水の音。。")
    fmt.Println(song.String())
    // Output: 古池や蛙飛び込む水の音
}

形態素解析用の辞書の読み込みが重いせいか、残念ながらGo Playground上ではタイムアウトしてしまい動作せず。 https://go.dev/play/p/7BBNLRWaOml

実装

Goで実装された日本語形態素解析ライブラリ ikawaha/kagome を利用しています。

GitHub - ikawaha/kagome: Self-contained Japanese Morphological Analyzer written in pure Go

そのため別途mecabのネイティブライブラリを必要としないため利用が非常に楽です。cgoも不要です。

サンプルでは辞書にMeCab IPADICを利用していますが、他の辞書も利用可能となっています。

個人的にはコレクション操作系は https://github.com/samber/lo の書き心地が良いと思っていてよく使っているのですが、ここは好みが分かれるところかもしれません。

これ

func (s *Song) String() string {
    strs := lo.FlatMap(s.Phrases, func(ns []Node, _ int) []string {
        return lo.Map(ns, func(n Node, _ int) string { return n.String() })
    })
    return strings.Join(strs, "")
}

ikku-go/song.go at 026ed526fbeb0b2e2c76ec5df7a6cedf4923fe91 · kurochan/ikku-go · GitHub

samber/lo を使わないと

func (s *Song) String() string {
    var str string
    for _, ph := range s.Phrases {
        for _, n := range ph {
            str += n.String()
        }
    }
    return str
}

こうなります。(わかりにくいと言われそうな気がする)

こういう部分は1行で書けた方がスッキリしてていいんじゃないでしょうか。

case lo.Contains([]string{"助詞", "助動詞"}, n._type()):

ikku-go/node.go at 026ed526fbeb0b2e2c76ec5df7a6cedf4923fe91 · kurochan/ikku-go · GitHub

肝心の575判定ロジックの部分はこちらです。

ikku-go/scanner.go at 026ed526fbeb0b2e2c76ec5df7a6cedf4923fe91 · kurochan/ikku-go · GitHub

ほぼr7kamuraさんの判定ロジックを流用させて頂きました🙏

テスト

ひととおりのテストを書いたあとにfuzzingも入れてみました。

残念ながら?想定外のケースのバグは見つからず。。他のプロジェクトで意図しないバグが発見できたことがあったので、混み入ったロジックを実装した時はfuzzingも入れておきたいですね。

https://github.com/kurochan/ikku-go/blob/026ed526fbeb0b2e2c76ec5df7a6cedf4923fe91/reviewer_test.go#L236-L256

まとめ

  • go版の「一句」判定ライブラリを作りました
  • みんな気軽に575判定してみてね!

サーバサイドでJWTの即時無効化機能を持っていないサービスは脆弱なのか?

きっかけ

昨年(2021年9月ごろ)に徳丸さんのこのツイートを見て、「2022年にはJWTを用いたセッション管理に代表される、ステートレスなセッション管理は世の中に受け入れられなくなっていくのだろうか?」と思っていました。

結論

冒頭だけ読んで勘違いが広まるとよくないので、議論の結果最新版では方針が変わったということだけ先に書いておきます。詳細が気になる方は最後まで読んでみてください🙇‍♂️

現状(2022年3月)の日本語版の記述

OWASP Top10 2022の「A01:2021 – アクセス制御の不備」の日本語版では、2022年3月3日時点では以下のように書かれています。

JWTトークンはログアウト後にはサーバー上で無効とされるべきである。

github.com

この記述からは、ログアウト時にクライアントサイドでセッション(JWT)を捨てるだけでは不十分で、クライアント側がJWTを再利用すればサーバへのアクセスが成功してしまうのが問題であるということが読み取れます。 サーバ側で無効なJWTを管理しなければならないということは、JWTを利用したセッション管理に代表されるようなステートレスなセッション管理は認証の実装方法として不十分なのでしょうか?

議論

こちらのIssueで議論がされていました。(徳丸さんのツイートよりも後に行われた議論のようです)

github.com

議論の詳細はここでは書きませんが、ここの議論からわかることをまとめます。

まず、JWT仕様を見る限り、OAuthをベースとしたOpenID Connectで使用することが想定された設計であることは明らかです。

datatracker.ietf.org

OAuthには2つのトークンがあり、短命なアクセストークンと長命なリフレッシュトークンから構成されています。 それぞれのトークンがどんな形式であるべきかは指定されていませんが、アクセストークンにはJWTが使われている実装が多いようです。

アクセストークンの生存期間を短命にすることで、アクセストークンが不正に利用されるリスクを低減しています(リスクを低減しているのであって、完全に防いでいるわけではない)。この短命なトークンの実装としてJWTを使うことで、ステートレスな認証を実装することができ、データベースの負荷低減に寄与します。

その代わりにリフレッシュトークンはサーバサイドでの管理を必須とし、ユーザがログアウトするなどの操作を行うとリフレッシュトークンが即時無効化され、アクセストークンの更新はできなくなります。

この前提があるため、少なくとも長命なアクセストークンを生成することは許容されなさそうです。どれくらいの時間が短命なのかどうかはサービスごとのリスク許容度によって判断する形になりそうですね。

(追記ここから 2022/04/18) 徳丸さんから反応をいただいたのですが、

たしかに誤解が広がるとよくないので、こちらでも強調しておきます。 JWTは本来的にはセッション管理のためのものではなく認証情報の受け渡しのためのものですが、その便利な特性からセッション管理にも転用されることもあるという背景があります。

こちらも参考になったので紹介しておきます 🙇‍♂️

(追記ここまで)

「数分であろうと即時無効化されているべきであるセッションが有効なものとして使えるのはリスクは低減されているが問題であることに変わりはないのでは?」という意見ももちろんありますが、絶対に許容されないという温度感ではないようです。

その代わりに、この特性が一般にはまだ広く理解が広まっていないのでは?という懸念への対応として、以下のリンクを紹介することでメリットとデメリットを正しく理解してもらおう、ということで着地したようです。 www.oauth.com

変更

この議論の結果、2021年9月24日にはA01の記述を修正するプルリクエストが出おり、即日取り込まれていたのでした。

github.com

Stateful session identifiers should be invalidated on the server after logout. Stateless JWT tokens should rather be short-lived so that the window of opportunity for an attacker is minimized. For longer lived JWTs it's highy recommended to follow the OAUTH standards to revoke access.

ということで、

「ステートフルなセッション識別子は、ログアウト後にはサーバー上で無効とされるべきである。ステートレスなJWTトークンは、攻撃者の機会を最小にするために、むしろ短命であるべきである。寿命の長いJWTの場合は、アクセスを取り消すためにOAuth標準に従うことが強く推奨される。」

という内容に記述が変わりました。

日本語版が追いついていない😱

日本語版の記述はどうなっているかというと、この議論の結果が反映されておらず、英語版と日本語版で乖離が出ていることがわかりました。 日本語版だけを読んで勘違いが広がってしまうのもよくないので、英語版の最新に追従するプルリクエストを出してみました。問題がなければ取り込んでもらえるかな?と思っています。

github.com

JWTの取り扱いに関する記述が意味的な差分としては一番大きそうですが、他の差分に関しては軽微な修正だと思われます。

「Amazon S3のストレージ料金を無料にする裏技」改

こちらの記事を読んで「なるほど、確かに面白いな」と思いました。

kusano-k.hatenablog.com

特に気になったのはこの部分。まだまだいけるんじゃないかと。

ファイルの取り出しのLISTは1000件まとめて取得できるので、無視できる。 PUTリクエストは0.0047USD/1,000回。 1回、1バイトあたり6.3172×10-9USD。 ストレージ料金の「GB」が10003なのか10243なのか分からない。 10243とすると、6.783USD/GB。 ということで、3,392月=283年以上保存するならば、S3 Glacier Deep Archiveに保存するよりも、ファイル名にエンコードしたほうがお得💰💰💰

Amazon S3のストレージ料金を無料にする裏技 - kusano_k’s blog

追記(2022/01/28 11:00)

職場の方から指摘をもらって、AWSの公式FAQに

1 か月に請求されるストレージの量は、月全体を通じて使用される平均ストレージです。これには、お客様の AWS アカウントで、お客様が作成したバケット内に格納されるすべてのオブジェクトデータやメタデータが含まれます。

Amazon S3 のよくある質問

という記述があることを知りました。メタデータもストレージ使用量の課金対象のようなので、この手法は残念ながら(?)無料ではなさそうということが発覚しました。
ということでブレイクスルーは起きませんでした。残念🥺🥺🥺

ブレイクスルーを起こしたい💰💰💰

3,392月=283年以上保存するならば、S3 Glacier Deep Archiveに保存するよりも、ファイル名にエンコードしたほうがお得💰💰💰

Amazon S3のストレージ料金を無料にする裏技 - kusano_k’s blog

これを超える方法が何かないか考えてみたところ、思いついたのでご紹介します。

docs.aws.amazon.com

Amazon S3にはメタデータという自由記述欄があります。メタデータには2種類あり、「System-defined object metadata」と「User-defined object metadata」があります。

System-defined object metadata

System-definedなメタデータは使えるキーの名前が決まっていて、自由に追加はできません。試しに適当にオブジェクトをアップロードし、メタデータを確認しました。

{
    "AcceptRanges": "bytes",
    "LastModified": "2022-01-27T13:14:34+00:00",
    "ContentLength": 0,
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "text/plain",
    "Metadata": {}
}

余計なキーを増やすと容量を消費してしまうので追加しないとして、この中で唯一任意の文字列が設定できるのは ContentType 一択のようです。

AcceptRanges, ETag はドキュメントにはメタデータとして記載されていないので、容量は消費しなさそうです。keyとvalueの合計なので、 LastModified で37byte消費、ContentLength で14byte消費といったところでしょうか。 ContentType のキー名は11byteです。

System-defined object metadataは2KBまで保存できるようなので、 2048 - 37 - 14 - 11 = 1986byte 使えそうです。引用元記事同様Base64エンコードするとすれば 1938 / 4 * 3 = 1489byteですね。

User-defined object metadata

User-definedなメタデータは使えるキーの名前は x-amz-meta- ではじまればなんでもいいようです。ここで文字数を消費したくないので x-amz-meta-0 とでもしておきましょう。 x-amz-meta-0 のキー名は12byteです。

User-defined object metadataは2KBまで保存できるようなので、 2048 - 12 = 2036byte 使えそうです。引用元記事同様Base64エンコードするとすれば 2036 / 4 * 3 = 1527byteですね。

$ aws  s3api head-object --bucket bucket --key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.txt
{
    "AcceptRanges": "bytes",
    "LastModified": "2022-01-27T13:36:57+00:00",
    "ContentLength": 0,
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "Metadata": {
        "0": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
    }
}

AWSコンソール上の見た目もよくわからない感じになっている。

f:id:kuro_m88:20220127224241p:plain
aws console上の見た目

気になるお値段

list-objects-v2 APIではメタデータが取得できないので、head-object APIでオブジェクトごとにリクエストしないといけない。この操作にかかる料金は1回あたり0.0000004USDで、誤差ということで考えないことにする。

1個のオブジェクトのキー名に、(1024-32)/(4/3)で744バイト分のデータを詰め込める。

Amazon S3のストレージ料金を無料にする裏技 - kusano_k’s blog

ということだったので、 オブジェクトのキー: 744 byte, システムのメタデータ: 1489byte, ユーザのメタデータ: 1527byte, 744 + 1480 + 1527 = 3751byteまで保存できそう。3751 / 744 = 5.04倍保存できるので、

3,392月=283年以上保存するならば、S3 Glacier Deep Archiveに保存するよりも、ファイル名にエンコードしたほうがお得💰💰💰

Amazon S3のストレージ料金を無料にする裏技 - kusano_k’s blog

3392ヶ月 / 5.04 = 673ヶ月 = 56年以上保存するならば、S3 Glacier Deep Archiveに保存するよりも、ファイル名にエンコードしたほうがお得!!💰💰💰