↓もし、良かったらSNSでの紹介よろしくお願いします。

WebAPIを利用したい人必見!PythonでHMAC

bootstrap

HMACとは?

HMACはAPIの利用で必要となりつつある

以前までHMACは暗号プロトコルの中でのみ登場する専門的なツールでした。しかし、Web APIが普及して、APIに認証機能を設けるためにHMACが使われはじめています。なので、専門家だけではなく一般のエンジニアでも使い方をマスターすべきツールだと私は考えています。そこで今回はPythonでHMACを利用する方法について解説します。

HMACを認証方式に用いるAPIの例

  • Twitter API
  • Amazon Product Advertising API
  • Tumblr API

HMACはどこで使う?

WebAPIを利用するとき、ある命令をAPIに送ります。その命令を受け取ったサーバーは、その命令の送り主を知ることができません。そこで、命令の送り主の情報を書き込んでもらうときに、よく用いられるツールがHMACです。

なぜHMAC?

送り主の情報を書き込むだけだったら送り主のIDを命令の後にくっつけるだけで良くて、特殊な技術は必要ないです。では、なぜHMACが必要なのでしょうか?それはHMACにはなりすまし防止機能が付いているからです。IDに紐付いた秘密鍵(パスワードのようなもの)を持っている人しかそのIDの送り状を作れない仕組みを可能にしているのがHMACです。

HMACはネット上で印鑑証明を再現するもの

syorui_inkansyoumei

HMACは印鑑証明にたとえられます。HMACの用語と印鑑証明との対応関係は次のようになります。

HMACの関連の用語と印鑑証明の対応関係

  • 印鑑の刻印    → secret key(秘密鍵)
  • 印鑑の種類    → hash function
  • 契約内容     → message(APIへの命令)
  • 捺印       → signature(署名)
  • 捺印された契約書 → message+signatureをURLに変換したもの
  • 捺印の検証    → verify(認証)

つまりWebAPIを利用する場合はmessage+signatureをサーバーに送れば良いことになります。なので、今回は捺印のされた契約書にあたるmessage+signatureの作り方についての解説をします。

PythonでHMAC

サンプルコード

# -*- coding: utf-8 -*-
import hmac
import hashlib
import base64
import urllib
#APIごとに設定が必要な値
secret_key = b"hogehogehogehoge"
hash_func = hashlib.sha256
encode_func = base64.urlsafe_b64encode
#APIごとに設定が必要な値おわり
message = b"Hello API!"
sing_gen = hmac.new(secret_key, message, hash_func)
raw_sign = sing_gen.digest()
sign = encode_func(raw_sign)
#+や=のエンコードが必要な場合は下の行をコメントアウト
#sign = urllib.quote(sign)

解説

secret_key = b"hogehogehogehoge"
hash_func = hashlib.sha256
encode_func = base64.urlsafe_b64encode

ここではAPIごとに異なる設定をしています。秘密鍵はバイト文字列として読み込む必要があります。署名はhash_funcの出力なのでバイト列で得られます。しかし、バイト列の状態だとURLで送信することができません。そこで、バイト列を文字列に変換する必要があります。その変換をするのがencode_func(バイト列→文字列)です。hash_func、encode_funcはAPIごとに異なるためドキュメントで確認が必要です。

message = b"Hello API!"

apiに対する命令であるmessageを設定しています。hash関数の入力はバイト文字列である必要があるのでバイト文字列として読み込んでいます。

sing_gen = hmac.new(secret_key, message, hash_func)

この部分が印鑑を押すところです。

raw_sign = sing_gen.digest()

sing_gen.digest()で捺印をraw_sign(バイト列)として出力します。

sign = encode_func(raw_sign)

これで署名の文字列であるsignができました。このsignをURLにくっつけてAPIにGETリクエストをすると認証が行われるはずです。

Apiによってはもう一段階処理が必要な場合も

sign = urllib.quote(sign)

base64.urlsafe_b64encodeurlで変換したとしても”=”や”+”などのクエリ内に含まれているとややこしい文字が含まれることがあります。なので、Apiによっては”=”や”+”をurllib.quoteでURLエンコードする必要があります。