バンドルカードと Pool のカードが 3D セキュアに対応しました

バンドルカードの SRE をしている summerwind です。最近は A Philosophy of Software Design を読んでいます。

タイトルの通り、2022年6月21日からバンドルカードPool のカードが 3D セキュアに対応しました。バンドルカードではアプリですぐに発行可能なバーチャルカードを含む全てのカードで対応しているので、気軽により多くの加盟店での決済にご利用いただけるようになりました。

いつもはバンドルカードのインフラやセキュリティといった領域を担当しているのですが、3D セキュアの対応では久しぶりにバックエンドエンジニアとして自分もプロダクト開発に関わったので、今回は 3D セキュアの仕組みとその開発に関する話を簡単に紹介したいと思います。

3D セキュアとは

3D セキュアは、オンラインなど非対面でクレジットカードを使用して決済をする際に、カードの所有者であることを事前に認証する仕組みです。3D セキュアを使用することで、カード情報の盗用によるオンライン上での不正利用を防止できます。ショッピングサイトなどでカード決済をする際に突然パスワードの入力を求められた、といった経験がある方も多いかもしれません。あれが 3D セキュアによる認証です。

3D というのは三次元のことではなく3つのドメイン (Domain) すなわち、加盟店/アクワイアラ、決済ネットワーク、イシュアの3つの立場を指しており、以下の図のようにそれぞれの立場にあるシステムが連携して本人認証をする仕組みになっています。カンムのようにカード発行をするイシュアの立場では Access Control Server (ACS) と呼ばれるシステムを用意することで 3D セキュアによる認証に対応できます。

3D セキュアの認証フロー

3D セキュアの最新の仕様は「EMV 3-D Secure」と呼ばれており、EMVCo の公式サイトで参照できるので、興味がある方はぜひ参照してみてください。

3D セキュアの認証フロー

EMV 3-D Secure の仕様では「Frictionless Flow」と「Challenge Flow」の2つの認証フローが定義されています。それぞれの認証フローについて簡単に説明します。

Frictionless Flow

従来の 3D セキュアの仕組みでは、本人認証時のパスワードの手間が大きく認証時に決済を諦めてしまう人が多いという課題がありました。この問題を軽減するため、EMV 3-D Secure では決済時の情報に基づくリスクベースの判断をして、パスワード入力などの認証ステップなしに認証を完了させるフローに対応しています。これが Frictionless Flow です。

カンムのようなイシュアの立場では、ACS で加盟店/アクワイアラから送られてきた決済の情報などを元にリスクを分析し、追加の認証は不要と判断した場合は認証を完了させる動きをとることになります。

Challenge Flow

Frictionless Flow を実行した結果、ACS が追加の認証が必要と判断した場合は Challenge Flow に移行します。

Challenge Flow では決済をしようとしているユーザーが本人であるかを確認する認証フローを実行します。認証フローではまずイシュアが持つカードの発行情報に基づいて、カードの所有者にワンタイムパスワードを送信します。次にカードの所有者はイシュアから送信されてきたワンタイムパスワードを決済画面に入力します。ワンタイムパスワードの一致が確認できたら認証は成功です。

バンドルカードでは Challenge Flow の場合に次のような認証画面を表示しています。

Challenge Flow の認証画面

バンドルカードと Pool における ACS

バンドルカードと Pool で 3D セキュアに対応するには、イシュアの立場として ACS を実装して用意する必要があります。しかし ACSフルスクラッチで実装するには多くの時間とコストがかかるため、今回の対応では Visa 様が提供する Visa Consumer Authentication Service (VCAS)ACS として導入しています。

VCAS では ACS としての機能が一通り提供されており、3D セキュアの認証フローの各種処理に必要となる情報を専用の API を介してイシュアから VCAS に提供することで、ACS を実装せずとも 3D セキュアに対応できる仕組みになっています。

VCAS を ACS として使用した認証フロー

VCAS の導入とその開発

VCAS の導入にあたっては、バンドルカードと Pool のそれぞれで VCAS と接続するための仕組みを開発する必要がありました。ここからは開発の流れなどを簡単にふりかえって気をつけた点などを紹介してみたいと思います。

前提の確認と方針決め

まず、今回の VCAS の導入では次のような前提や制約がありました。

  • Visa 様との共同プロジェクトによる開発
  • ローンチまでのスケジュールが比較的がっちりと決まっている
  • バンドルカードと Pool で同時に VCAS に対応する必要がある
  • 可能な限り早くユーザーが 3D セキュアを利用できるようにしたい

これらの前提と制約を受け入れつつスムーズに開発を進めるために、ある程度細かい部分までの仕様は先に決めて関係者の間で合意をとりつつ、実装はバンドルカードと Pool で順番に細かく進め、得られた知見をそれぞれのプロダクトの実装に相互にフィードバックしながら品質を高めていく方針としました。

設計と社内レビュー

制約と方針に基づいて、まずは設計を固めることとしましたが、この段階では「バンドルカードと Pool で同時に VCAS に対応する」という制約が最初の課題になりました。これは単純に Pool が新しいプロダクトであり、今まで2つのプロダクトで同時に同じ対応をする、という動きがこれまでの社内では無かったためです。そこでまずはバンドルカードを社内のリファレンス実装として先に実装することに決め、そこで得られた知見をもとに Pool での実装を進めることにしました。

次に「VCAS とは何か」「その対応のための要件と制約は何か」「そして2つのプロダクトで同時に対応するにはどういった構成パターンが考えられるか」「バンドルカードではどういった改修が必要か」といった情報を整理してドキュメントにまとめ、社内で「ぶつかり」と呼ばれているレビュー会を開催して設計を確定させることにしました。

設計ドキュメント

事前にいくつかの構成案の良し悪しや、個人的に迷っているポイントなどをドキュメントにまとめておいたおかげで、「ぶつかり」では各構成案に対する意見がスムーズに集約できました。また既存のシステムの設計思想の共有や今回の開発においてはその思想がどう適用しうるかといった議論もでき、純粋によい学びの時間になったなと感じています。

役割の分担

「ぶつかり」により設計がうまく確定できたので次のステップである実装に入っていくわけですが、開発規模がそこそこ大きくなってきたのでここでエンジニア間の役割分担をすることにしました。

3D セキュアに対応するには VCAS 向けの開発とは別に Processor と呼ばれるカード決済を処理するシステムも改修をして、Visa 様による事前の仕様確認テストをパスする必要がありました。Processor についてもバンドルカードと Pool のそれぞれで改修が必要だったため、社内の Processor の匠である hiroakis にその対応をお願いしました。

また、リファレンス実装として最初に実装をするバンドルカードの内部はいくつかのコンポーネントに分かれており、コンポーネントごとに改修が必要だったため、直近でゴリゴリにバンドルカードの改善を進めてくれている Shiba にも実装の手伝いをお願いして、最終的にバックエンドエンジニアは3人体制での開発となりました。

実装とフィードバックの反映

役割分担ができたので、それぞれの担当領域で実装を進めていきます。自分は 3D セキュアによる認証時に VCAS と連携する API をまずバンドルカード向けに実装しました。

設計と「ぶつかり」によりコードレビュー時に必要となる土台の知識は共有できていると感じていたので、ここからは比較的見やすい単位でコードレビューを進めていけるような流れを作ることにしました。具体的には次のような単位で順番に Pull Request を作成して実装とコードレビューを進めていきました。

  1. ただ起動するだけの API サーバーの実装
  2. VCAS と連携に必要な API エンドポイントを API サーバーに追加 (各エンドポイント単位で数回繰り返し)
  3. バグ対応やリファクタリングのための修正など

バンドルカードの実装がある程度の形になって VCAS と接続しての結合テストが可能な状態になった後は、並行して Pool 用 API の実装も同様の流れで進めました。

何かを新しく実装する時は何回か実装を繰り返すとより良いコードが見えてくる...といったことがよくあるかと思いますが、今回は幸いにも同じような API を2回実装する機会ができたので、Pool 用の API の実装時にはバンドルカード用の実装の課題をうまく改善する試みができました。また Pool 用の実装でうまくいった点は逆にバンドルカード用の実装にフィードバックする、といったことができたのもよかったなと感じています。

テストの実施

バンドルカードと Pool のそれぞれの実装が形になったあとは Visa 様の管理するシステムと接続しての結合テストを実施します。結合テストは以下の2つを観点でそれぞれ独立して実施されました。

  • VCAS とバンドルカードおよび Pool の API が連携して 3D セキュアの認証が正しく動作するか
  • 決済ネットワークである VisaNet とバンドルカードおよび Pool の Processor が 3D セキュア認証後のオーソリを正しく処理できるか

それぞれの結合テストではレスポンスにおける誤った値の指定や処理漏れなどの不具合が見つかりましたが、幸いにも軽微な修正で対応が可能なもので比較的スムーズにテストを終えることができました。

結合テストが完了すると、その後は本番環境の構築とリリース前の最終的なテストを経て、無事に機能リリースとなりました。

おわりに

今回は 3D セキュアの仕組みとその対応にあたっての開発の流れなどを簡単に紹介してみました。個人的には 3D セキュア対応により多くのユーザーの決済機会を増やすことができたのを嬉しいと思うと同時に、久々のプロダクト開発も楽しめてとてもよい経験になりました。

バンドルカードでは 3D セキュアへの対応をはじめとして、今後も自分の持っている価値をどこでも自由に交換出来るようにする「価値交換」、自分の持つ価値をより良く制御できるようサポートする「価値制御」、自分が持っている価値を未来まで拡張する「未来価値」の3つの領域にフォーカスしてプロダクトの改善を進めていく予定です。これらの3つの領域の詳細については、ぜひ COO である achiku が書いた以下のブログ記事もあわせて参照してみてください。また、先日新たに提供を開始した Pool でも資産形成を軸に様々な価値を提供していきます。

akirachiku.com

カンムではこれらのプロダクトの改善に加わってくれるメンバーを募集中です。ブログ記事の内容を深掘りするだけのカジュアル面談も大歓迎ですので、ぜひお声がけください。

kanmu.co.jp

バンドルカードの本人確認改善の取り組み

デザイナーの@torimizunoです。

この記事では、バンドルカードでの本人確認改善の取り組みについて、プロジェクトチームの活動の一部をご紹介します。

バンドルカードの本人確認とは

バンドルカードの本人確認
バンドルカードの本人確認

バンドルカードのバーチャルカードは本人確認不要で利用を開始できますが、リアル+カードを発行する場合は利用上限額が上がるため、本人確認手続きが必要になります。 本人確認手続きの詳細はお伝えできないのですが、手続きの一部として、本人確認書類と撮影した本人確認書類と本人情報をご提出いただき、本人であるかの確認を行います。(以降、「本人確認」と呼びます)

確認及び一定の審査が完了すると、カードの発行を行い、お客さまのもとへカードが送られます。 本人確認ができなかった場合は再度申請をお願いすることになり、お客さまのもとへカードが届くのにお時間がかかってしまいます。

本人確認でき発行へ進めたことを承認率と定義し、カスタマーサポートチームが日々状況を計測していました。 2021年5月にこの承認率が過去最低値を記録したことから、改善に注力したプロジェクトが発足し、私はデザイナーとして参加しました。

プロジェクトメンバー
取り組みの流れ
  1. 目的と目標の認識あわせ
  2. 一次情報による原因調査
  3. 課題と仮説立て
  4. イデアから検討
  5. プロトタイピングと実装とリリース
  6. 効果を振り返る
  7. 振り返りから次の打ち手を再検討し実施(2〜6を繰り返し)

目的と目標の認識あわせ

本人確認の承認率には、最初の申請で本人確認ができた初回承認率と、何度か再申請を重ねて本人確認ができた最終的な承認率と、ふたつの定義がありました。

初回の承認率が上がれば、最短でお客さまにリアル+カードが届くことにつながり、その後の承認率向上にもなるため、初回の承認率に的を絞ることを決めました。 過去の最高値を超える値を平均としていくために、過去の最高値を目標値として目指すとしました。

一次情報による原因調査

何が原因でお客さまは本人確認ができなかったのか、住所の間違いや氏名の間違いなど、理由は分類して計測できるしくみが既にできていました。

原因の割合でそれまでも傾向は見ていましたが、プロジェクトメンバーによって一次情報まで確認できている人とそうでない人で課題感の差がありました。 そこでひとりひとりが多角的に課題を知るために、プロジェクトメンバー全員が数百件ずつ申請情報を見に行き、本人確認できなかった理由を抽出していきました。

例えば、本人確認書類の厚みは「表面の厚みや内容が識別できる」を判断基準で真正性を確認していますが、本人確認ができないものとしては下記のような原因が抽出されました。

  • 撮影した書類の全体像が写っていない
  • 書類がぼやけて本人情報が読めない
  • 暗すぎる、反射で本人情報が読めない
  • 厚みが見えない
  • 厚みの角度が急すぎて本人確認情報が見えない
  • 書類の裏面の厚みを撮影してしまっている

本人確認書類の厚みの判断基準
本人確認書類の厚みの判断基準

また、自分たちで一次情報を確認していると「この申請は自分が本人確認ルールに則ってチェックすると承認だと思うけど、非承認になっている」と感じる内容があることにも気が付きました。

課題と仮説立て

本人確認できなかった原因を見ていくと、申請前 申請中 申請後のフローごとの課題と仮説が見えてきました。

フローごとの課題
フローごとの課題

申請前

そもそもの本人確認書類の情報と、住所や氏名等の現在の情報が合っていない場合が見られました。本人確認の趣旨が伝わっていない、住所確認でなく送付先住所と思われている等の仮説があげられそうです。

申請中

書類の撮影が上手くできなかったり、入力する情報を間違えてしまうなどの課題が見受けられました。撮影した書類の情報が他者にとって読み取れる必要があることが伝わっていない、書類の厚みの必要性が伝わっていない、などの仮説があげられそうです。

申請後

本人確認のチェックをしている方たちが、人によって申請内容の承認にぶれがあることが見えてきました。判断に悩む要因を一律本人確認できないとしている可能性がありそうです。

ここから各課題の割合と影響人数を出し、インパクトのシミュレーションを作成し、初回承認率の上昇に影響のある大きさの課題から取り組む優先度を検討していきました。

イデアから検討

課題によって、プロダクト側から申請前・申請中にできるアプローチと、確認オペレーションで申請後に解決できそうなものと両軸が考えられそうなため、平行して検討と実施を進めることにしました。

オペレーションについては カスタマーサポート・不正検知・コンプライアンスのメンバーを中心として、本人確認を実施しているメンバーが判断に迷ったものはプロジェクトメンバーにエスカレーションしてもらう流れを一時的に行いました。 そこでプロジェクトメンバーがチェックを行い、迷う判断基準を言語化し、確認マニュアルに落とし込んでいきます。

プロダクト方面からのアプローチは、Googleスプリントの一部のプロセスを採用し、アイデアだしはクレイジー8を活用しながら実施していきました。

クレイジー8とは

  • 課題解決の参考になる情報を集め案を考える(手書きでOK)…(10分)
  • その時点での案をひとり3分ずつプレゼンする…(3分✕人数)
  • ほかのメンバーのプレゼンを聞いたあと、アイデアを「ひとりで」練り、8つのマスに各1分で書く(8分)
  • 各メンバーのソリューションを匿名で批評・検討し、ベストを決める(20分)

カンムではmeetを利用しているので、ワークショップはMTG中に作成できるホワイトボード機能を利用して実施しました。 私以外のメンバーはクレイジー8をするのは始めてだったのですが、ワークショップ中にどんどんアイデアを書き起こしていってくれて、すごいなと感じました。

クレイジー8で出たアイデア例
クレイジー8で出たアイデア

まずは厚み・生年月日・氏名を取り組むことを決めたので、各課題について毎週のようにアイデアだしを行い、最終的にはどのアイデアが最も初回の本人確認の承認率を上昇させそうかを軸に投票し、ベストなアイデアを絞り込みつつ考慮すべき点もメンバーで洗い出していきました。

プロトタイピングと実装とリリース

イデアを絞り込んだ後は、ひたすらプロトタイプを作成してはチームメンバーで操作してブラッシュアップをしていきました。 例えば申請中の厚みの課題に対して、下記のような施策を実施しました。

①撮影前に注意ポイント画面を挟む 厚みは表面の撮影が必要なこと、厚みと情報が読める必要がある点を伝わりやすくする

撮影前
撮影前

②撮影時のガイドを読みとりやすいものへ 後ろ倒しの斜めから、手前斜め45度のガイドに変更して確認している本人情報が見えやすい角度での撮影を促す

撮影時
撮影時

③撮影後の確認画面の調整 厚み撮影後にチェックする箇所を確認する体験を挟み、注意点に気付けるようにする

撮影後
撮影後

ユーザーに表示される画面が増え完了までその分お時間が発生してしまうのですが、撮影で気をつけるポイントがわからず、撮影が上手くいかず申請後にやり直しが発生するほうがユーザーにとってもサービスにとってもデメリットが大きいと判断し、撮影前と撮影後のガイドを充実させました。 他にも、細やかにライティングの調整等を実施しています。

効果を振り返る

各アイデアを順にリリースしていき、それぞれで効果の振り返りをしていきます。 施策によっては仮説していた想定効果がでないものもあり、新たな仮説を立て直し次の施策へと回していきました。

仮説に対して効果があったものでいえば、例にもあげていた申請中の厚みに対する施策は効果が見られました。 リリース前とリリース後で厚みが原因の割合がどう変化したのか調査したところ、一時期は本人確認できなかった原因の平均4割を占めていましたが、施策以降は最小1割、平均2割以下にさがっていきました。

本人確認できなかった厚みの原因の100%のうち、改善前は「読み取れない&角度が急で読めない」が55%を占めていましたが、施策後は18%になり37%の減少が見られました。 裏面の厚みを撮影してしまう27%を占めていましたが、17%の割合に減少しました。

厚みが原因の内訳の変化
厚みが原因の内訳の変化

振り返りから次の打ち手を再検討し実施

振り返りでの分析で、数年前に申請して久しぶりに再申請を試み、本人確認できないユーザーが半数くらいいることもわかりました。 これに関しては、現状申請時にどの書類で申請したか情報を保存できておらず、一度でも再申請が発生すると、初回のガイドつき申請UIでなくガイドなしUIになってしまっている課題があるため、申請時に書類の情報の取得から順次取り組みをはじめています。

振り返りを重ねながら継続的に施策を実施した結果、2022年1月に初回承認率は目標値を達成し、その後数ヶ月安定して経過しています。

撮影時の厚みの課題以外に、入力時の間違いを減らす施策に関しても施策を繰り返しているため、また別の記事としてお伝えしていきたいです。

今回のプロジェクトから得た学び

実際に申請された情報をひとつひとつチームメンバーが見にいくことで、課題抽出の解像度があがりました。 それがメンバー間での意見活性化や、仮説とアイデアの立てやすさに繋がった感覚があります。 プロジェクトメンバー全員が一次情報をきちんと見ようとした意識で自然と動いていったのは、カンムの「事実と向き合う」文化がにじみ出ているのではないかと思います。

施策によっては効果がなかったものもありますが、実施したことでこの仮説ではない…という事実がわかったトライ自体に価値があるとカンムに来てから感じています。

一度施策を実施した上で再び一次情報を見にいった際、わかった事実を自分が持っているので、今まで見えてなかった観点の課題が見えるようになった時は学びが自分の中に入っている感覚がありました。その感覚が知れたことが嬉しいです。

引き続き、バンドルカードは「誰もがかんたんにわかる」プロダクトとしての品質を高めるアップデートを続けていきます。

採用リンク

カンムではプロダクトを一緒に磨いてくれる仲間を募集しています。

採用情報 team.kanmu.co.jp

社内イベント: エディタについて語る会で Vim script と ISO8583 の話をしました

エンジニアの佐野です。最近記事を書いていなかったので小ネタです。先日、菅原企画の社内イベント、エディタについて語る会が催されました。職種にもよりますがカンムでは多くの従業員はオンラインで業務を行っています。たまにはオフラインで交流も...ということで来れる人はオフィスに集まってエディタの話をしつつ軽食を楽しむというコンセプトです。

当日は Vim, Emacs, Visual Studio Code, nano... と様々なエディタのゆるい話から熱い話が語られました。私は VimVim script について話したので今日はそれを記事化します。


0. 私とエディタ

私は長らく Vim をエディタとして使っています。「エディタ」というものを意識したのは大学生の頃でしょうか。機械工学系だったのですがソフトウェア工学や C や C++ がカリキュラムにあり自分もそれらを履修しました。それらの演習では Microsoft Visual Studio (Visual C++だったかな...?)やメモ帳を使っていました。それにしてもなぜメモ帳だったのか...。

大学入学は2000年でした。ちょうどその頃に一般家庭にもインターネットが普及し始めました。IT革命という言葉が世間に踊った時期だった気がします。住んでいたアパートにもインターネット回線を導入して私はインターネットにどっぷりハマっていきました。インターネットに触れることでコンピュータに興味が出てきた私は Perl を独学したり PC を自作してサーバ構築の真似事のようなことを始めました。コードを書くときに前述の Visual Studio を使いたかったのですが有償(だったと思う)で手が出しにくい、メモ帳では機能が微妙すぎる、何かないだろうか?と本屋を物色していたら Vi (Vim), Emacs あたりの本を見つけました。どうやら Vi (Vim) というのがシンプルで良いらしいということで Vi を使い始めました。

使い始めた当初は「モードって何?」「なんでバックスペースで削除できないんだ?」という状態でした。普通の人からするとそんなものだと思います。設定ファイルの存在も知らず、毎回起動しては :set nu (行番号を表示する)を打っていました。

サラリーマンになって Java を書いていたときは Eclipse を使っていましたが、それ以外のプログラミング、サーバでのオペレーション、ファイル編集などはずっと Vim でした。これはほとんどのマシンに標準で入っていたというのと、大学からの流れで手に馴染んでいる、というのが理由でしょうか。

その後、新しいエディタやIDEが出る度に乗り換えを試みますが、結局は Vim に戻ってきました。私はロールとして長らくサーバサイドのアプリケーションエンジニアとインフラエンジニアをやっています。サーバに ssh してマシン上で作業したり手元でコードを書いたり...業務中は端末を操作している時間が長いため、多くの作業をターミナルで完結させたくなります。他のエディタを起動して時にターミナルに切り替えたり...というのがちょっとしたストレスになり、結局はVim に(ターミナルに)戻ってきてしまいます。

最近では GoLand や VSCode への乗り換えを試みましたがやはりダメでした。

1. Vim の設定

思い出話が長くなりました。学生の頃から使っていた Vim ですが、初期のころはいろいろなプラグインを入れたり設定をこねくり回したりして盆栽のように設定を育てていました。今はプラグインはだいぶ減って以下のもののみになっています。

これらのプラグインとともにいくつかの設定を施しています。 Vim を知らない人向けに少し書くと、Vim の設定ファイルは .vimrc で基本的な配置位置は $HOME/.vimrc になります。Vim のカスタマイズはこちらのファイルを編集することで行います。私の設定は以下のようになっています。

"=========================================================
" Basic Configuration
"=========================================================

set notitle
set nocompatible        "vi互換をoff
set nobackup            "バックアップファイルを作らない
set noswapfile          "スワップファイルを作らない
set number              "行番号を表示
set laststatus=2        "ステータスを常に表示
set showmode            "モードを表示する
set showcmd             "コマンドを表示
set noshowmatch
set display=uhex        "謎の文字を16進数で表示
set wildmenu            "補間候補を表示する
set wrap                "自動折返しを有効
set expandtab           "タブをスペースに変換
set tabstop=2           "タブをスペース4つ分として表示する
set shiftwidth=4        "シフトで移動する文字幅
set softtabstop=2       "タブキーを押したときに挿入する半角スペースの数
set noincsearch         "インクリメンタルサーチはしない
set wrapscan            "最後まで検索したら先頭へ戻る
set ignorecase          "大文字小文字無視
set smartcase           "検索文字列に大文字が含まれている場合は区別して検索する
set hlsearch            "検索文字をハイライト
set splitbelow          "新しいウィンドウを下に開く
set splitright          "新しいウィンドウを右に開く
set nocursorline        "カーソルのある行をハイライトしない
set nocursorcolumn      "カーソルのある列をハイライトしない
augroup Cursor
  autocmd WinLeave * setlocal nocursorline "カレントウィンドウから離れたらカーソルハイライトを消す
  highlight ZenkakuSpace cterm=underline ctermfg=lightblue guibg=#666666 "全角スペースを見えるようにする
  autocmd BufNewFile,BufRead * match ZenkakuSpace / /
augroup END

filetype plugin indent on

"=========================================================
" Private
"=========================================================

let mapleader = "\<Space>"
nnoremap <Leader>r :reg<CR>

"=========================================================
" Encode
"=========================================================
"表示するときの文字コード(ターミナルの設定と同じ)
set encoding=utf-8
"保存するときの文字コード
set fileencoding=utf-8
"文字コード自動判別の候補とする文字コード種を列挙
set fileencodings=iso-2022-jp,euc-jp,cp932,utf-8

"=========================================================
" Plugin management
"=========================================================

" プラグインインストールディレクトリ
let s:dein_dir = expand('~/.cache/dein')
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'

" dein.vim がなければ取得
if &runtimepath !~# '/dein.vim'
  if !isdirectory(s:dein_repo_dir)
    execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
  endif
  execute 'set runtimepath^=' . fnamemodify(s:dein_repo_dir, ':p')
endif

if dein#load_state(s:dein_dir)
  call dein#begin(s:dein_dir)

  let g:rc_dir    = expand('~/.vim/rc')
  let s:toml      = g:rc_dir . '/dein.toml'
  let s:lazy_toml = g:rc_dir . '/dein_lazy.toml'

  call dein#load_toml(s:toml,      {'lazy': 0})
  call dein#load_toml(s:lazy_toml, {'lazy': 1})

  call dein#end()
  call dein#save_state()
endif

if dein#check_install()
  call dein#install()
endif

"=========================================================
" Plugin configuration
"=========================================================
" gitgutter
set updatetime=250
let g:gitgutter_max_signs = 500

" goimports
let g:goimports = 1

" vim-quickhl
nmap <Space>m <Plug>(quickhl-manual-this)
xmap <Space>m <Plug>(quickhl-manual-this)
nmap <Space>M <Plug>(quickhl-manual-reset)
xmap <Space>M <Plug>(quickhl-manual-reset)

"=========================================================
" Color
"=========================================================
colorscheme cyberspace
set background=light

" 256色
set t_Co=256
" 背景色
set signcolumn=yes
hi SignColumn ctermbg=black
hi SignColumn guibg=black

"=========================================================
" Popup color
"=========================================================
hi NormalFloat guifg=#ffffff guibg=#191970
hi Pmenu guifg=#ffffff guibg=#191970

"=========================================================
" status line color
"=========================================================
set noshowmode
let g:lightline = { 'colorscheme': 'wombat' }

syntax enable

前半部分に set xxxx という設定が羅列されていますが、プラグインマネージャ dein の箇所には変数 ( let ... )や関数呼び出し ( call ... )、外部コマンドの呼び出し (execute '!git ...)、条件分岐 ( if ... ) が表れたりします。 set xxxx も含めこれらは Vim script と呼ばれます。つまり Vim の設定ファイルやプラグインの正体は Vim script の塊です。

ちなみに Vim script はオブジェクト指向プログラミングもサポートしています。 Vim script は単なる設定ではなくれっきとしたプログラミング言語です。

2. Vim script を書く

ではその Vim script を書いてみます。Vim の設定というよりは Hello, world のような簡単なプログラムを書いてみます。

Vim script の始め方は簡単で、 Vim を起動したらコマンドラインモードでそのまま Vim script が書けます。次の例ではテキストの2行目から4行目を取得して echo で結果を出力しています。結果はそのままステータスラインに表示されます。

map() などの便利な組み込み関数もあります。似たようなコードですが、2行目から4行目の数値をリストで取得して出力、続いてそれを map 関数を通して倍にして出力しています。

一連の処理は .vimrc に関数として定義することで call FuncName() で利用できます。次の関数は数値のリストを受け取ってそれを加算するものです。

3. ISO8583

ここまでで Vim script で普通のプログラミングができることを示しました。さて、カンムと言えば ISO8583 らしいのですが...先日の GoCon オフィスアワーで ISO8583 を Go で Parse してみましょうという出題がありました。これを Vim script で倒してみます。

ISO8583 自体の説明や GoCon の問題の説明や解説は下記のエントリを参照していただけると幸いです。が、少しだけ解説します。ISO8583というのはクレジットカードのデータ通信時に使われるプロトコルで、店頭でカードを切ったときにこのプロトコルにのっとってデータが飛んできます。カード会社はこのプロトコルを捌く必要があり、カンムでもこれを処理するサーバとアプリケーションが元気に稼働しています。これは Vim script ではなくは Go で書かれています。

tech.kanmu.co.jp

3.1 問題のファイルを読む

問題を解くにあたりまずやるべきことは問題のバイナリデータを読むことです。Vim script でもファイルシステムからファイルを読むことができます。次のようにファイルをバイナリモードで読み込んで1バイトずつ処理できます。

let inputfile = "/path/to/github.com/kanmu/gocon-2022-spring/message.bin"
for b in readfile(inputfile, 'B')
  " ISO8583 Processing
endfor

3.2 結果を送信する

さらに問題を解くには結果を送信する必要があるのですが Vim script でも当然できます。ソケットを開いてそこに HTTP を書き込むことで HTTP 通信ができます

let channel = ch_open("168.138.192.92:80",
            \ {"callback": "Callback", "mode": "raw", "waittime": "1000ms"})
let body = printf("{\"input\":{\"Type\":%d,\"PrimaryAccountNumber\":%x,\"ProcessingCode\":%x,\"  AmountTransaction\":%x,\"ExpirationDate\":%x}}",
            \ str2nr(mt), pan, pc, amount, ed)
let header = printf("POST /v1/data/iso8583/validation HTTP/1.1\r\nHost: 168.138.192.92\r\nConte  nt-Type: application/json\r\nContent-Length: %d\r\n\r\n",
            \ len(body))
let payload = header . body
call ch_sendraw(channel, payload)

3.3 ビット演算をする

ISO8583 を処理するにはビット演算を活用する必要があります。こちらについては詳細は上述したとおり GoCon の問題解説のエントリを見てください。and()論理積がとれます。 こちらはビットが立っている場所を調べて、立っていたら処理を行う...というコードになります。

" PAN
if and(bitmap, 0b0100000000000000000000000000000000000000000000000000000000000000) != 0
  if i == 10
    let panbyte = b/2
    let i = i + 1
    continue
  endif
  if i <= 10+panbyte
    let shift = 8 * (panbyte - (i - 10))
    let pan =  pan + b * float2nr(pow(2, shift))
    let i = i + 1
    continue
 endif
endif
" Processing Code
if and(bitmap, 0b0010000000000000000000000000000000000000000000000000000000000000) != 0
  if i <= 10+panbyte+pcbyte
    let shift = 8 * (pcbyte - (i - (10 + panbyte)))
    let pc =  pc + b * float2nr(pow(2, shift))
    let i = i + 1
    continue
  endif
endif

3.4 ビットシフトする

バイナリを処理する傍らビットシフトしたくなるんですが Vim script でも当然それはでき....なかった! Vim script にシフト演算子はありません(たぶん...)!よってここは2のべき乗で対応します。

if i <= 10+panbyte
  let shift = 8 * (panbyte - (i - 10))
  let pan =  pan + b * float2nr(pow(2, shift))
  let i = i + 1
  continue
endif

これは何をやっているのかというと、例えばカード番号 4019-2499-9999-9999 は ISO8583 では次のように BCD で表現されています。

0x40, 0x19, 0x24, 0x99, 0x99, 0x99, 0x99, 0x99

1バイトずつ処理していくため、まず 0x40 を左に7バイトシフトして 0x4000000000000000 とする、次に 0x19 を左に6バイトシフトして 0x19000000000000 とする... を行い、

 0x4000000000000000
   0x19000000000000
     0x240000000000
     ...
+
------------------------
4019249999999999

といった形でカード番号を取り出しています。

4. ISO8583() 関数を書く

重要な処理はだいたいここまでです。あとは一連の流れを組み立てていきます。結果、ISO8583 を処理する関数を書くと次のようになります。

function! ISO8583()
  let inputfile = "/path/to/github.com/kanmu/gocon-2022-spring/message.bin"

  let mt = ""
  let bitmap = ""
  let bitmapbyte = 8

  let panbyte = 0
  let pan = ""

  let pc = ""
  let pcbyte = 3

  let amount = ""
  let amountbyte = 6

  let ed = ""
  let edbyte = 2

  let i = 0
  for b in readfile(inputfile, 'B')
    " message type
    if i <= 1
      let mt = mt . printf("%02s", b)
    endif

    " bitmap
    if i >= 2 && i <= 9
      let shift = 8 * (bitmapbyte - (i - 1))
      let bitmap =  bitmap + b * float2nr(pow(2, shift))
    endif

    " data element
    if i >= 10
      " PAN
      if and(bitmap, 0b0100000000000000000000000000000000000000000000000000000000000000) != 0
        if i == 10
          let panbyte = b/2
          let i = i + 1
          continue
        endif
        if i <= 10+panbyte
          let shift = 8 * (panbyte - (i - 10))
          let pan =  pan + b * float2nr(pow(2, shift))
          let i = i + 1
          continue
        endif
      endif
      " Processing Code
      if and(bitmap, 0b0010000000000000000000000000000000000000000000000000000000000000) != 0
        if i <= 10+panbyte+pcbyte
          let shift = 8 * (pcbyte - (i - (10 + panbyte)))
          let pc =  pc + b * float2nr(pow(2, shift))
          let i = i + 1
          continue
        endif
      endif
      " Amount
      if and(bitmap, 0b0001000000000000000000000000000000000000000000000000000000000000) != 0
        if i <= 10+panbyte+pcbyte+amountbyte
          let shift = 8 * (amountbyte - (i - (10 + panbyte + pcbyte)))
          let amount =  amount + b * float2nr(pow(2, shift))
          let i = i + 1
          continue
        endif
      endif
      " Expiration date
      if and(bitmap, 0b0000000000000100000000000000000000000000000000000000000000000000) != 0
        if i <= 10+panbyte+pcbyte+amountbyte+edbyte
          let shift = 8 * (edbyte - (i - (10 + panbyte + pcbyte + amountbyte)))
          let ed =  ed + b * float2nr(pow(2, shift))
          let i = i + 1
          continue
        endif
      endif
    endif
    let i = i + 1
  endfor

  echo "--------------------"
  echo printf("Message Type: %s", mt)
  echo printf("Bitmap: %b", bitmap)
  echo printf("PAN: %x", pan)
  echo printf("ProcessingCode: %x", pc)
  echo printf("Amount Transaction: %x", amount)
  echo printf("Expiration Date: %x", ed)
  echo "--------------------"

  let channel = ch_open("168.138.192.92:80",
              \ {"callback": "Callback", "mode": "raw", "waittime": "1000ms"})
  let body = printf("{\"input\":{\"Type\":%d,\"PrimaryAccountNumber\":%x,\"ProcessingCode\":%x,\"AmountTransaction\":%x,\"ExpirationDate\":%x}}",
              \ str2nr(mt), pan, pc, amount, ed)
  let header = printf("POST /v1/data/iso8583/validation HTTP/1.1\r\nHost: 168.138.192.92\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n",
              \ len(body))
  let payload = header . body
  call ch_sendraw(channel, payload)
  echo payload
endfunction

function! Callback(handle, msg)
  echo a:msg
endfunction

ここで :call ISO8583() を呼び出してみます。クリアできました。

最後に

あまりドスの効いていない社内イベントの紹介記事になってしまいましたが...。Vim やカンムの社内イベントに興味を持っていただけたら幸いです。

Go Conference 2022 Spring: クイズ ISO 8583 Message Challange の紹介と解説

バンドルカードのバックエンドやインフラを担当しているエンジニアの summerwind です。最近は WebAssembly と JIT に興味があります。

4月23日に開催された Go Conference 2022 Spring ではカンムのメンバーが Go に関する内容で登壇しました。セッションで紹介したスライドは以下で参照できます。将棋プログラミングについては自分もまったく知らない世界の話だったのでとても興味深かったです。


今回のイベントにはカンムもスポンサーとして参加させていただき、今回もオフィスアワーの催しとして Go を使ったクイズ「ISO 8583 Message Challange」を公開しました。

クイズの問題

今回のクイズの問題は以下のリポジトリで参照できます。もし興味がありましたらぜひチャレンジしてみてください。

github.com

今回の問題のテーマは「バイナリ処理」です。インターネットでの通信やデータの保存などでは様々な形式のバイナリが使われていますが、一般的なプロダクト開発ではバイナリを直接扱うようなコードを書く機会は意外と少なかったりします。個人的には Go でバイナリを扱うコードを書くが好きなので、今回のクイズではバイナリのパースを通じてより多くの人にバイナリの操作を楽しんでもらえたらと思い、このテーマを設定してみました。

問題では、カンムの決済処理にも使われている ISO 8583 形式のメッセージをパースしてその中から答えとなる値を見つける、というゴールを設定しています。メッセージのバイナリファイルはリポジトリmessage.bin として保存されているので、これをパースしていくと答えが見つかる仕組みです。今回の問題では、より手軽にクイズにチャレンジしてもらえるよう以下のように parse.go ファイルに定義された Parse() 関数の中身だけを実装すれば回答を出せるようにしてみました。

問題の解説

ここからは実際にバイナリをパースしていく方法を解説してみたいと思います。

まず最初に、実装が必要な Parse() 関数の定義を見てみると次のようになっています。引数 buf にはバイナリの値が byte の配列として保存されており、*Message を返せばいいことが分かります。

// ISO 8583 メッセージのバイト列をパースして Message を返す関数
func Parse(buf []byte) *Message {
    var msg Message

    // TODO: バイト列 buf をパースして msg の各フィールドに値を設定してください

    return &msg
}

次に、戻り値である *Message の構造を見みてみると、次のようになっています。

// ISO 8583 メッセージ構造体
type Message struct {
    // メッセージ: 3桁の数字
    // ISO 8583 バージョンの 0 は含まないことに注意してください
    Type uint16

    // カード会員番号 (PAN): 16桁の数字
    PrimaryAccountNumber uint64

    // 処理コード: 6桁の数字
    ProcessingCode uint32

    // 取引金額: 任意の桁の数字
    AmountTransaction uint64

    // 有効期限: YYMM の4桁の数字
    ExpirationDate uint16
}

この構造から今回のクイズでは、バイナリから以下の5つのデータをパースして抽出すればよいことが分かります。

  • メッセージタイプ
  • カード会員番号
  • 処理コード
  • 取引金額
  • 有効期限

データの抽出には ISO 8583 メッセージのバイナリフォーマットの知識が必要になります。弊社の hiroakis が以前発表した資料にフォーマットの詳細と詳しい解説がありますので、ここからはこの資料とあわせて読み進めてみてください。

最初にメッセージタイプをパースします。これはメッセージの種類などを示す値でメッセージの先頭2バイトを4ビットのパック10進数 (Packed BCD) として扱う必要があります。今回の解説では処理を簡略化するため、BCD の処理については公開されているパッケージである https://github.com/albenik/bcd を利用していきます。

// メッセージタイプを取得
msg.Type = bcd.ToUint16(bin[0:2])

次にカード会員番号のパースに取り掛かりたいところですが、最初にビットマップと呼ばれる領域を取得しておく必要があります。

ビットマップはメッセージにどんな種類のデータが存在するかを示すビットを保存している64ビットの領域です (64ビットより長い場合もあります) 。この領域の各ビットの値が1だった場合はそのビット位置に対応するデータの存在を示しています。例えば、ビットマップの値が2進数で 01110010 であった場合、2、3、4、7番目のデータが存在していることを示しています。

// ビットマップ領域を取得
bitmap := binary.BigEndian.Uint64(bin[2:10])

ビットマップが取得できたので、カード会員番号を抽出します。カード会員番号の存在を示すのはビットマップの左から2ビット目の値なので AND 演算で確認します。ビットが存在した場合は値を抽出しますが、カード番号の値は可変長の値なので、最初に先頭1バイトの長さを抽出し、その長さ分のバイト数を Packed BCD として読み出します。

// バイナリのオフセット
offset := 10

// カード会員番号のビットの存在を確認
if (bitmap & 0x4000000000000000) > 0 {
    // カード会員番号の長さを取得
    length, _ := binary.Varint(buf[offset : offset+1])
    offset += 1

    // カード会員番号を取得
    msg.PrimaryAccountNumber = bcd.ToUint64(buf[offset : offset+int(length)])
    offset += int(length)
}

次に処理コードを抽出します。カード会員番号と同じようにビットマップで存在を確認してから、処理コードの値を取得します。処理コードの値は3バイトの固定長なので、そのまま読み出します。

// 処理コードのビットの存在を確認
if (bitmap & 0x2000000000000000) > 0 {
    // 処理コードを取得
    msg.ProcessingCode = bcd.ToUint32(buf[offset : offset+3])
    offset += 3
}

残りは取引金額と有効期限になりますが、これらは処理コードと同じ固定長のフィールドなので、次のように処理コードと同じように値を取得できます。

// 取引金額の取得
if (bitmap & 0x1000000000000000) > 0 {
    msg.AmountTransaction = bcd.ToUint64(buf[offset : offset+6])
    offset += 6
}

// 有効期限の取得
if (bitmap & 0x0004000000000000) > 0 {
    msg.ExpirationDate = bcd.ToUint16(buf[offset : offset+2])
    offset += 2
}

実はビットマップの存在確認に使用する各ビットの値は定数として事前に定義されているので、それらを使用すると最終的な Parse() の実装は次のようになります。

// ISO 8583 メッセージのバイト列をパースして Message を返す関数
func Parse(buf []byte) *Message {
    var msg Message

    // メッセージタイプを取得
    msg.Type = bcd.ToUint16(buf[0:2])

    // ビットマップ領域を取得
    bitmap := binary.BigEndian.Uint64(buf[2:10])

    // バイナリのオフセット
    offset := 10

    // カード会員番号を取得
    if (bitmap & BitPrimaryAccountNumber) > 0 {
        length, _ := binary.Varint(buf[offset : offset+1])
        offset += 1

        msg.PrimaryAccountNumber = bcd.ToUint64(buf[offset : offset+int(length)])
        offset += int(length)
    }

    // 処理コードを取得
    if (bitmap & BitProcessingCode) > 0 { 
        msg.ProcessingCode = bcd.ToUint32(buf[offset : offset+3])
        offset += 3
    }

    // 取引金額の取得
    if (bitmap & BitAmountTransaction) > 0 {
        msg.AmountTransaction = bcd.ToUint64(buf[offset : offset+6])
        offset += 6
    }

    // 有効期限の取得
    if (bitmap & BitExpirationDate) > 0 {
        msg.ExpirationDate = bcd.ToUint16(buf[offset : offset+2])
        offset += 2
    }

    return &msg
}

実装ができたのでクイズの README.md の記載に従いコードを実行してみると、バイナリファイルから正しい値を取得して正解が表示されました。

$ go run .
--------------------
Message Type: 100
PAN: 4019249999999999
Processing Code: 327327
Amount Transaction: 1138
Expiration Date: 2204
--------------------
Result: VALID: You have successfully parsed the ISO 8583 message! ...

回答判定の仕組み

問題が解けたところで、今回のクイズの回答判定の仕組みについても簡単に紹介します。

カンムではこれまでにも Go Conference の開催にあわせて CTF やクイズを公開してきました。これまでの問題では出題を担当するメンバーが独自に回答を判定する仕組みを実装していましたが、今回のクイズ作成にあたっては、より汎用的な回答判定をする仕組みを採用してみることにしました。

今回の回答判定に採用したのは Open Policy Agent (OPA) です。OPA を使用すれば正解判定を Rego で記述したポリシーとして扱うことができるため、非常に簡単に回答判定の仕組みが用意できました。今回の回答判定では次のようなポリシーを使用しています。

package iso8583

default validation = "INVALID"

validation = "VALID: You have successfully parsed the ISO 8583 message!" {
  input.Type == 100
  input.PrimaryAccountNumber == 4019249999999999
  input.ProcessingCode == 327327
  input.AmountTransaction = 1138
  input.ExpirationDate = 2204
}

目視によるパース

実は ISO 8583 形式のメッセージは16進数で見てみると、ある程度データの値を推測できます。実際に目視でパースして正解した、という方もいたようでした。

$ hexdump -C message.bin
00000000  01 00 70 04 00 00 00 00  00 00 10 40 19 24 99 99  |..p........@.$..|
00000010  99 99 99 32 73 27 00 00  00 00 11 38 22 04        |...2s'.....8".|
0000001e

おまけ

クイズのバイナリに含まれる 3271138 といった値はスターウォーズでたびたび登場するマジックナンバーに由来しています。これは Go のトリビアをリスペクトしてみました。

おわりに

Go Conference の開催中は、Twitter などでクイズの問題に実際に挑戦してバイナリパースの楽しさを感じてくれた方のコメントを見かけたりして嬉しかったです。

カンムでは実際に ISO 8583 のメッセージを処理するシステムを開発して決済サービスを提供しており、バイナリ処理に楽しさを感じるようなエンジニアを募集しています。カジュアル面談などは随時実施していますので、ぜひお気軽にお声がけください。

kanmu.co.jp

最後に、今回も素晴らしい Go Conference の場を提供してくれた運営のみなさま、参加者のみなさま、どうもありがとうございました!

カンムは SRE NEXT 2022 にスポンサーとして参加します #srenext

こんにちは!カンムでエンジニア採用を担当している @ayapoyo です。 ついに今週土曜・日曜は SRE NEXT の開催日ですね! 今回はじめての参加になるのでいまからワクワクしております!

sre-next.dev

ゴールドスポンサーとして協賛します!

今回カンムで初めて SRE NEXT にスポンサーとして参加させていただきます!

カンムは運営する『バンドルカード』は500万ダウンロードを突破、2022年には手元の資産形成に活用できるクレジットカード『Pool』をリリース予定と、サービスが拡大していく中でより信頼性の高い決済インフラを構築していくことが求められます。

今回のスポンサーをきっかけに、さまざまな業種・領域・フェーズにおける SRE 領域の知見を得るとともに、カンムとしても SRE コミュニティへ還元していければと思っています。

kanmu.co.jp

スポンサーセッションに登壇します!

今回はスポンサーセッションとして、インフラエンジニアの @sgwr_dts が登壇いたします!

もうすぐリリースされる新規事業『Pool』についてお話します。 Track B にてご視聴いただけます!ぜひ御覧ください。

そしてイベント当日はオンラインブースも出展予定です! CTO の @mururururu COO の @_achiku が滞在しているので、ぜひ遊びに来てくださいね。

参加申し込みは SRE NEXT HP から

sre-next.dev

当日たくさんの方とお話できることを楽しみにしております!イベントでお会いしましょう〜👋

カンムのセキュリティ事情

こんにちは、livaです。

カンムでセキュリティエンジニアやってます。入社してから半年程度経った今はPCI DSSの監査準備だったり優先度高めにした施策をOKRに落とし込んで手を動かしたりと慌ただしく動いてます。

初執筆のテックブログでなにを書こうかなと考えていて、3月の末に出たPCI DSSv4がいいかとも思ったんですが、読むだけで一苦労だったので諦めました。あとからゆっくり読みます。 今回はカンムの今と将来のセキュリティ事情を書こうと思います。

入社前の想定

面接や面談時にいくつか課題を聞いていて、大きく2つになるのかなーと考えてました。

1. PCI DSSの運用の課題

カンムはクレジットカードの決済フローでは「イシュア」にあたり、業界のセキュリティ基準であるPCI DSSに準拠している必要があります。これがないとそもそものビジネスが成り立ちません。毎年の準拠が必要なため、最低限のセキュリティの体制は出来上がっています。が、日々の運用や監査前の準備は慌ただしくなりがちでした。

2. プロダクトセキュリティ全般の課題

すでにいたエンジニアの良心に任せた運用とPCI DSSの要件に沿った運用がされています。防御機構も一通り入っているもののアラートに対する反応であったり、AWSで回しているスキャナ結果への反応だったりに対してあまり積極的に手を動かせていませんでした。

入社してから現在までの動き

PCI DSS

今までの自分は監査する方の手伝いはしたことあるんですが、受ける側はまともに経験してないので「とりあえず今年回してなにができるか考えて来年やりやすいように変える!」で動かしています。実質なにもできてない。v4のリリースもあって来年はこっちに揃えるつもりでもいるので「v4への対応&運用効率化」に課題が増えました。ナンテコッタイ。

プロダクトセキュリティ

PCI DSSと違って得意領域で、さらに時間をかけずに片付けられる課題もあったので以下の様なものはさっと片付けました。

それ以外にある腰を据えて取り組まないといけない課題については洗い出してざっくりな優先順位を出しました。

その他

なにかが起きた時、全社としてのなにかしら判断基準があるわけではなくて各チームの判断で動いているので、一体感のある何かは作りたいなぁというのを感じたので課題に積みました。そのための指標としてカンムで持っているいろんな情報資産のレベル分けをして、それをベースに意思決定できるような仕組みを作ろうとしてます。前職であったんですが色んなことが進めやすくなったのでカンムでも作ろうかと思って動いてます。

セキュリティOKR

立てようとしたきっかけが完全に覚えてないんですが、CTOと話してるときにそういう話をしたと思います。 この話の前後で同僚が退職したのもあり「1人だとどんだけできんだべ?」と考えた結果、Objective1つにKeyResult3つを立てました。この時気にしていたのは以下の点です。

  • 自分が楽しめること
  • 進捗0.7が完了
  • 0.7から1.0は趣味
  • チェックポイントの到達条件は明確に

OKRの立て方としては基本的なところですよね。基本に忠実に、やりやすいようにしました。自分が楽しめるのも個人的にはずっと大事にしているので織り込んでます。やりたいことをやるために先に手を付けなきゃいけない部分があるのでやる、みたいなことですね。先に面白いことが待ってるなら意外とやれるもんです。

これから

今は「目の前にあるもの片付けて隙間時間で新しいことの仕込み!」って状態になってますが、将来も見据えてはいます。

チーム

今は1人ですが、今後2人3人と増えていって、できることが増えた時も、各自が主担当領域を持ち、他の領域にもオーバーラップしていくのが自分としては好きなので、そういうチームを目指したいなぁと思います。例えば監査、インフラ、アプリと分けたとき、それぞれに担当を分けるけど、それぞれがなんとなく全部を把握していてバックアップとして機能できる、という。色んなチーム体系があるけど、自分の中ですんなりいきそうな体制はこうなるのかなぁと思ってます

PCI DSSの運用

ここは最近界隈でホットなOPA(Open Policy Agent)と相性がいいんじゃないかという話になっていて、今年度の監査が終わってから本格的に着手しようかなぁなんて考えてます。個人的にも「運用できたら3割くらいは心労が減るんじゃね?」って思ってたりするので手を付けたいですね。このビジネスをしている以上、監査からは逃げられないのでなるべく準備に手がかからない未来を目指したいところです。

プロダクトセキュリティ

エンジニアの個人技でどうにかなってるので、これを統一した仕組みに持っていきたいと思ってます。 「PR作ったらCIでDAST/SASTの各種スキャナ動かして、その結果を適当なとこに集めて、Dashboardで状況把握ができる」なんて形を作れたらなぁと思ってます。アプリもコンテナもインフラもそこを見ればどんな脆弱性があって具体的なリスクはこれで対応状況はどうなっていてというのが見れたら視覚的にも楽しいと思うんですよね、多分。あまりに検出結果が多くてげんなりすることが多いかも。 カードにありがちな不正利用にもセキュリティが齧る余地があるので、対応チームの動向をチラ見しながら自分なりに考えてます。自分がやられて嫌なこと考えてると楽しいのもあり、タスクで煮詰まった時の気分転換になってます。

全体

基本的なものはあるのだけど、それを日常的に動かそうとなると不足しているものがそこそこあるので、そういったものの整備や社内広報をして…なんてのをぼんやりと考えてます。カンムのいいところとして、エンジニア以外の職種でもエンジニアチックな動きができるので、そういった文化は活用しながらカンムに合ったセキュリティ体制を作っていきたいという思いが強いです。

最後に

現状と未来は出せる範囲で伝えられたかなぁと思います。「どこから手を付けるかはだいたい見えてるけどやることいっぱいで手が足りない!」って状況なので、一緒にやってくれるエンジニアを募集中です。「セキュリティに興味がある」ってだけでも大丈夫です。自分が必要なことを教えます。もちろん「こういうのやりたい!」って飛び込んできてくれる人も大歓迎です。

kanmu.co.jp

カンムは Go Conference 2022 Spring にスポンサーとして参加します #GoConference #gocon

こんにちは!カンムでエンジニア採用を担当している @ayapoyo です。 ついに今週土曜日は Go Conference 2022 Spring の開催日! 今回もスポンサーとして参加させていただきます。

gocon.jp

シルバースポンサーやります ʕ◔ϖ◔ʔ

2021年に引き続き、今回もスポンサーとして Go Conference に参加させていただきます!

カンムが展開する "バンドルカード" そして新規事業の "Pool" のバックエンドは Go メインで開発しており、このイベントを通して Go コミュニティの発展に寄与できればと思っています 💳

kanmu.co.jp

エンジニアがセッションに登壇します ʕ◔ϖ◔ʔ

今回の GoCon では、CTO の @mururururu バックエンドエンジニアの @pongzu がセッションに登壇します! 以下スケジュールで登壇するので、ぜひご覧ください。

16:50 Go で始める将棋AI @mururururu

17:55 外部コマンドの実行を含む関数のテスト @pongzu

オフィスアワーで クイズ を開催 ʕ◔ϖ◔ʔ

今回の GoCon も完全リモート開催!Remo でオフィスアワーを実施します。 カンムのブースではこんなことをやります!

決済処理に使われている ISO 8583 のバイナリを解析するクイズに挑戦!

カンムが提供するクレジットカードの決済処理にも使われている ISO 8583 形式のバイナリを解析するクイズを当日公開予定。 ブースやTwitterで随時ヒントの発表や解説などを行います!

カンムのエンジニアが作成したこのクイズ、なかなか解き応えのあるものに仕上がっています…! セッションの休憩時間、ぜひクイズにもチャレンジしてみてくださいね!

このほかにも

  • カンムやバンドルカード・Pool について聞いてみたい!
  • 登壇していたエンジニアと話してみたい!
  • Go についてわいわい話したい!

などなども大歓迎です!Remo のブースに遊びに来てみてくださいね!

参加登録は connpass から ʕ◔ϖ◔ʔ

gocon.connpass.com

gopher のみなさんとお会いできることを楽しみにしております👋