애플리케이션에서 개인 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
}
개인 키를 저장하는 가장 안전하고 안전한 방법은 무엇입니까? 난독 화, 암호화, 어떻게 생각하십니까?
컴파일 된 응용 프로그램에는 키 문자열뿐만 아니라 상수 이름 APP_KEY 및 APP_SECRET도 포함되어 있습니다. 이러한 자체 문서화 코드에서 키를 추출하는 것은 간단합니다 (예 : 표준 Android 도구 dx).
ProGuard를 적용 할 수 있습니다. 키 문자열은 그대로 유지하지만 상수 이름은 제거합니다. 또한 가능하면 짧고 의미없는 이름으로 클래스와 메서드의 이름을 바꿉니다. 그런 다음 키를 추출하는 데 어느 문자열이 어떤 목적을 달성하는지 알아 내기 위해 시간이 더 걸립니다.
ProGuard 설정은 두려워하는 것처럼 어렵지 않아야합니다. 우선 project.properties에 설명 된대로 ProGuard 만 활성화하면됩니다. 타사 라이브러리에 문제가있는 경우 proguard-project.txt에서 일부 경고를 억제하거나 난독 화되지 않도록해야 할 수 있습니다. 예를 들어 :
-dontwarn com.dropbox.** -keep class com.dropbox.** { *; }
이것은 무차별 접근 방식입니다. 처리 된 응용 프로그램이 작동하면 이러한 구성을 세분화 할 수 있습니다.
예를 들어 Base64 인코딩 또는 더 복잡한 것으로 코드에서 문자열을 수동으로 난독 처리 할 수 있습니다. 아마도 네이티브 코드 일 수도 있습니다. 그런 다음 해커는 인코딩을 정적으로 리버스 엔지니어링하거나 적절한 위치에서 디코딩을 동적으로 차단해야합니다.
ProGuard의 특수 형제 DexGuard 와 같은 상용 난독 화 장치를 적용 할 수 있습니다 . 문자열과 클래스를 추가로 암호화 / 난독 처리 할 수 있습니다. 그런 다음 키를 추출하는 데 더 많은 시간과 전문 지식이 필요합니다.
자체 서버에서 응용 프로그램의 일부를 실행할 수 있습니다. 키를 보관할 수 있으면 안전합니다.
결국, 그것은 당신이해야 할 경제적 절충입니다. 키가 얼마나 중요한지, 얼마나 많은 시간이나 소프트웨어를 감당할 수 있는지, 키에 관심이있는 해커가 얼마나 정교하고, 얼마나 많은 시간을 원할 것인지 키를 해킹하기 전에 얼마나 많은 가치를 지니고 있는지, 성공적인 해커가 키를 배포하는 규모 등은 중요합니다. 키와 같은 작은 정보는 전체 응용 프로그램보다 보호하기가 더 어렵습니다. 본질적으로 클라이언트 측에서는 깨지지 않는 것이 없지만 막대를 올릴 수는 있습니다.
(저는 ProGuard 및 DexGuard의 개발자입니다)
내 의견으로는 첫 번째 아이디어 만 보장 할 수있는 아이디어는 거의 없습니다.
인터넷상의 일부 서버에 비밀을 유지하고 필요할 때 간단히 사용하십시오. 사용자가 dropbox를 사용하려고 시도하면 사이트에 요청하고 비밀 키를 얻는 것을 막을 수있는 것은 없습니다.
비밀을 jni 코드에 넣고 변수 코드를 추가하여 라이브러리를 더 크고 더 디 컴파일하기 어렵게 만듭니다. 키 문자열을 몇 부분으로 나누고 여러 곳에 보관할 수도 있습니다.
obfuscator를 사용하고 코드 해시 비밀을 넣은 다음 나중에 필요할 때 해시 해제하십시오.
비밀 키를 이미지 중 하나의 마지막 픽셀로 자산에 넣습니다. 그런 다음 필요할 때 코드에서 읽으십시오. 코드를 난독 처리하면 읽을 코드를 숨길 수 있습니다.
APK 코드를 읽는 것이 얼마나 쉬운 지 간략히 보려면 APKAnalyser를 가져옵니다.
http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/
다른 접근 방식은 처음에는 장치에 비밀을 두지 않는 것입니다! 모바일 API 보안 기술 (특히 3 부)을 참조하십시오 .
시간을 존중하는 전통적 전통을 사용하여 API 엔드 포인트와 앱 인증 서비스간에 비밀을 공유하십시오.
클라이언트가 API 호출을 원할 때 앱 인증 서비스에 강력한 원격 증명 기술을 사용하여이를 인증하도록 요청 하고 비밀로 서명 된 시간 제한 (보통 JWT ) 토큰을받습니다.
토큰은 요청을 수행하기 전에 엔드 포인트가 서명을 확인할 수있는 각 API 호출 과 함께 전송됩니다 .
실제 비밀은 장치에 존재하지 않습니다. 사실, 앱은 그것이 유효한지 아닌지 전혀 알지 못하며, 인증을 요청하고 결과 토큰을 전달합니다. 간접적 인 이점으로 비밀을 바꾸고 싶다면 사용자가 설치된 앱을 업데이트하지 않고도 변경할 수 있습니다.
따라서 비밀을 보호하려면 앱에서 비밀을 지키지 않는 것이 좋습니다.
간단한 3 단계를 따라 API / 비밀 키 보안
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에서 토큰을 안전하게 저장하는 방법에 대한 후속 조치
가능한 솔루션 중 하나는 앱에서 데이터를 인코딩하고 런타임에 디코딩을 사용하는 것입니다 (해당 데이터를 사용하려는 경우). 또한 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 기본값 사용)를 추가 할 수 있습니다.
- 키를 암호화
- Firebase 데이터베이스에 저장
- 앱 시작 중 또는 필요할 때마다 가져 오기
- 해독 키를 사용
이 솔루션의 차이점은 무엇입니까?
- 파이어베이스에 대한 자격 증명 없음
- Firebase 액세스가 보호되므로 서명 된 인증서가있는 앱만 API 호출 권한을 갖습니다.
- 중개인 차단을 방지하기위한 암호화 / 암호 해독 그러나 이미 https를 firebase로 호출합니다.
App-Secret 키는 비공개로 유지해야하지만 앱을 출시 할 때 일부 사용자가이를 취소 할 수 있습니다.
그 사람들을 위해 그것은 숨겨지지 않을 것입니다 ProGuard
. 그것은 리 팩터이며 일부 유료 난 독자는 jk433g34hg3
문자열을 다시 얻기 위해 몇 가지 비트 연산자를 삽입하고 있습니다. 3 일 동안 일하면 해킹을 5-15 분 더 길게 만들 수 있습니다. :)
가장 좋은 방법은 그대로 유지하는 것입니다.
서버 측 (PC)에 저장하더라도 키를 해킹하여 인쇄 할 수 있습니다. 아마도 가장 오래 걸리나요? 어쨌든 최선의 경우 몇 분 또는 몇 시간이 걸립니다.
일반 사용자는 코드를 디 컴파일하지 않습니다.
이 예제에는 여러 가지 측면이 있습니다. 다른 곳에서는 명시 적으로 다루지 않았다고 생각되는 몇 가지 사항에 대해 언급하겠습니다.
운송시 비밀 보호
가장 먼저 알아야 할 것은 앱 인증 메커니즘을 사용하여 dropbox API에 액세스 하려면 키와 비밀을 전송해야한다는 것입니다. 연결은 HTTPS이므로 TLS 인증서를 모르면 트래픽을 가로 챌 수 없습니다. 이것은 사람이 모바일 장치에서 서버로 이동하는 동안 패킷을 가로 채서 읽는 것을 방지하기위한 것입니다. 일반 사용자에게는 트래픽의 프라이버시를 보장하는 정말 좋은 방법입니다.
좋지 않은 것은 악의적 인 사람이 앱을 다운로드하지 못하게하고 트래픽을 검사하는 것입니다. 모바일 장치로 들어오고 나가는 모든 트래픽에 Man-in-the-Middle 프록시를 사용하는 것은 정말 쉽습니다. Dropbox API의 특성으로 인해이 경우 앱 키와 비밀을 추출하기 위해 코드를 분해하거나 리버스 엔지니어링 할 필요가 없습니다.
서버에서 수신 한 TLS 인증서가 원하는 인증서인지 확인하는 고정 을 수행 할 수 있습니다. 이렇게하면 클라이언트에 검사가 추가되고 트래픽을 가로 채기가 더 어려워집니다. 이로 인해 비행 중 트래픽을 검사하기가 어려워 지지만 클라이언트에서 피닝 검사가 수행되므로 피닝 테스트를 비활성화 할 수 있습니다. 그래도 더 어렵게 만듭니다.
휴식시 비밀 보호
첫 번째 단계로, proguard 와 같은 것을 사용 하면 비밀이 어디에 있는지 명확하게 알 수 없습니다. 또한 NDK를 사용하여 키와 비밀을 저장하고 직접 요청을 보낼 수 있으므로 정보를 추출하는 적절한 기술을 가진 사람의 수가 크게 줄어 듭니다. 값을 메모리에 직접 저장하지 않으면 더 난독 화를 달성 할 수 있습니다. 다른 답변에서 제안한대로 사용 직전에 값을 암호화하고 해독 할 수 있습니다.
고급 옵션
이제 앱의 어느 곳에 나 비밀을 두는 것에 대해 편집증이 있고 더 포괄적 인 솔루션에 투자 할 시간과 돈이 있다면 자격 증명을 서버에 저장하는 것이 좋습니다 (있는 경우). 이렇게하면 서버를 통해 통신해야하므로 API 호출에 대한 대기 시간이 길어지고 데이터 처리량이 증가하여 서비스 실행 비용이 증가 할 수 있습니다.
그런 다음 서버를 보호하기 위해 서버와 가장 잘 통신하는 방법을 결정해야합니다. 내부 API에서 동일한 문제가 다시 발생하지 않도록하는 것이 중요합니다. 내가 제공 할 수있는 가장 좋은 방법은 중간자 위협으로 인해 비밀을 직접 전송하지 않는 것입니다. 대신 비밀을 사용하여 트래픽에 서명하고 서버로 들어오는 모든 요청의 무결성을 확인할 수 있습니다. 이를 수행하는 한 가지 표준 방법은 비밀 키를 사용하여 메시지의 HMAC를 계산하는 것입니다. 저는이 분야에서도 운영되는 보안 제품을 보유한 회사에서 일하고 있습니다. 이런 종류의 것들이 저에게 관심이있는 이유입니다. 사실, 여기에 나와있는 동료 중 한 사람의 블로그 기사가 있습니다.
얼마나해야합니까?
이와 같은 보안 조언을 사용하면 누군가 침입하기가 얼마나 어려운지에 대한 비용 / 혜택 결정을 내려야합니다. 수백만 고객을 보호하는 은행 인 경우 예산이 앱을 지원하는 사람과는 완전히 다릅니다 여가 시간. 누군가가 보안을 깨뜨리는 것을 막는 것은 사실상 불가능하지만 실제로는 종과 휘파람을 모두 필요로하는 사람이 거의 없으며 기본적인 예방책으로 먼 길을 갈 수 있습니다.
가장 안전한 솔루션은 키를 서버에 유지하고 해당 키가 필요한 모든 요청을 서버를 통해 라우팅하는 것입니다. 그렇게하면 서버가 안전한 한 키가 서버를 떠나지 않으므로 키도 서버를 떠나지 않습니다. 물론이 솔루션에는 성능 비용이 있습니다.
비밀 키를 보호하기 위해 무엇을하더라도 실제 솔루션은 아닙니다. 개발자가 응용 프로그램을 디 컴파일 할 수있는 경우 키를 보호 할 수있는 방법이 없습니다. 키를 숨기는 것은 모호한 보안으로 인한 것이며 코드 난독 화입니다. 비밀 키 보안의 문제점은 보안 키를 보호하려면 다른 키를 사용해야하며 해당 키도 보안되어야한다는 것입니다. 키로 잠긴 상자에 키가 숨겨져 있다고 생각하십시오. 방 안에 상자를 놓고 방을 잠그십시오. 보안을 유지하기 위해 다른 키가 남아 있습니다. 그리고 그 키는 여전히 응용 프로그램 내에서 하드 코딩 될 것입니다.
따라서 사용자가 PIN이나 문구를 입력하지 않으면 키를 숨길 수 없습니다. 그러나 그렇게하려면 대역 외에서 발생하는 PIN을 관리하기위한 체계를 갖추어야합니다. 이는 다른 채널을 통하는 것을 의미합니다. Google API와 같은 서비스의 키 보안에는 실용적이지 않습니다.
비밀을 유지하고 firebase database
앱이 시작될 때 웹에서 가져 오십시오. 웹 서비스를 호출하는 것보다 훨씬 좋습니다.
오래된 게시물이지만 여전히 충분합니다. 물론 NDK와 C ++을 사용하여 .so 라이브러리에 숨기는 것이 좋을 것이라고 생각합니다. .so 파일은 16 진수 편집기에서 볼 수 있지만 행운을 디 컴파일하면 : P
이러한 비공개를 유지하는 유일한 방법은 서버에 보관하고 앱이 서버에 무엇이든 보내도록하고 서버가 Dropbox와 상호 작용하는 것입니다. 그런 식으로 개인 키를 어떤 형식으로도 배포하지 마십시오.
쓴 경험을 바탕으로 IDA-Pro 전문가와상의 한 후 코드의 핵심 부분을 DLL / SharedObject로 옮긴 다음 서버에서 가져와 런타임에로드하는 것이 가장 좋습니다.
민감한 데이터는 다음과 같이 매우 쉽게 수행 할 수 있으므로 인코딩해야합니다.
$ strings libsecretlib.so | grep My
My_S3cr3t_P@$$W0rD
'Programming' 카테고리의 다른 글
내 응용 프로그램에 "암호화가 포함되어 있습니까?" (0) | 2020.03.04 |
---|---|
Windows에서 Git : mergetool을 어떻게 설정합니까? (0) | 2020.03.04 |
"스레드 안전"코드 란 무엇입니까? (0) | 2020.03.04 |
파이썬에서 숫자의 목록을 합치십시오 (0) | 2020.03.04 |
일부는 거절하더라도 모든 약속이 완료 될 때까지 기다리십시오 (0) | 2020.03.04 |