Ответ 1
После исчерпания существующего спектра доступных опций из Javascript я решил просто внедрить сертификат, который изначально был таким простым, что я закончил.
Перейдите к заголовкам под названием Решение для Android и IOS Solution, если вы не хотите читать процесс достижения решения.
Android
Следуя рекомендации Kudo Я подумал о том, чтобы реализовать пиннинг с помощью okhttp3.
client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build())
.build();
Сначала я начал изучать, как создать родной андроидный мост с реакцией native, создающий тост-модуль. Затем я расширил его методом отправки простого запроса
@ReactMethod
public void showURL(String url, int duration) {
try {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
Toast.makeText(getReactApplicationContext(), response.body().string(), duration).show();
} catch (IOException e) {
Toast.makeText(getReactApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
При отправке запроса я затем повернулся к отправке запрошенного запроса.
Я использовал эти пакеты в своем файле
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
Подход Kudo не был ясен, когда я получу открытые ключи или их создание. к счастью okhttp3 docs в дополнение к предоставлению четкой демонстрации того, как использовать CertificatePinner, заявил, что для получения открытых ключей все, что мне нужно будет сделать, это отправить запрос с неправильным выводом, и в сообщении об ошибке появятся правильные контакты.
После того, как вы поняли, что OkHttpClent.Builder() может быть привязанным, и я могу включить CertificatePinner перед сборкой, в отличие от вводящего в заблуждение примера в предложении Kudo (вероятно, и более старой версии), я придумал этот метод.
@ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
Callback successCallback) {
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
Response response =client.newCall(request).execute();
successCallback.invoke(response.body().string());
} catch (Exception e) {
errorCallbackContainingCorrectKeys.invoke(e.getMessage());
}
}
Затем, заменив публичные связки ключей, я получил ошибку, возвратил тело страницы, указав, что сделал успешный запрос, я меняю одну букву ключа, чтобы убедиться, что он работает, и я знал, что я на ходу.
У меня, наконец, был этот метод в моем файле ToastModule.java
@ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
Callback successCallback) {
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
.add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
.add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
.build();
OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
Response response =client.newCall(request).execute();
successCallback.invoke(response.body().string());
} catch (Exception e) {
errorCallbackContainingCorrectKeys.invoke(e.getMessage());
}
}
Android Solution Расширение действия Реальный корень OkHttpClient
Выяснив, как отправить прикрепленный http-запрос, было хорошо, теперь я могу использовать метод, который я создал, но в идеале я думал, что лучше всего расширить существующий клиент, чтобы сразу воспользоваться преимуществами реализации.
Это решение действует как RN0.35
, и я не знаю, как это будет справедливо в будущем.
Изучая способы расширения OkHttpClient для RN, я столкнулся с этой статьей, объясняя, как добавить поддержку TLS 1.2, заменив SSLSocketFactory.
Чтение. Я узнал, что реакция использует OkHttpClientProvider для создания экземпляра OkHttpClient, используемого объектом XMLHttpRequest, и поэтому, если мы заменим этот экземпляр, мы применили бы привязку ко всему приложению.
Я добавил файл под названием OkHttpCertPin.java
в папку android/app/src/main/java/com/dreidev
package com.dreidev;
import android.util.Log;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
public class OkHttpCertPin {
private static String hostname = "*.efghermes.com";
private static final String TAG = "OkHttpCertPin";
public static OkHttpClient extend(OkHttpClient currentClient){
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
.add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
.add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
.build();
Log.d(TAG, "extending client");
return currentClient.newBuilder().certificatePinner(certificatePinner).build();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return currentClient;
}
}
Этот пакет имеет метод расширения, который принимает существующий OkHttpClient и перестраивает его, добавляя certificatePinner и возвращает вновь созданный экземпляр.
Затем я изменил файл MainActivity.java после этого совета ответов, добавив следующие методы
.
.
.
import com.facebook.react.ReactActivity;
import android.os.Bundle;
import com.dreidev.OkHttpCertPin;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;
public class MainActivity extends ReactActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rebuildOkHtttp();
}
private void rebuildOkHtttp() {
OkHttpClient currentClient = OkHttpClientProvider.getOkHttpClient();
OkHttpClient replacementClient = OkHttpCertPin.extend(currentClient);
OkHttpClientProvider.replaceOkHttpClient(replacementClient);
}
.
.
.
Это решение было выполнено в пользу полного переопределения метода OkHttpClientProvider createClient, поскольку при проверке поставщика я понял, что главная версия реализовала TLS 1.2, но еще не был доступным вариантом для меня, и поэтому восстановление оказалось лучшим средством для расширения клиента. Мне интересно, как этот подход будет справедливым по мере моего обновления, но на данный момент он работает хорошо.
Обновление Кажется, что начиная с 0,43 этот трюк больше не работает. По срочным причинам я заморожу свой проект на 0,42 до тех пор, пока не станет ясно, почему восстановление перестало работать.
Решение IOS
Для IOS я подумал, что мне нужно будет следовать аналогичному методу, опять же, начиная с предложения Kudo в качестве моего руководства.
Проверяя модуль RCTNetwork, я узнал, что NSURLConnection используется, поэтому вместо того, чтобы пытаться создать совершенно новый модуль с AFNetworking, как было предложено в предложении, я обнаружил TrustKit
следуя руководству по началу работы, я просто добавил
pod 'TrustKit'
в мой podfile и запустил pod install
the GettingStartedGuide объяснил, как я могу настроить этот модуль из моего pList.file, но предпочитая использовать код, кроме файлов конфигурации, я добавил следующие строки в файл AppDelegate.m
.
.
.
#import <TrustKit/TrustKit.h>
.
.
.
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Initialize TrustKit
NSDictionary *trustKitConfig =
@{
// Auto-swizzle NSURLSession delegates to add pinning validation
kTSKSwizzleNetworkDelegates: @YES,
kTSKPinnedDomains: @{
// Pin invalid SPKI hashes to *.yahoo.com to demonstrate pinning failures
@"efghermes.com" : @{
kTSKEnforcePinning:@YES,
kTSKIncludeSubdomains:@YES,
kTSKPublicKeyAlgorithms : @[kTSKAlgorithmRsa2048],
// Wrong SPKI hashes to demonstrate pinning failure
kTSKPublicKeyHashes : @[
@"+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=",
@"aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=",
@"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY="
],
// Send reports for pinning failures
// Email [email protected] if you need a free dashboard to see your App reports
kTSKReportUris: @[@"https://overmind.datatheorem.com/trustkit/report"]
},
}
};
[TrustKit initializeWithConfiguration:trustKitConfig];
.
.
.
Я получил хэши открытого ключа из моей реализации Android и только что сработал (версия TrustKit, которую я получил в моих контейнерах, - 1.3.2)
Я был рад, что IOS оказалась в состоянии дыхания.
В качестве дополнительной заметки TrustKit предупредил, что Auto-swizzle не будет работать, если NSURLSession и Connection уже будут проверены. что, похоже, он работает до сих пор.
Заключение
В этом ответе представлено решение как для Android, так и для IOS, поскольку я смог реализовать это в собственном коде.
Одним из возможных улучшений может быть внедрение общего модуля платформы, в котором настройку открытых ключей и настройку сетевых поставщиков как Android, так и IOS можно управлять в javascript.
Предложение Kudo, упомянутое просто добавлением открытых ключей в пакет js, может, однако, подвергнуть уязвимости, где каким-то образом файл пакета может быть заменен.
Я не знаю, как этот вектор атаки может функционировать, но, безусловно, дополнительный шаг подписи пакета .js, как было предложено, может защитить пакет js.
Другим подходом может быть просто кодирование пакета js в 64-разрядную строку и включение его в собственный код непосредственно в качестве упомянутого в этом выпуске. Этот подход имеет преимущество при запутывании, а также упрощение набора js в приложение, что делает его недоступным для злоумышленников или, как я полагаю.
Если вы прочтете это, я надеюсь, что я просветил вас в поисках исправления вашей ошибки и пожелал вам насладиться солнечным днем.