Как безопасно работать с сетью в Android на Java

Короткий ответ: для простых запросов подойдёт HttpsURLConnection, но в продакшне используйте OkHttp с корректными таймаутами, TLS‑пиннингом и выполнением запросов вне UI‑потока.

Быстрые требования и настройки

  • В Android приложению нужно разрешение в манифесте:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • С Android 9 (API 28) по умолчанию запрещён plain HTTP. Для тестов можно включить cleartext только для домена через network_security_config, но в продакшне используйте HTTPS.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
  <domain-config cleartextTrafficPermitted="false">
    <domain includeSubdomains="true">example.com</domain>
  </domain-config>
</network-security-config>

и в манифесте android:networkSecurityConfig="@xml/network_security_config".

Нельзя отключать проверку TLS в продакшне. Игнорирование SSL (TrustAll) приведёт к уязвимости MITM и отклонению в Google Play.

Работа с java.net (HttpURLConnection): компактные примеры и важные нюансы

HttpURLConnection/HttpsURLConnection доступны «из коробки». Основные правила: устанавливайте таймауты, читайте responseCode, закрывайте потоки и никогда не выполняйте в UI.

Простой GET:

public String makeGetRequest(String urlString) throws IOException {
    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    try {
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        conn.setRequestProperty("Accept", "application/json");
        int code = conn.getResponseCode();
        InputStream is = (code >= 200 && code < 300) ? conn.getInputStream() : conn.getErrorStream();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) sb.append(line);
            if (code >= 200 && code < 300) return sb.toString();
            throw new IOException("HTTP " + code + ": " + sb.toString());
        }
    } finally {
        conn.disconnect();
    }
}

POST с JSON:

conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type","application/json; charset=utf-8");
// запись в OutputStream и чтение ответа аналогично

Ключевые моменты:

  • Всегда устанавливайте connect/read timeouts (5–10 с).
  • Закрывайте InputStream/OutputStream и вызывайте conn.disconnect().
  • Проверяйте responseCode и обрабатывайте errorStream.
  • Не храните ключи непосредственно в коде.

Почему чаще выбирают OkHttp и как безопасно его использовать

OkHttp упрощает код, поддерживает кэш, сжатие, ретраи и WebSocket. Для продакшна рекомендуют OkHttp + CertificatePinner и выполнение запросов в фоне.

Пример синхронного запроса (OkHttp):

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(10, TimeUnit.SECONDS)
    .build();

Request request = new Request.Builder()
    .url("https://api.example.com/data")
    .addHeader("Authorization", "Bearer " + token)
    .build();

try (Response response = client.newCall(request).execute()) {
    if (response.isSuccessful() && response.body() != null) {
        String body = response.body().string();
        // обработка
    } else {
        // обработка ошибки
    }
}

Асинхронно используйте enqueue(callback) или выполняйте вызов внутри ExecutorService/WorkManager.

Пиннинг сертификатов:

  • CertificatePinner позволяет ограничить доверенные сертификаты для домена.
  • Комбинируйте пиннинг с нормальной проверкой TLS и обновляйте пины заранее.

Для новых проектов начните с OkHttp: меньше бойлерплейта, встроенное кеширование, поддержка HTTP/2 и WebSocket.

Лучшие практики и безопасность (чеклист)

  • Используйте HTTPS и современные TLS (1.2+).
  • Таймауты: connect 5–10s, read 5–15s.
  • Не отключайте hostname/сertificates.
  • Храните секреты в безопасных местах (NDK, сервер, encrypted prefs), не в коде.
  • Выполняйте сетевые операции вне UI (ExecutorService, WorkManager, RxJava).
  • Обрабатывайте ошибки сети: retry с backoff, различайте сетевые и серверные ошибки.
  • Закрывайте body/streams и вызывайте disconnect().

Частые ошибки

  • Нет таймаутов → приложение «виснет».
  • Чтение только InputStream при ошибке → теряются сообщения об ошибке (используйте errorStream).
  • Хранение API‑ключей в строковых ресурсах или коде.
  • Игнорирование проверки сертификата или использование TrustAll.

FAQ

  • Нужно ли переходить с AsyncTask на ExecutorService? — Да. AsyncTask устарел; используйте ExecutorService, WorkManager или RxJava.
  • Как тестировать на плохой сети? — Используйте эмулятор/Network Link Conditioner или ноутбук с прокси, чтобы симулировать задержки и потерю пакетов.
  • Можно ли комбинировать HttpURLConnection и OkHttp? — Да, но лучше выбрать один стек; OkHttp можно использовать как слой HTTP для большинства задач.
  • Что делать при смене сертификата сервера при пиннинге? — Обновите пины заранее и обеспечьте fallback (несколько валидных пинов) чтобы не потерять доступ.

С этими практиками ваши сетевые запросы в Android на Java станут надёжнее и безопаснее. Для новых проектов рекомендуем начинать с OkHttp и строить надёжную обработку ошибок и хранение секретов на сервере.