암호가 필요없는 로그인 API 구현


목표

브라우저에서 별도 앱 설치 필요없이 최대한 간단하게 암호없는 로그인 시스템의 API까지 구현해봄

이전 글에서 언급한 로그인 시스템을 실제 API 구현까지 다뤄본다.

제한

  1. 현재는 구현의 편의상 ECDSA secp256r1 (NIST P-256)만 지원하게 해 뒀지만, 설계상 다른 공개키 알고리즘도 얼마든지 사용은 가능하다.
  2. 0-RTT를 위해 따로 서버에서 Challenge를 주는게 아니라 epoch millisecond를 body로 해서 서명하게 해버렸다. 일정 시간이 지나면 expire하도록 했지만 해당 시간 내에는 replay attack이 가능하다는 문제가 있긴 한데… TLS가 다 막아주시겠지?

API

loginSsh(
  timestamp: Int!
  sign: String!
): Boolean!

loginJwk(
  timestamp: Int!
  sign: String!
): Boolean!

addSsh(publicKey: String!): Boolean!

addJwk(jwk: String!): Boolean!

loginXXX API

로그인을 수행한다. loginSsh의 경우 OpenSSH에서 정의한 서명 포맷을 그대로 사용했기 때문에, 서명 안에 공개키가 그대로 들어있어서 사용이 간편하다. (서명 크기는 좀 커지지만…)

loginJwk의 경우, 공개키를 따로 입력으로 받았을 수도 있지만, 그냥 ECDSA public key recovery를 해서 입력값 / 서명값으로부터 공개키를 역산하는 방식을 해 봤다. 다만 recovery param을 따로 받지 않았기 때문에 가능한 공개키 값이 두개 계산되는데, 현재는 둘 중 아무거나 걸리면 로그인 성공으로 간주하도록 간단하게 구현했다. 공개키 충돌 같은 희박한 사건은 일단 생각하지 말자.

addXXX API

로그인된 유저의 경우 로그인을 추가한다. 그냥 public key를 serialize한 값을 새로 추가하는 것.

데이터베이스 설계

그냥 ssh키, jwk키를 모두 테이블에 때려박고, public key에 해당하는 부분을 unique 인덱스로 설정해서 해당값으로 검색을 수행할 수 있게 했다. 사실 현재는 ECDSA만 지원하고 있기 때문에 공개키가 사실 Curve의 X, Y 좌표인데, 이 중 X 값만 보관해도 충분한 것으로 알고 있다.