Segurança Mobile — Certificate Pinning

Charleston A.
5 min readMar 11, 2023

--

Certificate Pinning é uma técnica que permite aos desenvolvedores proteger seus aplicativos contra ataques de interceptação de tráfego, como Man-in-the-Middle (MITM), que podem ser usados ​​para roubar informações sensíveis, como senhas e informações financeiras.

O Certificate Pinning envolve a verificação da autenticidade do certificado SSL/TLS do servidor de destino, em vez de confiar nas autoridades de certificação padrão. Isso pode ser alcançado por meio do armazenamento de um hash do certificado do servidor em um arquivo de configuração do aplicativo móvel. Durante a conexão com o servidor, o aplicativo móvel verifica se o hash do certificado corresponde ao que foi armazenado no arquivo de configuração.

O Certificate Pinning é particularmente útil em situações em que os atacantes tentam interceptar o tráfego em redes Wi-Fi públicas, que são geralmente menos seguras do que redes privadas. Ao usar o Certificate Pinning, os desenvolvedores podem garantir que as informações do usuário estejam protegidas, mesmo em redes menos seguras.

Em conclusão, o Certificate Pinning é uma técnica importante que os desenvolvedores de aplicativos móveis devem considerar para aumentar a segurança de seus aplicativos. Ele pode ajudar a proteger informações sensíveis contra ataques de interceptação de tráfego, como Man-in-the-Middle, e garantir que os usuários possam usar aplicativos móveis com segurança em qualquer rede, pública ou privada.

Show me the code!

Serão apresentados exemplos em Kotlin, Swift, TypeScript/JS e Dart

Android Kotlin
Este código implementa o Certificate Pinning usando a biblioteca Retrofit. Ele define as mesmas constantes e cria o mesmo objeto OkHttpClient com o CertificatePinner configurado. Em seguida, ele cria um objeto Retrofit com a URL base e o OkHttpClient configurado e adiciona um conversor para Strings. O código define uma interface MyService que define um método para recuperar dados do servidor e, em seguida, cria um objeto MyService usando o Retrofit. O código usa o objeto MyService para fazer uma solicitação ao servidor e manipula a resposta da mesma maneira que o exemplo anterior.

import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory

class MainActivity : AppCompatActivity() {

private val TAG = "CertificatePinningDemo"
private val URL = "https://example.com"
private val PUBLIC_KEY_HASH = "sha256/PUBLIC_KEY_HASH"

private lateinit var responseTextView: TextView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

responseTextView = findViewById(R.id.response_text_view)

val client = OkHttpClient.Builder()
.certificatePinner(CertificatePinner.Builder()
.add("example.com", PUBLIC_KEY_HASH)
.build())
.build()

val retrofit = Retrofit.Builder()
.baseUrl(URL)
.client(client)
.addConverterFactory(ScalarsConverterFactory.create())
.build()

val service = retrofit.create(MyService::class.java)

service.getData().enqueue(object : Callback<String> {
override fun onFailure(call: Call<String>, t: Throwable) {
Log.e(TAG, "Request failed.", t)
runOnUiThread { responseTextView.text = "Request failed." }
}

override fun onResponse(call: Call<String>, response: Response<String>) {
val responseBody = response.body()
Log.d(TAG, "Response: $responseBody")
runOnUiThread { responseTextView.text = responseBody }
}
})
}

interface MyService {
@GET("/")
fun getData(): Call<String>
}
}

iOS Swift
Este código implementa o Certificate Pinning usando a biblioteca Alamofire. Ele define as mesmas constantes, cria um objeto ServerTrustPolicy com o certificado público do servidor e adiciona uma política de confiança ao objeto SessionManager do Alamofire. Em seguida, ele usa o objeto SessionManager para fazer uma solicitação HTTPS para a URL especificada e manipula a resposta da mesma maneira que os exemplos anteriores.

import UIKit
import Alamofire

class ViewController: UIViewController {

private let URL = "https://example.com"
private let PUBLIC_KEY_HASH = "PUBLIC_KEY_HASH"

@IBOutlet weak var responseLabel: UILabel!

override func viewDidLoad() {
super.viewDidLoad()

let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: [SecCertificateCreateWithData(nil, NSData(base64Encoded: PUBLIC_KEY_HASH, options: [])!)!],
validateCertificateChain: true,
validateHost: true
)

let serverTrustPolicies: [String: ServerTrustPolicy] = [
"example.com": serverTrustPolicy
]

let sessionManager = SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)

sessionManager.request(URL).responseString { response in
switch response.result {
case .success(let responseBody):
print("Response: \(responseBody)")
self.responseLabel.text = responseBody
case .failure(let error):
print("Request failed: \(error)")
self.responseLabel.text = "Request failed."
}
}
}
}

Typescript / JS
Este código implementa o Certificate Pinning usando a biblioteca Axios. Ele define as mesmas constantes e usa o método axios.get para fazer uma solicitação HTTPS para a URL especificada. O código especifica o certificado público do servidor na opção httpsAgent, juntamente com a opção rejectUnauthorized definida como true para rejeitar certificados não confiáveis. A resposta é manipulada no callback de sucesso e erros são manipulados no callback de erro.

import axios, { AxiosResponse } from 'axios';

const URL = 'https://example.com';
const PUBLIC_KEY_HASH = 'PUBLIC_KEY_HASH';

axios.get(URL, {
httpsAgent: {
rejectUnauthorized: true,
cert: Buffer.from(PUBLIC_KEY_HASH, 'base64')
}
}).then((response: AxiosResponse) => {
console.log(`Response: ${response.data}`);
}).catch((error) => {
console.error(`Request failed: ${error}`);
});

Flutter Dart
Este código implementa o Certificate Pinning usando a biblioteca Dio. Ele define as mesmas constantes e usa o método dio.get para fazer uma solicitação HTTPS para a URL especificada. O código especifica o certificado público do servidor na opção httpsAgent dentro de um interceptador. A resposta é manipulada no callback de sucesso e erros são manipulados no callback de erro.

import 'package:flutter/material.dart';
import 'package:dio/dio.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Certificate Pinning Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Certificate Pinning Demo'),
),
body: Center(
child: RaisedButton(
child: Text('Make Request'),
onPressed: () => makeRequest(),
),
),
),
);
}

void makeRequest() async {
try {
const String URL = 'https://example.com';
const String PUBLIC_KEY_HASH = 'PUBLIC_KEY_HASH';

Dio dio = Dio();
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (RequestOptions options) {
options.validateStatus = (status) => status < 500;
options.receiveTimeout = 5000;
options.sendTimeout = 5000;
options.baseUrl = URL;
options.headers = {
'Content-Type': 'application/json',
};
options.httpsAgent = HttpsAgent(
certificate: PUBLIC_KEY_HASH,
);
},
),
);

Response response = await dio.get('/');
print('Response: ${response.data}');
} catch (error) {
print('Request failed: $error');
}
}
}

Resumo da ópera

As técnicas de segurança em programação mobile são cruciais para proteger os aplicativos e as informações dos usuários.

Uma das técnicas mais importantes é o Certificate Pinning, que envolve a verificação da autenticidade do certificado SSL/TLS do servidor de destino, em vez de confiar nas autoridades de certificação padrão.

Isso pode ser alcançado por meio do armazenamento de um hash do certificado do servidor em um arquivo de configuração do aplicativo móvel.

Outra técnica importante é o uso de bibliotecas como OkHttp, Retrofit, Alamofire e Axios, que oferecem suporte para Certificate Pinning. Essas bibliotecas permitem que os desenvolvedores implementem o Certificate Pinning facilmente em seus aplicativos móveis e garantam a segurança das informações dos usuários contra ataques de interceptação de tráfego, como Man-in-the-Middle (MITM).

--

--