MFAとGoogle Authenticatorの仕組み

AWSのMFAで使用されるGoogle Authenticatorのワンタイムパスワード(TOTP)は、サーバとクライアントで共有する秘密鍵と現在時刻から確認コードを計算するアルゴリズム。

80ビットの秘密鍵をQRコード(16文字のBase32※(A-Z, 2-7))としてブラウザ上に表示し、サーバとクライアントに同じ秘密鍵が保存される。現在時刻は30秒ごとに値が変わるカウンターに変更されてから確認コードの6桁の数字が計算されてアプリに表示される。

※32種類の英数字のみを用いてバイナリデータを示す

TOTPに対応するサービスはGoogle, Amazon, Microsoft, Twitter, Instagram, Facebook, Dropbox, Github, WordPress, Slackなど。

K: 秘密鍵
TC: 現在時刻(UNIX TIME)
X: 時間ステップ(30)
T0: カウント開始時刻(0)
N: トークンの長さ(6)
ハッシュアルゴリズム: SHA-1

T = floor((TC – T0) / X)
H = HMAC-SHA-1(K, T)
TOTP = Truncate(H)
※RFC 4226 に定められている20バイト文字列から10進数N桁のトークンを得る関数

TOTPの実装方法についてClassmethodさんの解説
https://dev.classmethod.jp/articles/totp-implementation-pure-python/

自分でもやろうと思ったが、HMAC-SHA-1以降のTruncate関数の実装のところがうまくできない。

import os
import base64
import time
import math
import hmac
import hashlib

private_key = os.urandom(5) # 5byte = 80bit
private_key_base32 = (base64.b32encode(private_key))

t1 = time.time()
h = math.floor((t1 - 0) // 30)
sig = hmac.new(private_key_base32, str(h).encode('utf-8'), hashlib.sha1).hexdigest()

MFAが共通の秘密鍵とUnixTime、SHA-1のハッシュ関数からTruncate関数で10進数に変換して30秒ごとに表示しているということがわかりました。UNIXタイムを認証に使う場合、サーバ側とクライアント側でUnixタイムがずれていないことが前提となるため、どうやって担保するのが良いかわかりませんでしたが、アプリケーション側のユーザ認証でもAWSのMFAと同じような秘密鍵を共有した認証の仕組みが作れたら仕組みとして面白いのかなと思いました。

参考サイト
https://sekika.github.io/2016/03/26/GoogleAuthenticator/

[docker] amazonlinux2でPHP8.1を動かしたい

Dockerfile

FROM amazonlinux:2

RUN yum update -y

RUN amazon-linux-extras install -y epel
RUN yum -y install \
http://rpms.remirepo.net/enterprise/remi-release-7.rpm

RUN yum -y install \
    pcre-devel \
    php81 \
    php81-php-cli \
    php81-php-common \
    php81-php-devel \
    php81-php-gd \
    php81-php-intl \
    php81-php-mbstring \
    php81-php-mysqlnd \
    php81-php-pear \
    php81-php-pecl-apcu \
    php81-php-process \
    php81-php-opcache \
    php81-php-redis \
    php81-php-soap \
    php81-php-pecl-xdebug3 \
    php81-php-xml \
    php81-php-zip

 RUN alternatives --install /usr/bin/php php /usr/bin/php81 1

$ sudo docker build . -t amzn2php8.1
extrasではなくepelでremiでインストールする。
なるほどー

docker-compose up -dとdown –rmi all

$ docker-compose up -d
docker-compose.yml

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:80"

http://192.168.56.10:8080/
$ sudo docker images;
dockerhttpd_web latest 71323a09a84b 2 minutes ago 56.9MB
$ sudo docker-compose down
$ sudo docker images;
dockerhttpd_web latest 71323a09a84b 4 minutes ago 56.9MB
-> docker-compose downだけだと、imageは残ったまま

$ sudo docker-compose up -d
$ sudo docker-compose down –rmi all
$ sudo docker images;

imageも削除される
なるほど、概要は理解した

【clocコマンド】ディレクトリ配下のソースコードの行数を調べたい

wcコマンド: ファイルの行数、文字数を数える

$ ls -la
total 16
drwxrwxr-x 2 vagrant vagrant 4096 Dec 8 08:55 .
drwxr-xr-x 24 vagrant vagrant 4096 Dec 8 07:38 ..
-rw-rw-r– 1 vagrant vagrant 928 Dec 8 09:20 api.php
-rw-rw-r– 1 vagrant vagrant 892 Dec 8 09:24 index.php

$ wc -l $(find ./ -type f)
30 ./api.php
39 ./index.php
69 total

### cloc
clocコマンドでも利用できるみたいだ
$ sudo apt install cloc
$ cloc ./
2 text files.
2 unique files.
0 files ignored.

github.com/AlDanial/cloc v 1.82 T=0.01 s (199.1 files/s, 7068.3 lines/s)
——————————————————————————-
Language files blank comment code
——————————————————————————-
PHP 2 16 7 48
——————————————————————————-
SUM: 2 16 7 48
——————————————————————————-

うーん、wcよりclocの方が正確だな…

スタブを使ってテストをしたい

外部連携APIを開発する際に、環境がないといったケースに対応するためスタブってテストを行う。そのために簡単なスタブを構築します。

<?php
/* スタブAPIが受け付けるPOSTパラメータ:name */

header('Access-Controll-Allow-Origin: *');
// header('Access-Controll-Allow-Origin: http://192.168.56.10:8000/post.php');
header('Access-Controll-Allow-Credentials: false'); // Basic認証やCookieのやりとりをする場合に必要
header('Access-Controll-Allow-Headers: Content-Type');
header('Content-Type: application/json; charset=utf-8');

date_default_timezone_set('Asia/Tokyo');

if(isset($_POST['name']) === false || $_POST['name'] === ''){
	$_POST['name'] = 'TEST_API';
}

$postName = htmlspecialchars($_POST['name'], ENT_QUOTES);

$array = [
	'name' => $postName . '_RECIEVED',
	'date' => date("Y-m-d H:i:s"),
];

$json = json_encode($array);

// $file = new SqlFileObject('log.txt', 'a');
// $file->fwrite(
// 	"【→API】RequestParameter:" . $postName .  "'\n【←API】ReturnParameter :" . $json . "\n----------\n"
// );

echo $json;
exit;
$url = "http://192.168.56.10:8000/api.php";

// 設定するHTTPヘッダフィールド
$headerdata = array(
	'Content-Type: application/json',
	'X-HTTP-Method-Override: GET'
);

$param = array(
	"name" => "taro"
);

$postdata = json_encode($param);

$ch = curl_init($url);
$options = array(
	CURLOPT_POST => true,
	CURLOPT_RETURNTRANSFER => true,
	CURLOPT_HEADER => true,
	CURLOPT_HTTPHEADER => $headerdata,
	CURLOPT_POSTFIELDS => $postdata
);
curl_setopt_array($ch, $options);

$response = curl_exec($ch);
$response_info = curl_getinfo($ch);
$response_code = $response_info['http_code'];
$response_header_size = $response_info['header_size'];
curl_close($ch);

if($response_code == 200){
	print "[Result] success.\n";
} else {
	print "[Result] failed [$response_code]. \n";
}

$response_body = substr($response, $response_header_size);
print "[ResponseData]\n".trim($response_body);

なるほど、このような仕組みなのか…

Swagger Codegenとドライバ・スタブコード

Swagger Codegenを使用するとAPIコンシューマのドライバコードやAPIプロバイダのスタブコードを自動生成できる。
Swagger UIでインターフェース仕様書を作成し、APIコンシュマー、プロバイダを自動生成する

スタブコードとは?
– テスト実行時に呼び出し先が未完成の時に代替えとして使用する
– 値を返すダミー(本物に似ているが中身はない)
– スタブとは切り株という意味
– 呼び出し元コードから見て下位

ドライバ(代替)とは
– テスト実行時に、呼び出し元が未完成等の時に代替えとして使用
– ダミー(本物に似ているが中身はない)
– 対象コードから見てドライバは上位に当たる

呼び出す側がドライバで呼び出される側がスタブって意味か

PostgreSQLでDDLを作成する

$ psql –version
psql (PostgreSQL) 14.2 (Ubuntu 14.2-1.pgdg20.04+1)

ddl.sql

CREATE TABLE item (
	ItemID int,
	ItemName character varying(20)
);

insert into item values(0001, 'ゼリー'), (0002, 'プリン'), (0003, 'ヨーグルト');

### sqlに実行
$ psql -h HostName -p PortNumber -U UserName -d DatabaseName -f ddl.sql

$ sudo -u postgres psql -d testdb -f ddl.sql
CREATE TABLE
INSERT 0 3

testdb=# select * from item;
itemid | itemname
——–+————
1 | ゼリー
2 | プリン
3 | ヨーグルト
(3 rows)

なるほど、勉強になる。