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.
It's a little off topic, but authentication has three components in the first place.
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.
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.
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 |
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
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>
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.
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;
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>
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.
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.
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.
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.
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