Clear text HTTP request
Requête HTTP en clair
Description
Le trafic HTTP en clair désigne les données transmises entre une application mobile et son serveur backend sans aucun chiffrement, ce qui les rend facilement lisibles et susceptibles d'être interceptées par des acteurs malveillants. Ce manque de chiffrement pose des risques de sécurité importants pour les applications mobiles. Lorsque des informations sensibles, telles que des identifiants de connexion, des données personnelles ou des détails financiers, sont envoyées via des connexions HTTP en clair, elles deviennent vulnérables aux écoutes clandestines et aux attaques de l'homme du milieu.
L'un des principaux risques associés au trafic HTTP en clair dans les applications mobiles est l'exposition potentielle des données des utilisateurs. Les attaquants peuvent intercepter et extraire des informations précieuses, conduisant à l'usurpation d'identité, à l'accès non autorisé aux comptes et à l'utilisation abusive des données personnelles. De plus, les attaquants peuvent exploiter ces vulnérabilités pour altérer le contenu de l'application ou injecter du code malveillant dans le canal de communication.
De plus, le trafic HTTP en clair peut compromettre la confidentialité des utilisateurs. Les applications mobiles collectent et transmettent souvent des données d'utilisateurs à des fins d'analyse ou de publicité ciblée. Sans chiffrement, ces données sont facilement accessibles à des tiers, compromettant la confidentialité des utilisateurs et pouvant entraîner des violations de la vie privée.
Les applications mobiles qui ne mettent pas en œuvre de protocoles de communication sécurisés sont également exposées au risque d'attaques d'usurpation d'identité. Les attaquants peuvent créer de faux réseaux Wi-Fi ou utiliser d'autres techniques pour intercepter le trafic, en se faisant passer pour le serveur backend légitime. Les utilisateurs envoient sans le savoir leurs données à l'attaquant, qui peut ensuite les utiliser de manière abusive à des fins malveillantes.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(LoginApp());
class LoginApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Login App',
home: LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
String _loginStatus = '';
void _performLogin() async {
final String username = _usernameController.text.trim();
final String password = _passwordController.text.trim();
// Simulate an HTTP request for login.
final String apiUrl = 'http://example.com/login';
final Map<String, dynamic> requestBody = {
'username': username,
'password': password,
};
final http.Response response = await http.post(
Uri.parse(apiUrl),
headers: {'Content-Type': 'application/json'},
body: json.encode(requestBody),
);
if (response.statusCode == 200) {
// Successful login
setState(() {
_loginStatus = 'Login successful!';
});
} else {
// Failed login
setState(() {
_loginStatus = 'Login failed. Please try again.';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: _usernameController,
decoration: InputDecoration(labelText: 'Username'),
),
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _performLogin,
child: Text('Login'),
),
SizedBox(height: 10),
Text(_loginStatus, textAlign: TextAlign.center),
],
),
),
);
}
}
import UIKit
class LoginViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginStatusLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines),
let password = passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return
}
// Simulate an HTTP request for login.
let apiUrl = "http://example.com/login"
let requestBody: [String: Any] = [
"username": username,
"password": password
]
var request = URLRequest(url: URL(string: apiUrl)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: requestBody, options: [])
} catch {
print("Error creating JSON data: \(error)")
return
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Error: \(error)")
DispatchQueue.main.async {
self.loginStatusLabel.text = "Login failed. Please try again."
}
return
}
guard let data = data,
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
DispatchQueue.main.async {
self.loginStatusLabel.text = "Login failed. Please try again."
}
return
}
DispatchQueue.main.async {
self.loginStatusLabel.text = "Login successful!"
}
}
task.resume()
}
}
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_login.*
import org.json.JSONObject
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
loginButton.setOnClickListener {
val username = usernameEditText.text.trim().toString()
val password = passwordEditText.text.trim().toString()
// Simulate an HTTP request for login.
val apiUrl = "https://example.com/login"
val requestBody = JSONObject().apply {
put("username", username)
put("password", password)
}.toString()
Thread {
performLogin(apiUrl, requestBody)
}.start()
}
}
private fun performLogin(apiUrl: String, requestBody: String) {
try {
val url = URL(apiUrl)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json")
connection.doOutput = true
val outputStreamWriter = OutputStreamWriter(connection.outputStream)
outputStreamWriter.write(requestBody)
outputStreamWriter.flush()
val responseCode = connection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
runOnUiThread {
loginStatusLabel.text = "Login successful!"
}
} else {
runOnUiThread {
loginStatusLabel.text = "Login failed. Please try again."
}
}
} catch (e: Exception) {
Log.e("LoginActivity", "Error: ${e.message}")
runOnUiThread {
loginStatusLabel.text = "Login failed. Please try again."
}
}
}
}
Recommandation
Pour atténuer les risques associés au trafic HTTP en clair dans les applications mobiles, il est impératif de mettre en œuvre des protocoles de communication sécurisés et des bonnes pratiques. Vous trouverez ci-dessous quelques recommandations détaillées pour améliorer la sécurité des communications des applications mobiles :
-
Adopter le chiffrement HTTPS : L'étape la plus fondamentale consiste à utiliser HTTPS (HTTP Secure) pour toutes les communications entre l'application mobile et le serveur backend. HTTPS chiffre les données pendant la transmission, garantissant que les informations sensibles restent confidentielles et sécurisées. Obtenez un certificat SSL/TLS auprès d'une autorité de certification (CA) de confiance et imposez les connexions HTTPS pour tous les points de terminaison d'API et transferts de données.
-
Épinglage de certificat : Mettez en œuvre l'épinglage de certificat (certificate pinning) pour améliorer la sécurité des connexions HTTPS. Cela implique de coder en dur le certificat SSL du serveur ou sa clé publique dans l'application mobile. Ce faisant, l'application s'assure qu'elle ne communique qu'avec le serveur prévu, empêchant ainsi les attaques de l'homme du milieu où des attaquants tentent d'utiliser leurs propres certificats.
-
Activer HSTS (HTTP Strict Transport Security) : Utilisez les en-têtes HSTS pour ordonner au navigateur Web de l'application de toujours utiliser des connexions HTTPS, même si l'utilisateur tape par erreur "http://" dans l'URL. Cela empêche toute attaque potentielle de déclassement et garantit que toutes les communications sont chiffrées.
-
Utiliser la dernière version de TLS : Assurez-vous que l'application mobile utilise la dernière version du protocole TLS (Transport Layer Security) pour tirer parti des fonctionnalités de sécurité et des algorithmes de chiffrement les plus robustes.
-
Mettre en œuvre la vérification des certificats : Configurez l'application pour qu'elle vérifie les certificats SSL révoqués ou expirés afin d'éviter les interactions avec des serveurs potentiellement compromis.
-
Chiffrement des données sensibles : Chiffrez les données sensibles stockées sur l'appareil ou transmises au serveur. Utilisez des algorithmes de chiffrement puissants et gérez les clés de chiffrement de manière sécurisée, cela peut ajouter une couche supplémentaire de sécurité à la transmission des données.
-
Transmission sécurisée des données pour toutes les API : Évaluez toutes les API utilisées par l'application et assurez-vous qu'elles utilisent également HTTPS pour la transmission des données. Cela inclut les API tierces et les services intégrés à l'application.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(LoginApp());
class LoginApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Login App',
home: LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
String _loginStatus = '';
void _performLogin() async {
final String username = _usernameController.text.trim();
final String password = _passwordController.text.trim();
// Simulate a secure HTTPS request for login (Replace this with your actual secure API endpoint).
final String apiUrl = 'https://www.example.com/login';
final Map<String, dynamic> requestBody = {
'username': username,
'password': password,
};
final http.Response response = await http.post(
Uri.parse(apiUrl),
headers: {'Content-Type': 'application/json'},
body: json.encode(requestBody),
);
if (response.statusCode == 200) {
// Successful login
setState(() {
_loginStatus = 'Login successful!';
});
} else {
// Failed login
setState(() {
_loginStatus = 'Login failed. Please try again.';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: _usernameController,
decoration: InputDecoration(labelText: 'Username'),
),
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(labelText: 'Password'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _performLogin,
child: Text('Login'),
),
SizedBox(height: 10),
Text(_loginStatus, textAlign: TextAlign.center),
],
),
),
);
}
}
import UIKit
class LoginViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginStatusLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
guard let username = usernameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines),
let password = passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return
}
// Simulate a secure HTTPS request for login (Replace this with your actual secure API endpoint).
let apiUrl = "https://www.example.com/login"
let requestBody: [String: Any] = [
"username": username,
"password": password
]
var request = URLRequest(url: URL(string: apiUrl)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: requestBody, options: [])
} catch {
print("Error creating JSON data: \(error)")
return
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Error: \(error)")
DispatchQueue.main.async {
self.loginStatusLabel.text = "Login failed. Please try again."
}
return
}
guard let data = data,
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
DispatchQueue.main.async {
self.loginStatusLabel.text = "Login failed. Please try again."
}
return
}
DispatchQueue.main.async {
self.loginStatusLabel.text = "Login successful!"
}
}
task.resume()
}
}
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_login.*
import org.json.JSONObject
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
loginButton.setOnClickListener {
val username = usernameEditText.text.trim().toString()
val password = passwordEditText.text.trim().toString()
// Simulate a secure HTTPS request for login (Replace this with your actual secure API endpoint).
val apiUrl = "https://www.example.com/login"
val requestBody = JSONObject().apply {
put("username", username)
put("password", password)
}.toString()
Thread {
performLogin(apiUrl, requestBody)
}.start()
}
}
private fun performLogin(apiUrl: String, requestBody: String) {
try {
val url = URL(apiUrl)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json")
connection.doOutput = true
val outputStreamWriter = OutputStreamWriter(connection.outputStream)
outputStreamWriter.write(requestBody)
outputStreamWriter.flush()
val responseCode = connection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
runOnUiThread {
loginStatusLabel.text = "Login successful!"
}
} else {
runOnUiThread {
loginStatusLabel.text = "Login failed. Please try again."
}
}
} catch (e: Exception) {
Log.e("LoginActivity", "Error: ${e.message}")
runOnUiThread {
loginStatusLabel.text = "Login failed. Please try again."
}
}
}
}
Liens
- Missing Encryption of Sensitive Data (CWE-311)
- Testing for Weak SSL/TLS Ciphers, Insufficient Transport Layer Protection
- Top 10 2013-A6-Sensitive Data Exposure
- Insufficient Transport Layer
- Cleartext Transmission of Sensitive Information (CWE-319)
Normes
- OWASP_ASVS_L1:
- V9_1_1
- OWASP_ASVS_L2:
- V1_9_1
- V9_1_1
- OWASP_ASVS_L3:
- V1_9_1
- V9_1_1
- PCI_STANDARDS:
- REQ_2_2
- REQ_4_2
- REQ_6_2
- REQ_6_3
- REQ_6_4
- REQ_11_3
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_6_7
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- HIPAA_CONTROLS:
- SECURITY252
- SECURITY212
- SECURITY213