hangum / TadpoleForDBTools

https://tadpolehub.com
538 stars 148 forks source link

이런기능은 어떨까요? two factor 인증 #325

Closed stariki closed 10 years ago

stariki commented 10 years ago

올챙이가 강력할 수록 보안이슈도 커질 것 같습니다.

관리자 로긴시 two factor인증을 도입하시는 것은 어떨까요? 비밀번호 이외에 one time password를 입력받아 사용자를 인증하는 거죠. 공개된 알고리즘을 사용하면 될 듯 싶습니다. (RFC 6238) 스마트폰 앱으로 많이 있으니, 사용자는 그냥 설치해서 사용하면 되고요. (ex 구글 OTP) 올챙이서버에서

어떨까요?

hangum commented 10 years ago

현재 올챙이 비번을 잃어 버렸을 경우에대비한 기능이 갖추어져있습니다. 필요한 기능이긴 한데.. 다른 이슈가 많아 조금 미뤄서 처리 해야할 듯해요.

감사해요.

stariki commented 10 years ago

"올챙이로 100대서버 관리/접속 하는데, 올챙이가 뚫리면 100대가 뚫리는 것 아니야?" 라는 걱정/우려가 있을 수 있다고 생각합니다.

올챙이의 비약적인 성장을 대비해, 다다다음~에는 넣어주셨으면 합니다. 감사합니다.

p.s 비번 분실시, 질문에 대한 답변이 개인적인 내용이 될 수 도 있다고 생각합니다. 평문보다는 해쉬(message digest)해서 저장해주시는 것이 좋지 않을까 싶습니다.

hangum commented 10 years ago

아네. 거기까지는 생각을 못했네요. 좀더 진지하게 고민해 보겠습니다. 감사합니다.

hangum commented 10 years ago

비번관련 부분은 수정되었습니다.

stariki commented 10 years ago

작업해주신다고 하셔서, qrcode 쪽 링크 자료 올립니다.

1) qrcode에 들어가는 내용 (text 포멧) https://code.google.com/p/google-authenticator/wiki/KeyUriFormat

2) 샘플처럼 아래 내용을 qrcode에 넣으면 "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"

image

3) 스마트폰에 구글OTP 앱 설치 후, 계정추가 메뉴에서...

4) 서버 검증은 아래 부분 활용..

http://tools.ietf.org/html/rfc6238 구현된 소스 조금 수정했습니다.

import java.lang.reflect.UndeclaredThrowableException;
import java.security.GeneralSecurityException;
import java.util.Date;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;

public class TOTP {

    private TOTP() {}

    private static byte[] hmac_sha(String crypto, byte[] keyBytes,
            byte[] text){
        try {
            Mac hmac;
            hmac = Mac.getInstance(crypto);
            SecretKeySpec macKey =
                new SecretKeySpec(keyBytes, "RAW");
            hmac.init(macKey);
            return hmac.doFinal(text);
        } catch (GeneralSecurityException gse) {
            throw new UndeclaredThrowableException(gse);
        }
    }

    private static byte[] hexStr2Bytes(String hex){
        byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
        byte[] ret = new byte[bArray.length - 1];
        for (int i = 0; i < ret.length; i++)
            ret[i] = bArray[i+1];
        return ret;
    }

    private static final int[] DIGITS_POWER
    // 0 1  2   3    4     5      6       7        8
    = {1,10,100,1000,10000,100000,1000000,10000000,100000000 };

    private static byte[] longToByteArray(long n)
    {
           byte[] b = new byte[8];
           b[7] = (byte) (n);
           n >>>= 8;
           b[6] = (byte) (n);
           n >>>= 8;
           b[5] = (byte) (n);
           n >>>= 8;
           b[4] = (byte) (n);
           n >>>= 8;
           b[3] = (byte) (n);
           n >>>= 8;
           b[2] = (byte) (n);
           n >>>= 8;
           b[1] = (byte) (n);
           n >>>= 8;
           b[0] = (byte) (n);

           return b;
    }

    public static String generateTOTP(String key,
            String returnDigits
            ){
        int codeDigits = Integer.decode(returnDigits).intValue();
        String result = null;

        // Get the HEX in a Byte[]
//        byte[] msg = hexStr2Bytes(time);  
        byte[] msg = longToByteArray(new Date().getTime()/(30*1000));   // 30초 주기로 생성..
        byte[] k = hexStr2Bytes(key);

        byte[] hash = hmac_sha("HmacSHA1", k, msg);

        int offset = hash[hash.length - 1] & 0xf;

        int binary =
            ((hash[offset] & 0x7f) << 24) |
            ((hash[offset + 1] & 0xff) << 16) |
            ((hash[offset + 2] & 0xff) << 8) |
            (hash[offset + 3] & 0xff);

        int otp = binary % DIGITS_POWER[codeDigits];

        result = Integer.toString(otp);
        while (result.length() < codeDigits) {
            result = "0" + result;
        }
        return result;
    }

    public static void main(String[] args) {

        String key = "48656C6C6F21DEADBEEF";   // "JBSWY3DPEHPK3PXP" secret base32 decoding
        System.out.println(generateTOTP(key, "6"));  //  구글 OTP 앱에서 표시되는 값과 같아야...

      }
}

생성알고리즘이 시간 기반인 경우, 스마트폰 시간 오차가 있으니 이점 감안해주셔야 합니다.

이상입니다. 감사합니다.

hangum commented 10 years ago

https://github.com/wstrange/GoogleAuth 를 이용하였습니다.

예상 시나리오는 다음과 같습니다.

  1. 사용자 등록시 OTP 옵션을 추가하구요. (디비에는 otp, secret key 컬럼을 추가)
  2. 로그인시 OTP 가 되어 있다면 ID/PASS 입력시 OTP 입력 창이 나와서 입력하도록 합니다.