태그 보관물: reverse-engineering

reverse-engineering

애플리케이션에서 개인 API 키를 저장하고 보호하는 모범 사례 [닫기]

대부분의 앱 개발자는 일부 타사 라이브러리를 앱에 통합합니다. Dropbox 또는 YouTube와 같은 서비스에 액세스하거나 로깅이 충돌하는 경우. 타사 라이브러리 및 서비스의 수는 엄청납니다. 이러한 라이브러리와 서비스는 대부분 서비스를 인증함으로써 통합되며, 대부분 API 키를 통해 이루어집니다. 보안 목적으로 서비스는 일반적으로 공개 및 개인을 생성하며, 종종 비밀 키라고도합니다. 불행히도 서비스에 연결하려면이 개인 키를 인증에 사용해야하므로 응용 프로그램의 일부일 수 있습니다. 말할 것도없이, 이것은 엄청난 보안 문제에 직면 해 있습니다. 공개 및 개인 API 키는 몇 분 만에 APK에서 추출 할 수 있으며 쉽게 자동화 할 수 있습니다.

이와 비슷한 것이 있다고 가정하면 비밀 키를 어떻게 보호 할 수 있습니까?

public class DropboxService  {

    private final static String APP_KEY = "jk433g34hg3";
    private final static String APP_SECRET = "987dwdqwdqw90";
    private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;

    // SOME MORE CODE HERE

}

개인 키를 저장하는 가장 안전하고 안전한 방법은 무엇입니까? 난독 화, 암호화, 어떻게 생각하십니까?



답변

  1. 컴파일 된 응용 프로그램에는 키 문자열뿐만 아니라 상수 이름 APP_KEY 및 APP_SECRET도 포함되어 있습니다. 이러한 자체 문서화 코드에서 키를 추출하는 것은 간단합니다 (예 : 표준 Android 도구 dx).

  2. ProGuard를 적용 할 수 있습니다. 키 문자열은 그대로 두지 만 상수 이름은 제거합니다. 또한 가능하면 짧고 의미없는 이름으로 클래스와 메서드의 이름을 바꿉니다. 그런 다음 키를 추출하는 데 어느 문자열이 어떤 목적을 달성하는지 알아 내기 위해 시간이 더 걸립니다.

    ProGuard 설정은 두려워하는 것처럼 어렵지 않아야합니다. 우선 project.properties에 설명 된대로 ProGuard 만 활성화하면됩니다. 타사 라이브러리에 문제가있는 경우 proguard-project.txt에서 일부 경고를 억제하거나 난독 화를 방지해야 할 수 있습니다. 예를 들어 :

    -dontwarn com.dropbox.**
    -keep class com.dropbox.** { *; }

    이것은 무차별 접근 방식입니다. 처리 된 응용 프로그램이 작동하면 이러한 구성을 세분화 할 수 있습니다.

  3. 예를 들어 Base64 인코딩 또는 더 복잡한 것으로 코드에서 문자열을 수동으로 난독 처리 할 수 ​​있습니다. 아마도 네이티브 코드 일 수도 있습니다. 그런 다음 해커는 인코딩을 정적으로 리버스 엔지니어링하거나 적절한 위치에서 디코딩을 동적으로 가로 채야합니다.

  4. ProGuard의 특수 형제 DexGuard 와 같은 상용 난독 화 장치를 적용 할 수 있습니다 . 문자열과 클래스를 추가로 암호화 / 난독 처리 할 수 ​​있습니다. 그런 다음 키를 추출하는 데 더 많은 시간과 전문 지식이 필요합니다.

  5. 자체 서버에서 응용 프로그램의 일부를 실행할 수 있습니다. 키를 보관할 수 있으면 안전합니다.

결국, 키가 얼마나 중요한지, 얼마나 많은 시간이나 소프트웨어를 감당할 수 있는지, 키에 관심이있는 해커가 얼마나 정교한 지, 얼마나 많은 시간을 원할 것인지는 경제적으로 절충해야합니다. 키를 해킹하기 전에 얼마나 많은 가치를 지니고 있는지, 성공적인 해커가 키를 배포하는 규모 등은 중요합니다. 키와 같은 작은 정보는 전체 응용 프로그램보다 보호하기가 더 어렵습니다. 본질적으로 클라이언트 측에서는 깨지지 않는 것이 없지만 막대를 올릴 수는 있습니다.

(저는 ProGuard 및 DexGuard의 개발자입니다)


답변

내 의견으로는 첫 번째 아이디어 만 보장 할 수있는 아이디어는 거의 없습니다.

  1. 인터넷상의 일부 서버에 비밀을 유지하고 필요할 때 간단히 사용하십시오. 사용자가 dropbox를 사용하려고 시도하면 사이트에 요청하고 비밀 키를 얻는 것을 막을 수있는 것은 없습니다.

  2. 비밀을 jni 코드에 넣고 변수 코드를 추가하여 라이브러리를 더 크고 더 디 컴파일하기 어렵게 만듭니다. 키 문자열을 몇 부분으로 나누고 여러 곳에 보관할 수도 있습니다.

  3. obfuscator를 사용하고 코드 해시 비밀을 넣은 다음 나중에 필요할 때 해시 해제하십시오.

  4. 비밀 키를 이미지 중 하나의 마지막 픽셀로 자산에 넣습니다. 그런 다음 필요할 때 코드에서 읽으십시오. 코드를 난독 처리하면 읽을 코드를 숨길 수 있습니다.

APK 코드를 읽는 것이 얼마나 쉬운 지 간략히 보려면 ​​APKAnalyser를 가져옵니다.

http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/


답변

다른 접근 방식은 처음에는 장치에 비밀을 두지 않는 것입니다! 모바일 API 보안 기술 (특히 3 부)을 참조하십시오 .

시간을 존중하는 전통적 전통을 사용하여 API 엔드 포인트와 앱 인증 서비스간에 비밀을 공유하십시오.

클라이언트는 API 호출을 원할 때 앱 인증 서비스에 강력한 원격 증명 기술을 사용하여이를 인증하도록 요청 하고 비밀로 서명 한 시간 제한 (보통 JWT ) 토큰을받습니다.

토큰은 요청을 수행하기 전에 엔드 포인트가 서명을 확인할 수있는 각 API 호출 과 함께 전송됩니다 .

실제 비밀은 장치에 존재하지 않습니다. 사실, 앱은 그것이 유효한지 아닌지 전혀 알지 못하며, 인증을 요청하고 결과 토큰을 전달합니다. 간접적 인 이점으로 비밀을 바꾸고 싶다면 사용자가 설치된 앱을 업데이트하지 않고도 변경할 수 있습니다.

따라서 비밀을 보호하려면 앱에서 처음에 비밀 키를 사용하지 않는 것이 좋습니다.


답변

오래된 보안되지 않은 방법 :

API / 비밀 키를 보호하는 간단한 3 단계 ( 구식 답변 )

Gradle을 사용하여 API 키 또는 비밀 키를 보호 할 수 있습니다.

1. gradle.properties (프로젝트 속성) : 키를 사용하여 변수를 만듭니다.

GoolgeAPIKey = "Your API/Secret Key"

2. build.gradle (모듈 : app) : build.gradle에 변수를 설정하여 액티비티 또는 프래그먼트에서 변수에 액세스합니다. buildTypes {}에 아래 코드를 추가하십시오.

buildTypes.each {
    it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}

3. 앱의 BuildConfig를 통해 Activity / Fragment에서 액세스하십시오.

BuildConfig.GoogleSecAPIKEY

업데이트 :

위의 솔루션은 오픈 소스 프로젝트에서 Git을 커밋하는 데 도움이됩니다. (David Rawson과 riyaz-ali의 의견에 감사드립니다).

Matthew와 Pablo Cegarra의 의견에 따르면 위의 방법은 안전하지 않으며 Decompiler는 누군가 비밀 키로 BuildConfig를 볼 수 있습니다.

해결책 :

NDK를 사용하여 API 키를 보호 할 수 있습니다. 네이티브 C / C ++ 클래스에 키를 저장하고 Java 클래스에서 액세스 할 수 있습니다.

NDK를 사용하여 API 키를 보호 하려면 블로그를 따르십시오 .

Android에서 토큰을 안전하게 저장하는 방법에 대한 후속 조치


답변

App-Secret 키는 비공개로 유지해야하지만 앱을 출시 할 때 일부 사용자가이를 취소 할 수 있습니다.

그 사람들을 위해 숨겨지지 않을 것입니다 ProGuard. 그것은 리 팩터이며 일부 유료 난 독자는 jk433g34hg3
문자열을 다시 얻기 위해 몇 가지 비트 연산자를 삽입하고 있습니다. 3 일 동안 일하면 해킹을 5-15 분 더 길게 만들 수 있습니다. 🙂

가장 좋은 방법은 그대로 유지하는 것입니다.

서버 측 (PC)에 저장하더라도 키를 해킹하여 인쇄 할 수 있습니다. 아마도 가장 오래 걸리나요? 어쨌든 최선의 경우 몇 분 또는 몇 시간이 걸립니다.

일반 사용자는 코드를 디 컴파일하지 않습니다.


답변

가능한 해결책 중 하나는 앱에서 데이터를 인코딩하고 런타임에 디코딩을 사용하는 것입니다 (해당 데이터를 사용하려는 경우). 또한 progaurd를 사용하여 앱의 디 컴파일 된 소스 코드를 읽고 이해하기 어렵게 만드는 것이 좋습니다. 예를 들어 앱에 인코딩 된 키를 넣은 다음 런타임시 비밀 키를 해독하기 위해 앱에서 디코딩 방법을 사용했습니다.

// "the real string is: "mypassword" "; 
//encoded 2 times with an algorithm or you can encode with other algorithms too
public String getClientSecret() {
    return Utils.decode(Utils
            .decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}

보호 된 앱의 디 컴파일 된 소스 코드는 다음과 같습니다.

 public String c()
 {
    return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
  }

적어도 그것은 나를 위해 충분히 복잡합니다. 이것은 선택의 여지가 없지만 내 응용 프로그램에 값을 저장할 때하는 방식입니다. 물론 우리 모두는 그것이 최선의 방법은 아니지만 나를 위해 일한다는 것을 알고 있습니다.

/**
 * @param input
 * @return decoded string
 */
public static String decode(String input) {
    // Receiving side
    String text = "";
    try {
        byte[] data = Decoder.decode(input);
        text = new String(data, "UTF-8");
        return text;
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return "Error";
}

디 컴파일 된 버전 :

 public static String a(String paramString)
  {
    try
    {
      str = new String(a.a(paramString), "UTF-8");
      return str;
    }
    catch (UnsupportedEncodingException localUnsupportedEncodingException)
    {
      while (true)
      {
        localUnsupportedEncodingException.printStackTrace();
        String str = "Error";
      }
    }
  }

Google에서 약간의 검색으로 많은 암호화 기 클래스를 찾을 수 있습니다.


답변

@Manohar Reddy 솔루션에 firebase 데이터베이스 또는 firebase RemoteConfig (Null 기본값 사용)를 추가 할 수 있습니다.

  1. 키를 암호화
  2. Firebase 데이터베이스에 저장
  3. 앱 시작 중 또는 필요할 때마다 가져 오기
  4. 해독 키를 사용

이 솔루션의 차이점은 무엇입니까?

  • 파이어베이스에 대한 자격 증명 없음
  • Firebase 액세스가 보호되므로 서명 된 인증서가있는 앱만 API 호출 권한을 갖습니다.
  • 중개인 차단을 방지하기위한 암호화 / 암호 해독 그러나 이미 https를 firebase로 호출합니다.