Как безопасно работать с сетью в 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 и строить надёжную обработку ошибок и хранение секретов на сервере.