NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    AWS LambdaでワンタイムパスワードのAuthenticatorを作ってみる

    はじめに

    2要素認証を設定することが企業は元より個人でも当たり前になってきましたね。よくあるのがGoogle Authenticatorなどの認証コードを使用するものです。これにより、「IDとパスワードを知っていること」と「有効な認証コードを取得できること」の2要素でセキュアな認証プロセスが構築されます。

    有効な認証コードを取得する際には、Google Authenticatorなどが主流ですが、このようなアプリを使用すると個人で所有する端末でしか有効な認証コードを取得できなくなります。これ自体は本人のみが所有しているという要素を認証プロセスに組み込むことを意図したものですが、認証コードの取得方法が所有する端末に限定されており、取得方法に拡張性がない状態です。

    拡張性を作るとセキュアな状態を維持しにくくなると理解しつつ、興味本位でAPI経由で認証コードを取得できる仕組みを作り、拡張性を持たせてみたので共有したいと思います。

    やりたいこと

    Google Authenticatorと同じような機能をAWS Lambdaで実装し、API経由で6桁の認証コードを取得できるようにする。

    前提知識

    ここまで認証コードと呼んでいたものは、ワンタイムパスワードのことです。ワンタイムパスワードについて、基本的な知識を抑えておきます。

    OTPとは

    多要素認証の一つとしてよく用いられている一度のみ使用することができるパスワードのことです。 一度のみしか使用できないため、ワンタイムパスワード(One-Time Password)と言われています。

    TOTPとは

    Time-based One-Time Passwordのことで、一定時間使用することができるパスワードのことです。 パスワードが有効な時間は一般的に30秒~60秒のようです。 Googleアカウントの2段階認証プロセスやAWSの多要素認証として使用することができ、2要素認証の手段として主流のものです。

    TOTPの実装方法

    TOTPはRFC6238として標準仕様が定義されており、秘密鍵と時間を元にパスワードを生成するアルゴリズムとなっています。
    pythonではそれを基に実装されたライブラリがあるため、これを使うと簡単に実装できます。

    github.com

    AWS Lambdaを実装する

    AWS LambdaでTOTPを生成する処理を実装していきたいと思います。 AWS LambdaのランタイムにはPython 3.9を使用します。

    Lambdaレイヤーを実装する

    Lambda関数でpyotpライブラリをimportできるようにするために、まずはLambdaレイヤーでpyotpを取り込んでおきます。 pythonが実行できる環境で以下のコマンドを実行していきます。

    1. 「python」ディレクトリを作成する

    mkdir python
    

    2. pyotpライブラリを「python」ディレクトリにインストールする
    cf. コンパイル済みバイナリを含む Python パッケージを Lambda に追加する | AWS re:Post

    pip install \
        --platform manylinux2014_x86_64 \
        --target=python \
        --implementation cp \
        --python 3.9 \
        --only-binary=:all: --upgrade \
        pyotp
    

    3. 「python」ディレクトリをzip圧縮する

     zip -r ./python.zip ./python
    

    4. 生成された「python.zip」をLambdaレイヤーにアップロードする

    Lambda関数を実装する

    1. Lambda関数を作成する

    2. Lambdaレイヤー「pyotp」をアタッチする

    3. コードを実装する

    import json
    import pyotp
    
    # 簡単にするためハードコーディングしていますが、実際にはよりセキュアな方法で管理してください
    SECRET = '****'
    
    def lambda_handler(event, context):
        return {
            'statusCode': 200,
            'body': json.dumps({
                'code': pyotp.TOTP(SECRET).now()
            })
        }
    

    4. SECRET(秘密鍵)を取得して上記の****を実際の値で差し替える
    SECRETはGoogle Authenticatorなどに読み込ませるQRコードの中に記録されています。 例えば、Googleアカウントの2段階認証プロセスの登録で発行されるQRコードの中は以下のようになっています。
    otpauth://totp/Google%3Ayouraccount%40gmail.com?secret=****&issuer=Google

    5. Lambda関数をテスト実行してTOTPを確認する

    Amazon API Gatewayの実装

    外部からAPI経由で認証コードを取得できるようにするためにAPI Gatewayを構成していきます。
    今回は簡単に試すためにHTTP APIを使用しますが、用途に応じて変えてください。

    1. HTTP APIを作成します

    2. 発行された呼び出しURLからAPIエンドポイントを確認する
    呼び出しURLにLambda関数名を繋げたURLがエンドポイントとなります。
    https://n8ilsfyu25.execute-api.ap-northeast-1.amazonaws.com/CustomAuthenticator

    3. curlコマンドやWebブラウザなどでAPIエンドポイントにリクエストしてみる
    下図の通りAPI経由で認証コードが取得できていることが分かります。

    おわり

    APIから認証コード(TOTP)を取得することができるようになりました。SlackやLINEなどに組み込めばチャットツール上で認証コードを取得できるようにすることもできそうですね。ただし、API自体にリクエスト元検証の仕組みは必要ですし、チャットツールに組み込む場合は許可された人だけが参照できるようにする必要もあります。これにより、APIやチャットツールへのアクセス権限を所有する人だけが認証コードを取得できるようになります。

    ということで、今回はTOTPをAPI経由で取得できる仕組みを構築してみました。 元々はGoogle Authenticatorが何をやっているのかよく分からなかったですが、今回このような仕組みを作ってみたことでよく分かったので嬉しいです。

    執筆者岩崎 聖夜

    モバイルアプリ開発者です。AWSでデータ分析基盤を作ったりもしています。