[GO] The story of introducing a multi-factor authentication function using a one-time password into a Java application

Overview

Here is a summary of the findings of introducing one-time password authentication to Java applications that have a user authentication function based on ID / Password.

What has been realized is "Multi-Factor Authentication". It's also called two-step verification, which is a hot topic in a certain Pay. We say element or stage, but this article deals with the former. (See below) In addition, the one-time passwords used in this article are "time-based", so-called TOTP (Time-based One Time Password) compliant. (See below)

The contents are roughly divided into two parts: a "setting function" that allows users to authenticate with a one-time password, and an "authentication function" that is performed for users whose settings are valid.

We also assumed that you would use Google Authenticator to issue the one-time password. There are several one-time password issuing apps, but each has slightly different specifications.

About the three elements of authentication

It's a little off topic, but authentication has three components in the first place.

  1. Something you ** know ** (knowledge element) --Elements that only the person is aware of. This is the password.
  2. Something you ** have ** --Elements that only the person has. This is the one-time password generated from your smartphone.
  3. Something you ** are ** (biological element) ――It is an element due to the physical characteristics of the person himself / herself. For example, fingerprints and veins.

Authentication with a combination of two or more of these factors is called "multi-factor authentication". Therefore, the frequently asked "password + secret question" are both "knowledge elements", so they are single-factor authentication.

Also, since the frequently heard "two-step verification" does not touch on the elements just because there are stages in the authentication phase, it seems that the security is not strong in the case of two-step verification using a single factor. However, in many cases, it seems to indicate stepwise authentication with two or more factors.

About authentication method

I would like to review the TOTP (Time-based One Time Password) covered in this article.

TOTP is a method that uses ** time ** as a generation element when generating a one-time password. The technical specifications are defined in RFC6238.

Both the authentication requesting side (called the client) and the authentication performing side (called the server) generate a one-time password that is valid for a certain period of time from the time based on Unix Epoch. , The validity is verified by matching the one-time passwords generated by the client and the server respectively. In addition, it is not "one-time only" because it is valid for a certain period of time. If you do not take measures, it will be effective as many times as you like within a certain period of time. This is said to be the weakness of the TOTP method.

In addition to time, the ** common key ** issued by the server to the client in advance is used to generate the one-time password. Taking Google Authenticator as an example, when enabling multi-factor authentication for a web service, the QR code issued by the web service is first read by Google Authenticator. This QR code consists of a URI that conforms to the Google Authenticator specification, but it contains a common key. This stores the common key on both the web service (server) and the Google Authenticator (client). It is important to note here that the QR code contains a common key, so if it is stolen, it will be possible for someone else to authenticate. Therefore, the screen displaying the QR code should not be stolen by others.

About the valid time of the one-time password

From the conclusion, it seems that it is often 30 seconds. This is also written in RFC6238 as follows, and it seems that the balance between security and usability is good.

We RECOMMEND a default time-step size of 30 seconds. This default value of 30 seconds is selected as a balance between security and usability.

This 30 seconds is also called ** step **. Unix Epoch is used to calculate the steps. This means that the same one-time password will be generated on any device at the same time. It is not affected by time zone or other factors.

However, if the client is a terminal such as a "one-time password generator" that cannot obtain the correct time via the Internet, such as a smartphone, it is necessary to consider the phenomenon that the time gradually shifts. Therefore, it seems that the step used for verification is not exactly that step, but a policy such as allowing one step before and after is often decided. The library used this time is designed to allow "one step before and after", so the actual verification is as follows.

time one-time password Verification timing inspection result
00:00:00 ~ 00:00:29 111111 NG
00:00:30 ~ 00:00:59 222222 OK
00:01:00 ~ 00:01:29 333333 Verify here OK
00:01:30 ~ 00:01:59 444444 OK
00:02:00 ~ 00:02:29 555555 NG

About Google Authenticator

Check the specifications of Google Authenticator. Details can be found on GitHub, so I'll take a look at the important points in the implementation.

--Android / iOS compatible --Supports reading QR code --Read a URI format like ʻotpauth: //totp/ServiceName: [email protected]? secret = JBSWY3DPEHPK3PXP & issuer = ServiceName -ServiceName:[email protected] --A label indicating the account name associated with the service, which is displayed on Google Authenticator. --The delimiter is:(colon), so you cannot include: in the preceding and following elements. --secret (required) --A base32 encoded string with a common key --ʻIssuer (highly recommended) --The service name, preferably the same as the label ServiceName --One-time passwords are valid for 30 seconds

Implementation

Library introduction

GoogleAuth

I used GoogleAuth, which wraps the complicated processing required for one-time password generation. I think that there is no problem just reading the README for normal use.

Since it was a Maven project, I added the following to pom.xml. It seems that it also supports Gradle.

pom.xml


<dependency>
    <groupId>com.warrenstrange</groupId>
    <artifactId>googleauth</artifactId>
    <version>1.1.5</version>
</dependency>

jquery-qrcode

I used jquery-qrcode to generate the QR code. It is output as HTML5 canvas by JavaScript, so I am grateful that there is no need to manage image files.

Also, as you can guess from the name, jQuery is required. The main reason for the selection is that the project originally used jQuery, so I don't think it is necessary to introduce jQuery for this purpose.

This is also described in the README. I cloned the GitHub repository, copied the js file to the project package, and did the following:

sample.html


<script type="text/javascript" src="/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="/jquery.qrcode.min.js"></script>

Setting function

Case to enable

flow

sample.png

One thing to keep in mind is that user operations are entered in the middle of the flow. I think it is preferable to design it so that the user does not expect it to operate as expected. Therefore, instead of enabling it immediately after receiving an activation request, the flow is such that it is enabled when it is confirmed that the user has performed the expected operation.

code

Common key issuance

Issue the common key as follows. The common key issued here will be used later, so keep it in a session variable.

Sample.java


GoogleAuthenticator gAuth = new GoogleAuthenticator();
GoogleAuthenticatorKey key = gAuth.createCredentials();
String secret = key.getKey();

It also generates a URI that includes the issued common key. Set serviceName and ʻuserIdas appropriate. (Note that neither can include:`)

Sample.java


String uri = "otpauth://totp/" + serviceName + ":" + userId + "?secret=" + secret + "&issuer=" + serviceName;
QR code display

Make the URI including the common key into a QR code as follows. The library also has a Sample (https://github.com/jeromeetienne/jquery-qrcode/blob/master/examples/basic.html).

sample.html


<body>
<div id="qrcode"></div>
<script type="text/javascript" src="/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="/jquery.qrcode.min.js"></script>
<script>
$(function() {
    $('#qrcode').qrcode(
        {
            width: 150,
            height: 150,
            text: '{Generated URI}'
        }
    );
});
</script>
</body>
One-time password verification

Set up a one-time password input form on the QR code display screen. Prompt the user to read the QR code with Google Authenticator and then enter the generated one-time password. With this one-time password verification, we assume that the user has successfully registered with Google Authenticator and enable multi-factor authentication for that user.

Verify as follows.

Sample.java


String secret = {Common key held in the previous flow};
int verificationCode = Integer.parseInt({Entered one-time password});
GoogleAuthenticator gAuth = new GoogleAuthenticator();
boolean isCodeValid = gAuth.authorize(secret, verificationCode);

If the verification result is correct, save the common key in a persistent area such as DB by associating it with the user.

Backup code issuance

Since Google Authenticator is a smartphone app, it is effective to issue a backup code that can be used instead of the one-time password in case the smartphone becomes unusable, that is, the one-time password cannot be issued.

The library will issue 5 random integers if you do the following:

Sample.java


GoogleAuthenticatorKey key = gAuth.createCredentials();
List<Integer> scratchCodes = key.getScratchCodes();

Let's save this together with the common key in the persistent area.

Case to disable

There is nothing special to do with the library. All you have to do is delete the common key and backup code stored in the persistent area.

Authentication function

flow

sample2.png

code

One-time password verification

Do the same as the validation you did when you enabled it in the settings feature. At this point, I think that the common key is in the DB etc., so verify it using the entered one-time password.

String secret = {Common key obtained from DB etc.};
int verificationCode = Integer.parseInt({Entered one-time password});
GoogleAuthenticator gAuth = new GoogleAuthenticator();
boolean isCodeValid = gAuth.authorize(secret, verificationCode);

In addition, it seems that it is desirable for security to verify the one-time password at the same time as the password and before the password, not in the stepwise verification. I referred to the article here.

Summary

When I heard about multi-factor authentication, I got the impression that development was difficult due to the troublesome settings, but I found that if there was a library that supports the language, it would be easy to introduce. Also, by actually introducing it, it was a big harvest that I became able to understand how it works even when I set it when using the service. I would like to make use of this experience when developing the next service.

Recommended Posts

The story of introducing a multi-factor authentication function using a one-time password into a Java application
The story of creating a database using the Google Analytics API
The story of seeing painful eyes when looking sweet around authentication (try introducing authentication using django-allauth)
Finding the optimum value of a function using a genetic algorithm (Part 1)
The story of remounting the application server
The story of writing a program
The story of having a hard time introducing OpenCV with M1 MAC
The story of developing a web application that automatically generates catchphrases [MeCab]
What Java users thought of using the Go language for a day
A memorandum of using Python's input function
The story of blackjack A processing (python)
The story of making a web application that records extensive reading with Django
A story of using rembg to make a white background + person video into a black background video
Get the caller of a function in Python
The story of making a lie news generator
The story of making a mel icon generator
This is a sample of function application in dataframe.
The story of launching a Minecraft server from Discord
A story about using Resona's software token with 1Password
# Function that returns the character code of a string
The story of making a music generation neural network
Drawing on Jupyter using the plot function of pandas
A story about changing the master name of BlueZ
[Python] Mask the image into a circle using Pillow
Zip 4 Gbyte problem is a story of the past
A story that analyzed the delivery of Nico Nama.
The story of using circleci to build manylinux wheels