js로 AWS를 생성한 MFA의 "totp.js"읽기
46413 단어 JavaScriptvanillajsTOTPtech
개시하다
외부 프로그램 라이브러리를 사용하지 않는 자바스크립트의 TOTP로 totp를 설치합니다.js를 만들었지만 너무 힘을 줘서 해설 기사를 쓰지 않으면 자신도 모르기 때문에 이곳에서 공양한다.
이 기사는 이른바'조직의 기술 전달 방법을 강화하다'이 말한'뒷면 설계도'다.(참고로 이 책은 엔지니어, 프로젝트 매니저, 제품 관리자, 폐품 관리자들에게 꼭 읽어야 할 책이다. 도서관에도 책이 많아서 한 시간 정도면 다 읽을 수 있을 것 같다.)
totp.js
//javascript:
(secret=>{
var b32=s=>[0,8,16,24,32,40,48,56]
.map(i=>[0,1,2,3,4,5,6,7]
.map(j=>s.charCodeAt(i+j)).map(c=>c<65?c-24:c-65))
.map(a=>[(a[0]<<3)+(a[1]>>2),
(a[1]<<6)+(a[2]<<1)+(a[3]>>4),
(a[3]<<4)+(a[4]>>1),
(a[4]<<7)+(a[5]<<2)+(a[6]>>3),
(a[6]<<5)+(a[7]>>0),
]).flat(),
trunc=dv=>dv.getUint32(dv.getInt8(19)&0x0f)&0x7fffffff,
c=Math.floor(Date.now()/1000/30);
crypto.subtle.importKey('raw',new Int8Array(b32(secret)),{name:'HMAC',hash:{name:'SHA-1'}},true,['sign'])
.then(k=>crypto.subtle.sign('HMAC',k,new Int8Array([0,0,0,0,c>>24,c>>16,c>>8,c])))
.then(h=>document.querySelector('#mfacode').value=('0'+trunc(new DataView(h))).slice(-6))
})('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
다중 요소 인증에 사용되는 QR 코드(예: AWS)에 다음 정보가 포함되어 있습니다.otpauth://TYPE/LABEL?secret=XXX&Issuer=YYY
이 시크릿을 추출하고 위에서 지정한 마지막 호출을 통해 TOTP의 6비트를 #mfacode에 입력합니다.책갈피를 설정하면'스마트폰을 시작하는 Google Authenticator, Issuer를 선택하고 눈으로 6자리 입력을 본다'는 번거로움이 없어진다.
실장 해설
그런 다음 설치에 대해 설명합니다.
책갈피 설명
#L1
//javascript:
책갈피 접두어.자바스크립트의 설치 자체를 책갈피에 넣고 한 페이지를 표시하는 상태부터 이 책갈피를 여는 동작을 통해 자바스크립트의 '책갈피' 를 실행할 수 있습니다 javascript:
.이 줄 자체는 이미 주석을 달았기 때문에 실현의 의미가 없지만 이것은 이 실현이 책갈피라는 것을 나타낸다.
지금 실행 공식
#L2,17
(secret=>{ /* ... */ })('XXXXX')
자바스크립트의 함수 정의문仮引数=>{関数定義}
뒤에 바로 (実引数)
을 붙여서 실제 매개 변수를 정의된 함수에 전달하여 실행합니다.const func = (secret => {}); func('XXXXX')
내연 전개 형식.function (仮引数) {}
의 양식으로 설명된 경우 함수 정의 이전(
을 추가하지 않으면 해석기는 함수 정의문으로 해석하지만 추가(
등을 통해 실시간 실행식으로 해석할 수 있다.+
와 !
부터 시작되는 의식도 있다.Immediately Invoked Function Expression; 지금 실행 공식
base 32 디코더 설치
#L3,11
var b32=s=>[0,8,16,24,32,40,48,56]
.map(i=>[0,1,2,3,4,5,6,7]
.map(j=>s.charCodeAt(i+j)).map(c=>c<65?c-24:c-65))
.map(a=>[(a[0]<<3)+(a[1]>>2),
(a[1]<<6)+(a[2]<<1)+(a[3]>>4),
(a[3]<<4)+(a[4]>>1),
(a[4]<<7)+(a[5]<<2)+(a[6]>>3),
(a[6]<<5)+(a[7]>>0),
]).flat(),
매개 변수로 수신된base 32 문자열을 정수의 배열로 되돌려줍니다.base32의 상세한 규격 참조rfc4648.간단하게 말하면 문자A
~Z
를 정수치 0~25, 문자2
~7
를 정수치 26~31로 고친 후 5자리로 표시된 문자를 연결하여 8자리에 따라 정수로 분할한다.8자마다 5바이트를 생성합니다.base 32 변환 테이블
문자
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
값(10진수)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
값(16진수)
00
01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F
문자
Q
R
S
T
U
V
W
X
Y
Z
2
3
4
5
6
7
값(10진수)
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
값(16진수)
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1D
1E
1F
변환 예:
NQC42JFR ULD4RJP7
0D 10 02 1C 1A 09 05 11 | 14 0B 03 1C 11 09 0F 1F
01101 10000 00010 11100 11010 01001 00101 10001 | 10100 01011 00011 11100 10001 01001 01111 11111
01101100 00000101 11001101 00100100 10110001 | 10100010 11000111 11001000 10100101 11111111
6C 05 CD 24 B1 A2 C7 C8 A5 FF
#L3,5
s=>
[0,8,16,24,32,40,48,56].map(i=>[0,1,2,3,4,5,6,7].map(j=>s.charCodeAt(i+j))
.map(c=>c<65?c-24:c-65)
)
8글자마다 "0글자에서 7글자를 꺼내 변환표로 변환합니다."생성 구조
[ [0D, 10, 02, 1C, 1A, 09, 05, 11], [14, 0B, 03, 1C, 11, 09, 0F, 1F], ...]
의 결과.#L6,9
a=>[(a[0]<<3)+(a[1]>>2),
(a[1]<<6)+(a[2]<<1)+(a[3]>>4),
(a[3]<<4)+(a[4]>>1),
(a[4]<<7)+(a[5]<<2)+(a[6]>>3),
(a[6]<<5)+(a[7]>>0),]
수신 길이 8의 진열[0D, 10, 02, 1C, 1A, 09, 05, 11]
을 8비트 단위로 다시 구분[6C, 405, 1CD, D24, B1]
하여 길이가 5의 진열을 형성한다..flat()
[ [6C, 405, 1CD, D24, B1], [A2, 2C7, 1C8, 8A5, 1FF], ...]
의 배열[ 6C, 405, 1CD, D24, B1, A2, 2C7, 1C8, 8A5, 1FF, ...]
의 평평한 모양으로 변했다.flat()
는 ECMAScript 2019에 추가된 기능입니다.(잠시 휴식)
[0,1,2,3,4,5,6,7].map()
이런 건 믿음직하지 않다?+
아닌?TOTP 설치
TOTP(Time-Based One-Time Password)로서 RFC 6238과 RFC 4226이 정의한 것을 설치합니다.
#L12,18
trunc=dv=>dv.getUint32(dv.getInt8(19)&0x0f)&0x7fffffff,
c=Math.floor(Date.now()/1000/30);
crypto.subtle.importKey('raw',new Int8Array(b32(secret)),{name:'HMAC',hash:{name:'SHA-1'}},true,['sign'])
.then(k=>crypto.subtle.sign('HMAC',k,new Int8Array([0,0,0,0,c>>24,c>>16,c>>8,c])))
.then(h=>document.querySelector('#mfacode').value=('0'+trunc(new DataView(h))).slice(-6))
rfc6238 TOTP = HOTP(K, T)
T = (Current Unix time - T0)/X
T0의 값은 0입니다.X의 값은 30입니다.
rfc4226
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
K는 공유 키
C는 카운터(변경된 값)
Step 1: HS = HMAC-SHA-1(K,C) ... HS가 20바이트인 해시 값
Step2-1: HS의 마지막 4자리 꺼내기 → 오프셋 0~15
Step2-2: HS의 오프셋 바이트에서 4바이트 꺼내기
Step2-3: 하위 31비트 제거
3단계: 10진수 및 6자 분리
설치 순서를 살펴보겠습니다.
#L12
var trunc=dv=>dv.getUint32(dv.getInt8(19)&0x0f)&0x7fffffff;
|
... 2-1단계: HS 마지막 4비트 체크 아웃_offset_ = dv.getInt8(19)&0x0f
... Step2-2: HS의 오프셋 바이트에서 4바이트(32비트) 추출dv.getUint32( _offset_ )
... 2-3단계: 하위 31비트 꺼내기var c=Math.floor(Date.now()/1000/30);
TOTP에서 사용되는 카운터가 계산됩니다.&0x7fffffff
의 밀리초를 초로 환산하여 30을 나눈다.#L14,15
crypto.subtle.importKey('raw',new Int8Array(b32(secret)),
{name:'HMAC',hash:{name:'SHA-1'}},true,['sign'])
.then(k=>crypto.subtle.sign('HMAC',k,new Int8Array([0,0,0,0,c>>24,c>>16,c>>8,c])))
HMAC-SHA-1은 내장형 라이브러리Web Crypto API를 사용하여 계산할 수 있습니다.우선 HMAC-SHA-1용 크립토키SubtleCrypto.importKey()를 사용하고SubtleCrypto.sign() 이 크립토키를 먹게 함으로써 계산 결과를 얻는다.두 함수 모두 Promise로 되돌아오기 때문에 결과를 수신하려면
Date.now()
를 사용해야 한다.#L16
.then(h=>document.querySelector('#mfacode').value=('0'+trunc(new DataView(h))).slice(-6))
then에서 계산 결과를 받아서 6개의 문자를 추출하여 mfacode라는 ID를 가진 DOM 요소에 저장합니다.간혹 자릿수가 부족한 경우가 있기 때문에 머리에 문자.then()
를 붙여 보충한다.수확하다.
끝말
여기까지 읽어줘서 고마워요.
질문이나 지적이 있으면 가볍게 댓글로 남겨주세요.여러분의 소감을 환영합니다.
Reference
이 문제에 관하여(js로 AWS를 생성한 MFA의 "totp.js"읽기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/matobaa/articles/114449b0213daf텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)