As of September 25, 2020, banks have been cracked for some reason, but none of them have authentication such as one-time passwords. Of course, if a one-time password is stolen "during that one-time", it will be out, but it is harder to steal the communication contents at all times.
However, the fact that the one-time password mechanism depends on an external mechanism such as Google Authenticator is a risk in another sense (such as sudden changes in specifications).
So, verify how much you can do on your own.
The following elements are combined to generate a digest (sha256), and 6 digits are extracted from the digest.
--Different private key for each user (client and server have the same private key) --Timestamp (This sample uses the integer part divided by 60 seconds) --Salt (long string held on the server side)
onetime.js
async function digestMessage(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hash = crypto.subtle.digest('SHA-256', data);
return hash;
}
function buf2hex(buffer) { // buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
async function onetimePass(secret_key){
const ts_min = parseInt( Date.now() / 60000 ).toString();
const buffer = await digestMessage(secret_key + '_and_sault' + ts_min);
const hexString = '0x' + await buf2hex(buffer) ;
const digitString = BigInt(hexString).toString(10);
return digitString.slice(-6);
}
onetime.html
<html>
<script type="text/javascript" src="./onetime.js"></script>
<body>
<script>
(async () => {
document.write(await onetimePass('secret_key'));
})();
</script>
</body>
</html>
onetime.rb
require 'digest/sha1'
def onetime_pass(secret_key, next_min = false)
#Round the timestamp from seconds to minutes
ts_min = Time.now.to_i / 60
ts_min += 1 if next_min #If you want to cover up to the next minute (the side receiving the check)
text = "#{secret_key}_and_sault"
Digest::SHA256.hexdigest("#{text}#{ts_min}").to_i(16).to_s[-6, 6]
end
#Assuming that it will span just one minute, we will be able to obtain the password after one minute.
puts onetime_pass('secret_key')
puts onetime_pass('secret_key', true)
You can see that the same 6 digits are created on the front side and the server side. (The second line on the server side is the password after 1 minute)
――If you write JS for the first time in a long time, no matter what you do, only promises will come back. I wrote async and await by trial and error, but I don't have much of myself. ――It seems that RFC also has the logic of a one-time password properly, but I'm done with "I'm sure this is the case" without reading it. ――I wrote it in JS on the terminal side, but the essential "how to pass the private key" is not mentioned in this article. It seems good to pass it with a QR code, but the program that reads the QR code and stores the private key in the local storage on the terminal side seems to be much longer than the above source. Considering that, Google Authenticator is used, and the library on the server side of Google Authenticator is used, so is it a solution? ――I made a digest with sha256, but when the last 6 digits are taken out in decimal, the collision probability increases.
Recommended Posts