Amazon Rekognition で作る顔認証:証明写真 × ライブカメラで本人確認
こんにちはSGKです。
最近、Amazon Rekognition を使って「証明写真とライブカメラを使った顔認証」を実装する機会があったので、主に Cakephp で実装した場合のサンプルコードを提示しつつ紹介したいと思います。
要件としては、まず顔写真付きの証明書画像をアップロードし、その写真とアップした人(デバイスのカメラの前にいる人、ライブユーザー)が同じ人物かどうかを調べる事です。
要点
要点は以下の通り、順番も以下の順で処理します。
- 証明書画像をアップロード
- Cognito Identity Pool から匿名アクセス用のトークンを取得
- Rekognition Face Liveness セッションの開始
- AWS SDK Rekognition クライアントで撮影しライブユーザか判別
- 証明書画像の顔とライブ判別で撮影した画像が同じか判別
証明書画像をアップロード
まず対象となる顔写真付きの証明書の画像をユーザにアップロードしてもらいます。
第一段階の処理としてはアップロードされたファイルが画像かどうかを確認するに留めます。
このアップロードのHTTPのセッションで次の処理に進みます。
<!--
最初は簡単に、画像をアップするだけのフォーム、
ここで選択した画像を Ajaxで送信する想定、jsは略します。
-->
<form>
<input type="file" name="image" accept="image/*">
<input type="submit" value="次へ">
</form>
Cognito Identity Pool から匿名アクセス用のトークンを取得
AWS Rekognition はライブユーザの確認をブラウザ側で処理します。
そのためライブユーザ確認のAPIの利用許可をするためのトークンをサーバ側で発行してブラウザ側に渡す必要があります。
Cognito Identity Pool で一時的な匿名アクセス用のトークンやキーなどを取得します。
引き続きこのHTTPセッションで次の処理に進みます。
Rekognition Face Liveness セッションの開始
トークンとは別にライブユーザ確認のセッションを開始する事をAWSに通知し、セッションIDを取得しておきます。
このセッションIDはブラウザ側でライブユーザ確認をするためと、ライブユーザ確認時に撮影した画像を取得するために使用します。
以上のサーバ側の処理で一旦クラインアントにレスポンスします。
// CakePhpのコントローラのメソッド、ブラウザからのAjaxを受けて、AWS RekognitionのFaceLivenessセッションを開始するためのコード例
public function createFaceLivenessSession(){
try{
// アップロードされた証明書画像の簡易バリデーション、保存はしないし、この段階では顔認証などしない
// validationCertificateImages は自作のメソッド、内容は略します。
$resImegeValid = $this->validationCertificateImages();
if( is_string($resImegeValid) ){
throw new \Exception($resImegeValid);
}
// Cognito Identity Pool から匿名アクセス用のトークンを取得
// CLIENT_CONFIG() は AWS の シークレットやトークンなどAPI接続情報を配列で返す関数の想定、内容は略します。
$clientCognito = new \Aws\CognitoIdentity\CognitoIdentityClient($this->CLIENT_CONFIG());
$resultCognitoId = $clientCognito->getId([ 'IdentityPoolId' => COGNITO_ID_POOL_ID ]);
$resultCognitoCredentials = $clientCognito->GetCredentialsForIdentity([
'IdentityId' => $resultCognitoId['IdentityId'],
]);
// Rekognition Face Liveness セッションの開始
$clientRekognition = new \Aws\Rekognition\RekognitionClient($this->CLIENT_CONFIG());
$resultSession = $clientRekognition->createFaceLivenessSession([
'ClientRequestToken' => uniqid(),
'Settings' => [
'OutputConfig' => [
'S3Bucket' => S3_BUCKET,
'S3KeyPrefix' => S3_KEY_PREFIX_FACE_LIVENESS,
],
'AuditImagesLimit' => 1
]
]);
// Liveness セッションのID、アクセス用のトークンやキーをjsonで返す
return $this->response->withType('application/json')->withStatus(200)->withStringBody(json_encode([
'sessionId' => $resultSession['SessionId'],
'identityId' => $resultCognitoCredentials['IdentityId'],
'accessKeyId' => $resultCognitoCredentials['Credentials']['AccessKeyId'],
'secretKey' => $resultCognitoCredentials['Credentials']['SecretKey'],
'sessionToken' => $resultCognitoCredentials['Credentials']['SessionToken'],
'region' => \App\Lib\AwsRekognition::AWS_REGION,
], JSON_UNESCAPED_UNICODE));
}catch( \Exception $e ){
return $this->response->withType('application/json')->withStatus(422)->withStringBody(json_encode([
'error' => 'エラー:'.$e->getMessage(),
], JSON_UNESCAPED_UNICODE));
}
}
AWS SDK Rekognition クライアントで撮影しライブユーザか判別
Rekognition Face Liveness セッションID、Cognito Identity のトークンとキーを使ってFace Livenessを開始します。
ここではカメラの前の人がライブユーザーかどうかを評価します。
カメラの前に写真を置いたり、人では無かったりすると評価値が下がります。
ですが、証明書と別の人がカメラの前にいてもここでの評価は下がりません。
Face Liveness を開始するとあとはこのAPIに則った動作が開始され、カメラの起動、撮影、ライブユーザーかどうかの評価点の返却、までの処理が行なわれます。
最終的にライブユーザーかどうかの評価点が規定値以上かどうか評価出来たら次に進むためセッションIDと最初にアップした証明書の画像を再度サーバにポストします。
※この部分のコードは省略します。
AWS Amplify を使ってコンパイルした js を使用して、Ajax の返りを受けて処理の続きをコールバック起動したり、画像を再送信するためBase64化したり、Face LivenessのインターフェイスをHTMLで用意、微修正のCSS、エラーのハンドリングなど書ききれない処理を沢山やります。。
証明書画像の顔とライブ判別で撮影した画像が同じか判別
先ほどの Face Liveness で撮影した画像は AWS S3 に保存されており、セッションIDを使って参照ができます。
セッションIDを使って取得した情報には画像の他に「ライブ度合いの評価点」も含まれるため、再度その点数が規定値以上か確認します。
※これを行なわないと「ライブ度合いの評価点が規定値以上」とフロント側で偽装されてポストされている可能性を排除できません。
二段階目の確認として Face Liveness の画像とアップロードされた証明書の画像を compareFaces で確認し、閾値以上であれば「証明書の顔の人がカメラの前にいる」という確認が完了します。
// CakePhpのコントローラのメソッド、最終的に総合的なバリデーションを行うためのコード例
public function rekognitionFace(){
try{
//アップした証明書画像をデコード+簡易な画像のバリデーション
// decodeB64Image は自作のメソッド、内容は略します。
$image = $this->decodeB64Image( $this->request->getData('image_front') );
if( empty($image) ) throw new \Exception('証明書画像の取得に失敗しました。');
$clientRekognition = new \Aws\Rekognition\RekognitionClient(CLIENT_CONFIG());
// セッションIDから Face Liveness の結果を取得
$resultFaceLiveness = $clientRekognition->getFaceLivenessSessionResults([
'SessionId' => $this->request->getData('sessionId')
]);
// ライブユーザのスコア(最大100)が FACE_LIVENESS_BORDER 未満は NG
if( $resultFaceLiveness['Confidence'] < FACE_LIVENESS_BORDER ){
throw new \Exception('カメラ映像による本人確認に失敗しました。もう一度やり直してください。');
}
//証明書画像と、FaceLiveness で撮影した画像の顔がの閾値(最大100)が FACE_REKOGNITION_BORDER 以上か評価する
$resultCompareFaces = $clientRekognition->compareFaces([
'SourceImage' => ['Bytes' => $image],
'TargetImage' => $resultFaceLiveness['ReferenceImage'],
'SimilarityThreshold' => FACE_REKOGNITION_BORDER,
]);
if( empty($resultCompareFaces['FaceMatches']) ){
throw new \Exception(
'証明書画像の本人確認に失敗しました。'
);
}
return $this->response->withStatus(200)->withStringBody('ok');
}catch( \Exception $e ){
return $this->response->withType('application/json')->withStatus(422)->withStringBody(json_encode([
'error' => 'エラー:'.$e->getMessage(),
], JSON_UNESCAPED_UNICODE));
}
}
まとめ
と上記のように Amazon Rekognition を使えば簡単に顔認証とライブユーザー確認ができます。
と言いたいところですが中段のコードの記述を略した部分を含めかなりの量のコードを書かなくてはいけませんし、実装に合わせてクライアントAjaxバックエンドの繋ぎなどで難しい所があります。
それにこれも省略している部分なのですがAWS側の設定をやらないといけませんし、証明書画像には顔があれば良いだけなので正規の証明書かどうかの確認をしていません。
略した部分についてはAWSの公式ドキュメントをご参照の上、補足として頂きたいと思います。
とはいえコアな部分「顔認証」と、「ライブユーザー確認」がAPIで提供されている事はありがたい事です。