この記事は「正解」を示すものではなく、SREとして日々インフラに関わる中で感じた個人的な考えです。セキュリティを体系的に学びたい方は、専門の書籍や公式ドキュメントもあわせて参照してください。
最近、ソフトウェアの脆弱性をきっかけにしたセキュリティ事案が続いています。ニュースを見ながら「外部に公開しているサービスは、結局どう守ればいいのか」を改めて考えたので、2つの視点でまとめてみました。
この記事は「正解」を示すものではなく、SREとして日々インフラに関わる中で感じた個人的な考えです。セキュリティを体系的に学びたい方は、専門の書籍や公式ドキュメントもあわせて参照してください。
最近、ソフトウェアの脆弱性をきっかけにしたセキュリティ事案が続いています。ニュースを見ながら「外部に公開しているサービスは、結局どう守ればいいのか」を改めて考えたので、2つの視点でまとめてみました。
以前から「6歳になったら踊り子に乗りたい」と言っていたが、ここ最近はその話をしなくなっていた。おそらく、自分の中でブームが終わったのかな?と思っていたところだった。
ところが、なぜか最近になってまた言い始め、いきなり週末に行きたいと言い出した。
ちょうど日曜日に予定がなかったので、行ってみることにした。
踊り子は主に伊豆方面へ向かう電車だが、子どもと二人で伊豆に行く予定はないし、行ったとしても特にやることがない。
そこで発想を変えて、上り電車に乗って都会で遊んでくることにした。横浜なら何かしらやることがある。
私も踊り子に乗るのは初めてだったし、きっと子どもは運転席を見たがるだろうと思ったので、JRのえきねっとで座席を選んで予約した。
海側ではなく陸側の席だったが、ちょうど一番前の席が空いていた。しかも、隣の席も空いていたので、窓側を予約した。
子どもは朝からテンション高めで、早起き。何度も「お昼ごはんの頃に乗れるよ」と伝えたが、いまいち理解していない様子だった。
結局、乗車時間の2時間ほど前に小田原駅に到着。いつもの図書館と子育てサロンのコースに行った。
予想どおり、あまり時間をつぶせなかったので、結局、先に昼ごはんを食べることにした。
一応、妻におにぎりを作ってもらっていたが、それだけでは足りないと思うので、先に少し食べても問題ないはず。
いつも行っているハンバーガー屋で軽めの昼ごはんとアイスを食べたら、ちょうどいい時間になった。飲まずに持っていたコーラを持ち、駅に戻った。
列車は意外と混んでいた。予約したときには空いていた隣の席にも、おばさんが座っていて、すでに晩酌を始めていた。
仕方なく、子どもを膝の上に座らせた。
乗車時間はそれほど長くないので、移動中も子どもは喜んでいた。途中で運転席も見に行った。もちろん、抱っこで。
まずはNゲージ体験のため、ポポンデッタ横浜西口店(https://www.popondetta.com/yokohama/)へ移動した。
移転前は商業施設の中にあったのでアクセスが便利だったが、今回はだいぶ遠くなったように感じた。
それでも、子どもはテンション高めで、文句も言わずに歩いてくれた。
体験時間は20分。車両は一番安いものを選び、料金は800円だった。
鉄道クラブにあったコントローラーとは少し違っていたが、問題なく操縦できた。
最初は20分では足りないのではないかと心配していたが、意外にも15分ほどで満足したのか、自分から切り上げた。
もちろん、店内にはいろいろな電車関連の商品があるので、何か買いたいらしい。
意外とNゲージの車両もばら売りされていて、動力車は2,800円程度だった。どうやら欲しいらしい。
とはいえ、線路などもそろえる必要があるし、いきなり購入するのも大変なので、いったんママと相談することにした。
一応、何か買うつもりで来ていたので、お土産として電車の靴下を購入した。
おそらくまた来ることになりそうなので、会員証も作った。
次の場所へ移動しようとしたところ、問題発生。
トイレに行きたいらしいが、店員さんに聞いたところ、店内にはトイレがないとのこと。
来た道を戻り、コンビニでトイレを借りた。こういうことも何年ぶりだろう。
その後は、京急ミュージアムへ移動した。
本来は横浜駅まで戻って地下鉄を利用するつもりだったが、距離的に微妙だったので、歩いて行くことにした。
意外にも、子どもは文句を言わずに歩いてくれた。やっぱり楽しかったのかな。
京急ミュージアムは、週末ということもあってか、まあまあ混んでいた。
今まで何度も来ているし、シミュレーターも予約なしでは体験できないので、うちの子にとっては、もうあまりやることがないようだった。
結局、割と早めに切り上げて帰ることにした。
帰ろうとしたところ、子どもから「相鉄線に乗って帰りたい」と意外な提案があった。
確かに相鉄線でも帰れるが、乗り換えが必要だし、東海道線よりも時間がかかる。
そのことを説明したが、それでもいいらしい。
私も相鉄線には乗ったことがなかったので、少し気になっていたところだった。
改札に入りながら時間を確認すると、ちょうど海老名行きの急行に間に合いそうだった。
電車に乗ると、子どもはさっそく運転席を探し始めた。
何とか一番端の車両まで行ったが、そこからは車掌さんの席が見えた。また反対方向へ移動し、ようやく運転席が見えたところで電車が出発した。
車内が空いていてよかった。
初めて乗る電車だし、運転席が見えるように抱っこしてあげたが、さすがに私も体力の限界だった。
幸い、座席が空いていたので、座って休みながら抱っこすることにした。
どうやら子どもも疲れてきたようで、「ママに秦野まで迎えに来てほしい」と言い出した。
最初はバスに乗って帰るつもりだったが、私も疲れていたので、妻に迎えに来てもらうことにした。
今回のお出かけで、子どもはみなとみらい線の電車が大変気に入ったらしい。
踊り子に乗ることが目的だったはずなのに、最後には別の電車がお気に入りになったようだ。
10月は鉄道関連のイベントが多いので、色々と調べていたところ、昨年に続いて今年も大雄山駅で体験型イベントが開催されるらしいことが分かった。
終点まで行くことになるので、少し遠くはあるが、普段はあまり乗らない大雄山線に乗れるし、ちょっとしたお出かけにも良さそう。
子供も運転士体験ができるので、数日前から楽しみにしていた。
3連休の最終日で、連日のお出かけによる疲れも残っていたが、最後まで頑張る覚悟で出かけることにした。
初めて参加するイベントだったので、どれくらい混雑するのか心配だった。
おそらく昼食後に行った方が空いている気もしたが、調べてみると大雄山駅周辺には子供と一緒に食事ができそうな場所が少なそうだった。そのため、どうしても小田原で昼食を取ってから向かうプランになる。
結局、頑張って早めに出発することにした。
小田原から大雄山線に乗り換えたところ、何人かの子供が目に入った。みんな手元にチケットのようなものを持っていて、「小田原駅で何か配っていたのかな?」と少し焦った。
ただ、事前の告知には特に案内がなかったので、とりあえずそのまま向かうことにした。
電車の運転席は子供にとってとても見やすい位置にあり、乗っている間、ずっと前方を眺めていた。
大雄山駅に着くと、降りる人の流れに乗って、そのまま洗車体験の電車に乗り込んだ。ちょうど始まる頃だったので、タイミングが良かったかもしれない。
洗車体験が終わり、次にどこへ行こうか考えていると、子供が「お土産を買いたい」と言い始めた。
これまでの経験から、並んでいるときなども、手元に好きなものがあれば飽きずに過ごしてくれるので、お土産を買うのも良いかもしれない。
お土産売り場には行列ができていて、なかなか商品を選べなかったが、予想どおり、ロマンスカーミュージアムでもよく買っている列車のバームクーヘンを選んでいた。
その後は敷地内で色々な体験を楽しみ、最後に運転士体験をしてから、帰りの電車に乗った。
帰りの電車には、行きの電車で仲良くなった子供とそのお母さんが偶然乗っていた。帰りは一緒に遊びながら、楽しく電車で過ごすことができた。
小田原に戻ってもまだ時間があったので、久しぶりに新幹線を見に行った。
Stripe Tour Tokyo 2026に参加してきました。
始まりは、AWS Summitの終わりにふらっと立ち寄ったStripeのブースでした。
仕事で前から気になっていたことをいくつか聞いてみたのですが、返ってきたのは「担当者に聞かないと分からないですね」という答え。そして「Stripe Tour Tokyoに来ていただければ、担当者と直接話せますよ」と勧められました。
実は昨年も行こうとしていたのですが、時間が取れずに断念していました。今年こそは、と思って参加を決めました。
早く行くと朝食がもらえる、という情報を事前に聞いていました。
ただ、自宅から会場までは距離があるので、早朝に着くのはなかなか大変です。それでも「時間ギリギリには間に合うかな」という電車で出発したところ……乗る電車を間違えました。
思いもよらないタイムロス。遠方から都心のイベントに行くたびに、乗り換えの難しさを感じます。
幸い、告知された時間を過ぎても、基調講演が始まるまで朝食は出し続けてくれていました。ありがたい。
トルティーヤとおにぎりが並んでいるのが、グローバル企業の日本開催らしくて面白かったです。
会場は天井の高い、いわゆる高級ホテル。こういう場所に来るのは久しぶりです。海外の会社のイベントなので、外国の方も多く、少し海外カンファレンスのような雰囲気がありました。
そして正直に書くと、セッションにはあまり参加せず、ほとんど普通に仕事をしていました。
平日開催なので仕事もMTGも普通に入っていますし、Wi-Fiは会場で提供されていて接続も安定していました。席にも余裕があり、作業場所には困りませんでした。
一方で、気になった点もあります。
次回はモバイルバッテリーと、できればノイズキャンセリングイヤホンを持っていこうと思います。
昼食はお弁当が配られました。何種類かある中から、迷わずすき焼き弁当を選びました。イベントのお弁当としてはかなり満足度が高かったです。
今回の一番の目的は、エンジニアブースでした。
とりあえずサブスクリプション関連のブースに行ったところ、「それならこちらへ」とAPI(開発者)ブースに案内してもらえました。対応してくれたのは、海外出身らしいエンジニアの方と日本人エンジニアの方の2人。
APIのアップグレードや、Webhookを処理するときのレートリミットなど、ずっとモヤモヤしていたことを直接聞けました。「結局こちらで工夫するしかないのか」という答えもありましたが、公式の人から直接聞いて納得できることにはやはり価値があります。
あと、英語にはなりますが、StripeにはDiscordのコミュニティがあって、リアルタイムで質問できるそうです。これは知らなかった。
👉 https://discord.com/invite/stripe
せっかく担当者がいるので、会場から社内のチャットで「Stripeに聞きたいことはありませんか?」と呼びかけてみました。
すると、以前Stripeを使ったプロジェクトのメンバーから、こんな悩みが返ってきました。
最初は質問が抽象的だったので、「なぜその質問をしようと思ったのか(何に困ったのか)」を聞き直してから、サポート担当のブースへ持っていきました。具体的な背景が分かると、相手も答えやすくなるのを改めて実感しました。
担当の方から聞けた話をまとめると、こんな感じです。
「メールよりサポートケース」という話は、Stripeを使っている人にはかなり役立つ情報だと思います。
ちなみに、別件の相談でも担当の方と長く話し込んだので、すっかり顔見知りになりました。こういう関係ができるのも、オフラインイベントの良さですね。
帰り際にはお土産が配られていて、Tシャツかタオルを選べました。
もちろんタオルを選びました。Tシャツはもう家に十分あるので、実用性で選ぶとタオル一択です。
振り返ると、平日参加ということもあって、内容は「いつもの仕事を場所を変えてやった」に近い一日でした。
でも、それが逆に良かったと思います。
「仕事環境が充実している」こと自体が、このイベントの大きな魅力だと感じました。人混みや長時間のイベントが苦手な自分でも、自分のペースで過ごせたのは大きいです。
来年もまた行きたいと思います。次は電車を間違えずに、朝食の時間に間に合うように。
2026年9月26日(土)に中野で開催された Platform Engineering Kaigi 2026 に参加してきました。全体の感想と、聴講したセッションのまとめです。
登壇:Kaspar von Grünberg 氏(『Thinking in Platforms』著者)|セッションページ
要点:モデルの競争ではなく、プラットフォームの競争
/pr-review(読み取り専用でリスクがほぼない)→ /validate-change(ハイブリッド)の順に、2〜3人の小チームで磨く。12:15-12:45 Room A|登壇:小野 大器 氏(enechain)|セッションページ
要点:認知負荷は消えず、プラットフォームへ移る。それでも引き受ける価値がある
13:00-13:30 Hall|登壇:木嶋 幸子 氏(レッドハット)|セッションページ
要点:AI Slop は「低品質」ではなく、受け手の期待値とのずれ
成果物の4象限マトリクス
| 目的への貢献度 \ 受け手の負荷 | 負荷:低 | 負荷:高 |
|---|---|---|
| 貢献度:高 | 緑ゾーン(目標) 目的に効き、受け手も楽 |
グレー 良いものでも期待値がずれると AI Slop 扱いされる |
| 貢献度:低 | グレー 楽だが目的に効かない |
グレー 効かないうえに負荷も高い |
13:45-14:15 Room A|登壇:ハオ 氏(テナブルネットワークセキュリティジャパン)|セッションページ
要点:静的スキャンの緑チェック ≠ 安全
RoleLastUsed で検出し、Claude でコード修正の PR を作る形でも実現できそうです。15:15-15:45 Hall|登壇:松岡 雄地 氏、小西 杏典 氏(AWS ジャパン)|セッションページ
要点:AI エージェントに専門知識を持たせて、ゴールデンパスをスケールさせる
仕事でぜひ活用してみたいです。次のコマンドで導入できるらしいです。
npx apex-skills
16:45-17:15 Hall|登壇:山口 能迪 氏(Grafana Labs)|セッションページ
要点:「人の判断から仕組みへ」
登壇:鈴木 勝史 氏(スリーシェイク)|セッションページ
要点:境界は「機能」と「役割」の2つ。両者は連動するので継続的に引き直す
ignore_changes の漏れ、本当の原因は「イメージを誰が管理するか」を決めていなかったこと。※ 各セッションの内容は、当日の録音を文字起こし・要約したものです。聞き取りの誤りや解釈を含む場合があります。
モバイルアプリのアイテム登録機能に、CloudFront Signed Cookie を使った画像配信基盤を導入した話。設計と実装、そして途中で踏んだ3つの地雷について書きます。
モバイルアプリでは、アイテムを写真つきで登録すると、ポイントや保証の管理に使えるようになっている。今回、この「アイテム写真」と「購入証明書の写真」を、これまでのような単純な公開URLではなく、本人だけがアクセスできる形で配信する基盤を作り直しました。この記事では、その設計と、実装中に遭遇した細かいけれど厄介な問題についてまとめます。
これまで、アイテム画像はクライアントが用意した URL 文字列をそのまま DB に保存するだけの、ごく単純な仕組みでした。しかしアプリの機能拡張にともない、次の要件が持ち上がりました。
「本人限定」と「CDN配信」を両立させる方法として選んだのが、CloudFront の Signed Cookie です。署名付きURLだと画像1枚ごとに個別の署名が要りますが、Signed Cookie ならログイン時に一度発行しておけば、あとはブラウザ(アプリ)が自動的にCookieを送ってくれるので、モバイル側の実装がシンプルになります。
全体は「アップロード」と「配信」の2つの流れに分かれます。どちらも同じ非公開S3バケットとRailsアプリサーバーを経由しますが、認証の仕組みが異なります。
ポイントは、Signed Cookie の検証ロジックはアプリケーションコードのどこにも存在しないということです。Railsが担うのはCookieを「正しく発行する」ことだけで、検証はすべてCloudFrontのエッジ側で完結します。裏を返すと、発行時にちょっとでも間違った値を作ってしまうと、それを検出できるのは実際にCloudFrontにリクエストが飛んだ瞬間だけ、ということでもあります。これが後述するハマりどころに直結します。
Signed Cookie は次の2つのタイミングで発行・再発行します。
有効期限は10年という、一見雑に見える値を設定しています。アプリ側のログイントークン自体に有効期限の概念が無いため、それに合わせて「実質失効しない」十分に長い値にした、という判断です。
アップロードAPIは、これまでの「クライアントがURL文字列を送る」方式から、「クライアントが画像バイナリそのものを multipart で送る」方式に変更しました。サーバー側は受け取った画像を検証してS3に保存し、DBには完全なURLではなくS3キーだけを保持します。レスポンスを組み立てる際に、このS3キーをCloudFront配信URLへ変換します。
users/{user_id}/items/{item_id}/case/{timestamp}-{random}.jpg
users/{user_id}/items/{item_id}/purchase_certificate/{timestamp}-{random}.jpg
DBに完全なURLではなくキーだけを持たせたのは、配信ドメインを後から変更しやすくするためです。実際、開発環境と本番環境で配信ドメインが異なるため、この一段のクッションが効いています。
設計自体は素直でしたが、実装の過程で3つ、地味に厄介な問題にぶつかりました。いずれも「ローカル開発環境には本物のCloudFrontが無い」ことが発見を遅らせた共通点です。
問題Signed Cookieには「既定ポリシー(canned policy)」と「カスタムポリシー(custom policy)」の2種類があります。既定ポリシーはCookieに有効期限しか含まず、CloudFront側が実際にリクエストされたURLからポリシーを再構築して検証します。今回のように users/{user_id}/* というワイルドカードを含むリソースを許可したい場合、既定ポリシーでは検証時に組み立て直したURLと署名対象が一致せず、問答無用で403になります。
気づいたきっかけローカル開発環境には実物のCloudFrontが存在しないため、この不具合はテストでは一切表面化しませんでした。実際にAWS SDKのCookie発行ロジックを手元で動かし、生成された署名を実際に検証してみて初めて気づく類の問題です。
解決ポリシーJSONを自前で組み立てて明示的に渡す、カスタムポリシー方式に切り替えました。発行されるCookie名も変わる(CloudFront-Expires → CloudFront-Policy)ため、関連するテストやドキュメントもあわせて追随させています。
問題CloudFrontの署名付きの値は、通常のBase64ではなく+ = / をそれぞれ - _ ~ に置き換えた独自形式です。ところがRailsのCookie機構は、値をSet-Cookieヘッダーに書き出す際、内部でURLエンコード処理を通しており、この~が%7Eに変換されてしまっていました。
Signature=abc~def~ghi
Signature=abc%7Edef%7Eghi
ブラウザ(アプリ)はSet-Cookieの値をそのままのバイト列として保存・送信するだけなので、%7Eは~には戻りません。CloudFront側はこの3文字を「1文字の~」として解釈できず、署名データの一部を読み違えて検証に失敗します。
気づいたきっかけログインもアイテム登録も普通に成功し、返ってくる画像URLも一見正しく見えるため、切り分けが非常に難しい壊れ方でした。実際にレスポンスの生ヘッダーを見て、値の中に%7Eが複数含まれていることに気づいたのがきっかけです。
解決RailsのCookie機構(フレームワークによる自動エスケープ)を経由せず、Set-Cookieヘッダーの文字列を自前で組み立てて直接レスポンスに載せるように変更しました。値自体はエスケープが不要な文字種(英数字と- _ ~のみ)だったため、素通しで問題ありません。
問題購入証明書登録APIでは、画像とあわせてAI判定結果(金額や店舗名などを含むJSONオブジェクト)も送る必要があります。しかし multipart/form-data は「名前=文字列」しか運べないため、ネストしたオブジェクトを送ろうとすると、数値が文字列になり、nullが空文字になり、空配列がキーごと消える、という劣化が起こります。
ai_result[price]=1200 ai_result[dates][]=
ai_judgment_result=
{"price":1200,"dates":[]}
解決ネストしたオブジェクトをブラケット記法に分解するのではなく、JSON文字列化した1つのフィールドとして送ってもらい、サーバー側でパースする方式に統一しました。型情報を保ったまま運べるうえ、クライアント側の実装もシンプルになります。
今回いちばん学びが大きかったのは、「検証ロジックがアプリの外(CDNのエッジ)にある」仕組みは、ローカル環境だけでは正しさを保証できないということです。3つのハマりどころのうち2つは、実際にAWSのSDKを手元で動かして署名やヘッダーを1バイトずつ確認するまで発覚しませんでした。テストが通っていても、境界の外側で何が起きているかは別の話だと痛感しました。
一方で、Signed Cookieという仕組み自体は、いったん正しく発行できてしまえばアプリ側の実装は驚くほどシンプルです。画像1枚ごとに署名付きURLを発行する必要も、モバイル側で特別なヘッダーを付け回す必要もありません。ログインという1点さえ正しく作り込めば、あとは普段どおりの画像読み込みで「本人限定配信」が実現できる。地味ですが、良い設計だったと思っています。
この基盤は現在、社内のモバイルアプリチームと連携しながら実際のクライアント実装を進めているところです。実機での検証がまとまり次第、続報を書きたいと思います。
■発端
加齢のせいか、最近、眠りが浅くなってきた気がする。
色々調べてみたところ、世の中には睡眠の質を計測するアプリがあることを知った。
それで、良さそうなものを入れてみたのだが、以下のような難点があった。
・寝る前にアプリを起動する必要がある
・家族と一緒に寝ているせいか、自分には身に覚えのない「いびき」や「咳」が記録されていた
・一応、録音もされていたが、確認するためには課金が必要だった
ちょうどAmazonセールでポイントも貯まっていたので、何かしら睡眠を計測できる装置を買うのも良いかと思うようになった。
調べてみると、睡眠計測専用の装置もあるらしいが、数万円台のものが多く、かなり高い。
一般的なのはスマートウォッチで、安いものなら1万円未満のものもあった。
ただ、別に画面を見ながら操作をしたいわけでもないし、中国製の端末を身につけて、自分の個人データを計測することには少し抵抗がある。
そこで、最終的にたどり着いたのが「スマートバンド」というものだった。
■購入
初めはスマートリングも考慮した。
ただ、これもまあまあ高かったし、そもそも普段から指輪を使う習慣がない。
あまり指輪をつけたくないし、個人的には見栄えもあまり好みではない。
スマートバンドで調べると、GoogleとAmazfitあたりに絞られた。
サイズ感などはGoogleのものが良さそうだったのだが、最近発売されたばかりということもあってか、どこも品薄で売り切れ状態。
しかも、計測自体はできるものの、計測値を評価するためには課金が必要になるようだった。
一方、AmazfitはちょうどAmazonセールで10%ほど安くなっていた。
しかも、今まで貯めていたAmazonポイントで全額支払える。
ということで、Amazfit HELIO STRAPを購入した。
■Amazfit HELIO STRAP しばらく使ってみての感想
注文してからは、結構早めに発送された。
・今まで使っていた睡眠測定アプリと並行して使ってみた。寝る前にアプリを立ち上げなくても済むのは助かる。ただし、周りの音を拾う機能がないため、以前記録されていた咳・いびきについては、結局謎のままになってしまっている
・腕に何かを付けて寝たことがないので、これを付けたまま寝られるかが心配だったが、意外と普通に寝られた。ただし、ある程度しっかり手首につけておかないと、起きたときに緩くなっていた。今度、余裕があったら大きめのバンドを買って、足に付けても良いかもしれない
・HYBRIDCHARGEで総合的な「元気度」のようなものが分かるのは助かる。数値をどこまで信頼して良いのかは分からないが、自分が客観的にどのくらい疲れているのかが見えるのは面白い
・モデル名に「FIT」がついていることもあり、運動項目が充実している。普段、あまり運動していない自分でも、これを見ていると運動をして数値化したい気持ちにさせられる。運動する前に何の運動をするか選ぶようになっているが、残念ながら弓道はなかった。代わりにアーチェリーを選択している
・運動項目にスイミングもあるので、おそらく防水性能もあるのだと思う。ただ、高い機器を水の中に入れるのはやはり怖い。一応、旅行中にプールで使ってみたが、今のところは大丈夫そう
・一応、課金メニューは存在するが、確かに使わなくても特に支障はなさそう。睡眠健康評価は少し気になるので、どこかで一度、有効化してみるかもしれない
ちなみに、買ったタイミングで一番最初に気づいたのは子どもだった。
その後は旅行だったこともあり、ずっと付けていたが、誰も特に気づかなかった。
そういえば、着替えるときに妻が気づいていたような……。