Transmission of sensitive information in an HTTP request
CRITICAL | |||
Detection method | DAST NETWORKING |
Description
Using HTTP instead of HTTPS makes a man-in-the-middle attack possible. This can lead to loss of data confidentiality. Note, that all data sent via the HTTP protocol are transferred in plain text with absolutely no protection. Anybody, who shares a network with you, can receive or even forge the data having relevant experience.
Using the HTTPS protocol that is based on HTTP plus SSL / TLS, allows to protect the transferred data from an unauthorized access or modification. It is recommended to use HTTPS for all situations involving exchange of valuable information between a client and a server, particularly for login pages and other pages requiring authentication.
Recommendations
Ideally, not encrypted traffic should never be used in an application. If this is difficult to implement or there is a need to use third-party services via HTTP, pay special attention to verification and validation of the received data and never transmit confidential information over such protocol.
If you need to choose a data transmission method, the following chart can be helpful:
Comparison of HTTP and HTTPS:
|
|
HTTP |
HTTPS |
Characteristics |
URL Encryption of contents Protection of contents from modification Server authentication |
http:// Impossible Impossible Impossible |
https:// Possible Possible Possible |
Risks |
Reading of contents by an attacker Modification of contents by an attacker Application’s access to a fake server |
High High High |
Low Low Low |
Android uses java.net.HttpURLConnection/javax.net.ssl.HttpsURLConnection as API for establishing communication channels over HTTP/HTTPS. Apache HttpClient is not supported starting from Android 6.0 (API 23).
To establish a channel via HTTPS, you shouldn't use SSLSocket. Unlike HttpsURLConnection, this class doesn't check by default if there is a match between the server name and the name of the host declared in the certificate. Moreover, when using SSLSocket, developers often make mistakes that lead to security defects in the communication channel. |
Using HTTPS with SSL pinning
An application can be additionally protected from fraudulent certificates with help of a mechanism known as SSL pinning. The mechanism prevents a certificate issued by a trusted certificate authority from being compromised in the system storage, which makes security violation of the data transmission channel nearly impossible.
Rules:
- Compare the server certificate with the one stored in the application
- The URI scheme must be https://
- Sensitive information may be transmitted
- You can trust the received data, as they are received from a real server
- Handle SSL issues in an appropriate manner
Example: Correct implementation of SSL Pinning
PrivateCertificateHttpsGet.java
package com.mobix.android.https.privatecertificate;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;
import android.content.Context;
import android.os.AsyncTask;
public abstract class PrivateCertificateHttpsGet extends AsyncTask<String, Void, Object> {
private Context mContext;
public PrivateCertificateHttpsGet(Context context) {
mContext = context;
}
@Override
protected Object doInBackground(String... params) {
TrustManagerFactory trustManager;
BufferedInputStream inputStream = null;
ByteArrayOutputStream responseArray = null;
byte[] buff = new byte[1024];
int length;
try {
URL url = new URL(params[0]);
// Compare the server certificate with the one stored in the application
// Configure the keystore to establish connections using only certificates from the application's resources
KeyStore ks = KeyStoreUtil.getEmptyKeyStore();
KeyStoreUtil.loadX509Certificate(ks,
mContext.getResources().getAssets().open("cacert.crt"));
// *** 2 *** The URI scheme must be https://
// *** 3 *** Sensitive information may be transmitted
trustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManager.init(ks);
SSLContext sslCon = SSLContext.getInstance("TLS");
sslCon.init(null, trustManager.getTrustManagers(), new SecureRandom());
HttpURLConnection con = (HttpURLConnection)url.openConnection();
HttpsURLConnection response = (HttpsURLConnection)con;
response.setDefaultSSLSocketFactory(sslCon.getSocketFactory());
response.setSSLSocketFactory(sslCon.getSocketFactory());
checkResponse(response);
// *** 4 *** You can trust the received data, as they are received from a real server
inputStream = new BufferedInputStream(response.getInputStream());
responseArray = new ByteArrayOutputStream();
while ((length = inputStream.read(buff)) != -1) {
if (length > 0) {
responseArray.write(buff, 0, length);
}
}
return responseArray.toByteArray();
} catch(SSLException e) {
// *** 5 *** Handle SSL issues in an appropriate manner
// Omit since this is an example
return e;
} catch(Exception e) {
return e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
// Omit since this is an example
}
}
if (responseArray != null) {
try {
responseArray.close();
} catch (Exception e) {
// Omit since this is an example
}
}
}
}
private void checkResponse(HttpURLConnection response) throws IOException {
int statusCode = response.getResponseCode();
if (HttpURLConnection.HTTP_OK != statusCode) {
throw new IOException("HttpStatus: " + statusCode);
}
}
}
KeyStoreUtil.java
package com.mobix.android.https.privatecertificate;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
public class KeyStoreUtil {
public static KeyStore getEmptyKeyStore() throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException {
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(null);
return ks;
}
public static void loadAndroidCAStore(KeyStore ks)
throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
KeyStore aks = KeyStore.getInstance("AndroidCAStore");
aks.load(null);
Enumeration<String> aliases = aks.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Certificate cert = aks.getCertificate(alias);
ks.setCertificateEntry(alias, cert);
}
}
public static void loadX509Certificate(KeyStore ks, InputStream is)
throws CertificateException, KeyStoreException {
try {
CertificateFactory factory = CertificateFactory.getInstance("X509");
X509Certificate x509 = (X509Certificate)factory.generateCertificate(is);
String alias = x509.getSubjectDN().getName();
ks.setCertificateEntry(alias, x509);
} finally {
try { is.close(); } catch (IOException e) { /* Skip as an example*/ }
}
}
}
PrivateCertificateHttpsActivity.java
package com.mobix.android.https.privatecertificate;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
public class PrivateCertificateHttpsActivity extends Activity {
private EditText mUrlBox;
private TextView mMsgBox;
private ImageView mImgBox;
private AsyncTask<String, Void, Object> mAsyncTask ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUrlBox = (EditText)findViewById(R.id.urlbox);
mMsgBox = (TextView)findViewById(R.id.msgbox);
mImgBox = (ImageView)findViewById(R.id.imageview);
}
@Override
protected void onPause() {
if (mAsyncTask != null) mAsyncTask.cancel(true);
super.onPause();
}
public void onClick(View view) {
String url = mUrlBox.getText().toString();
mMsgBox.setText(url);
mImgBox.setImageBitmap(null);
if (mAsyncTask != null) mAsyncTask.cancel(true);
mAsyncTask = new PrivateCertificateHttpsGet(this) {
@Override
protected void onPostExecute(Object result) {
if (result instanceof Exception) {
Exception e = (Exception)result;
mMsgBox.append("\nException occurs\n" + e.toString());
} else {
byte[] data = (byte[])result;
Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
mImgBox.setImageBitmap(bmp);
}
}
}.execute(url);
}
}
Using a modern approach—Network Security Configuration
The Android platform provides a new simple instrument for configuration of a network—Network Security Configuration (NSC). This instrument is available on Android 7.0. and above. With help of NSC, you can configure net connections (including SSL Pinning) using XML files. To turn on the configuration, you need to link the configuration file with the application's manifest. To do this, use the networkSecurityConfig attribute in the application tag.
1. Create the configuration file
res/xml/network_security_config.xml
2. Add the android:networkSecurityConfig attribute specifying the file location in AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="co.netguru.demoapp">
<application
android:networkSecurityConfig="@xml/network_security_config">
...
</application>
</manifest>
3. Create the configuration file and add certificate fingerprints
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<pin-set>
<pin digest="SHA-256">ZC3lTYTDBJQVf1P2V7+fibTqbIsWNR/X7CWNVW+CEEA=</pin>
<pin digest="SHA-256">GUAL5bejH7czkXcAeJ0vCiRxwMnVBsDlBMBsFtfLF8A=</pin>
</pin-set>
</domain-config>
</network-security-config>
As you can see, this method is very easy to implement. Keep in mind, however, that the above implementation is possible only for API level 24 and higher.