SSL Pinning & Signature checking(SecureCoding – P2)

by baka3k
762 views
This entry is part [part not set] of 9 in the series Coding

SecureCoding – P2

Hi, lại là mình, rambler coder đây

Trong phần trước chúng ta đã tìm hiểu về MITM attack và một số thủ thuật đi kèm

Trước khi đi vào detail rule/coding tips ở những phần sau, phần thứ 2 này mình muốn chia sẻ thêm về 1 số rule config project, runtime application để hạn chế việc application bị tấn công, sửa đổi.

Table of contents

Ssl Pinning

Hành động này gần như là bắt buộc với tất các ứng dụng sử dụng giao thức HTTPS. Tuy nhiên trên Google developer page chúng ta có thể thấy một đoạn warning như sau

Caution: Certificate Pinning is not recommended for Android applications due to the high risk of future server configuration changes, such as changing to another Certificate Authority, rendering the application unable to connect to the server without receiving a client software update.

Sự lo lắng này hoàn toàn hợp lý, khi certificate hết hạn, khi thay đổi certificate..etc – không có cách nào chắc chắn người dùng sẽ cập nhật phần mềm, đồng nghĩa với việc người dùng không thể kết nối tới máy chủ, và có thể chúng ta sẽ mất một lượng user rất lớn. Nên nếu sử dụng SSL pinning, chúng ta phải handle được các case liên quan đến certification và phải tính đến khả năng force update đối với client khi có exception liên quan đến ssl certificate

Có rất nhiều cách để triển khai SSL pinning, tuỳ thuộc cách thức mà ứng dụng kết nối với server mà chúng ta lựa chọn item phù hợp

  1. NetworkSecurityConfig

Step 1 Create networkSecurityconfig file

res/xml/network_security_config.xml

với nội dung sau

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain> // your domain
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> // your hash key 
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin> // your hash key 
        </pin-set>
    </domain-config>
</network-security-config>

Step2

Khai báo sử dụng trong AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

Chi tiết vui lòng tham khảo https://developer.android.com/training/articles/security-config#CertificatePinning

  1. OKHttpClient

Nếu đang triển khai kết nối bằng OKHttpClient thì có thể tham khảo link bên dưới https://github.com/baka3k/CleanArchitecture/blob/main/data/src/main/java/com/baka3k/architecture/data/service/base/Service.kt

private fun buildCertificatePinner(): CertificatePinner {
        return if (pinning != null && pinning.isNotEmpty()) {
            val builder = CertificatePinner.Builder()
            for (item in pinning) {
                builder.add(item.value, item.key)
            }
            builder.build()
        } else {
            CertificatePinner.Builder()
                .add("api.themoviedb.org", "sha256/+vqZVAzTqUP8BGkfl88yU7SQ3C8J2uNEa55B7RZjEg0=") // your hash key (pinning SSL)
                .build()
        }
    }
}

Sử dụng

val pining = buildCertificatePinner()
val okHttpClient = OkHttpClient.Builder()
            .certificatePinner(pining)
            .build()
  1. Hash Key? – sha256 lấy ở đâu?

Trong 2 ví dụ ở trên chúng ta đều thấy sự xuất hiện của chuỗi ký tự sha256, có nhiều cách để lấy chuỗi này, ví dụ dùng open-ssl hoặc cách đơn giản nhất là truy cập https://www.ssllabs.com/ – Điền link muốn check vào và thông tin sẽ hiện ra như sau

pinning

Self signature

Đây là một kỹ thuật mình thấy khá hiếm người sử dụng trong ứng dụng(mặc dù nó rất dễ, mất vài dòng code thôi) – dùng để ngăn chặn(phần nào đó) việc APK bị sửa đổi, thêm mã độc.

Hầu hết lập trình viên Android đều từng ít nhất một lần tải APK từ nguồn ko chính thống, ví dụ apkpure, apkresult hoặc các game trên appstorevn

Điều gì đảm bảo các apk này là apk sạch? ko có bị sửa đổi với mục đích thu thập thông tin người dùng, chèn quảng cáo hoặc tệ hơn là Phishing, hoặc tạo ra các attack vector để tấn công vào một hệ thống nào đó – ở đây có thể là hệ thống của chính chúng ta? – nếu đó là APK của chúng ta?

Ví dụ, attacker có thể modify một file APK cho app ngân hàng/ví điện tử nào đó, ở giao diện đăng nhập thay vì việc gửi thông tin đăng nhập thì attacker sẽ log lại thông tin đăng nhập này, bao gồm mật khẩu, password và gửi về máy chủ của attacker chả hạn.

Một file APK có thể được sửa đổi và sign đi sign lại nhiều lần, cách nhanh nhất để kiểm tra một file APK có bị sửa đổi rồi sign lại hay ko là check signature của file APK, signature là duy nhất, nó chỉ được gen ra bởi key dùng để sign apk và ko có cách nào từ signature trong apk sinh ngược được ra sign key cả.

  1. Dùng Keytool để lấy ra SHA của file APK release
keytool -printcert -jarfile app-release.apk
... 
MD5:  B3:4F:BE:07:AA:78:24:DC:CA:92:36:FF:AE:8C:17:DB
SHA1: 16:59:E7:E3:0C:AA:7A:0D:F2:0D:05:20:12:A8:85:0B:32:C5:4F:68XXXX
SHA256: 1XXXXXXXXXXXX1XXXXXXXXXXXX1XXXXXXXXXXXX1XXXXXXXXXXXX1XXXXXXXXXXXX
  1. Trên source code ứng dụng, khi vào một màn hình nào đó chúng ta có thể check SHA dựa vào đoạn code đơn giản bên dưới
Signature[] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : sigs)
{
    Trace.i("MyApp", "Signature hashcode : " + sig.hashCode());
}

Compare các mã sha này => có thể phán đoán được ứng dụng có bị sửa đổi hay ko, tuỳ thuộc business, bạn có thể quyết định stop hẳn ứng dụng hoặc disable các tính năng tương ứng.

API Key

Đây là rule liên quan đến việc lưu trữ các API key/ User/Password cần thiết khi buid ứng dụng

  1. API Key Thông thường khi phát triển ứng dụng, developer hay có xu hướng hardcode API key trực tiếp vào source code, ví dụ thế này
object MovieDBServer {
    const val MOVIE_DB_ACCESS_KEY = "xxxxxxxxxxxxx"
}
 @GET("popular")
    suspend fun getPopular(
        @Query("api_key") clientId: String = MovieDBServer.MOVIE_DB_ACCESS_KEY
    ): MovieResult

Giả sử chúng ta có 2 API key dùng cho test server và production server thì sao?

object MovieDBServer {
    const val MOVIE_DB_ACCESS_KEY_DEBUG = "xxxxxxxxxxxxx"
    const val MOVIE_DB_ACCESS_KEY_PRODUCTION = "xxxxxxxxxxxxx"
}

Tất nhiên khi query thì phải check như thế này

@GET("popular")
    suspend fun getPopular(
        @Query("api_key") clientId: String = if (BuildConfig.DEBUG)
            MovieDBServer.MOVIE_DB_ACCESS_KEY_DEBUG
            else MovieDBServer.MOVIE_DB_ACCESS_KEY_PRODUCTION
    ): MovieResult

Có 2 issue ở đây

  • Có source code là có key, key không được quản lý tập trung
  • Nếu thêm nhiều loại key, vd stagging, production, develop..etc thì phải sửa source để build

Để giải quyết vấn đề này, người ta sẽ đẩy việc define các key ra file – ngoài source code và sẽ inject vào trong quá trình build time, các job build khác nhau cho các môi trường khác nhau sẽ inject các API key khác nhau. Tức là thế này

@GET("popular")
    suspend fun getPopular(
        @Query("api_key") clientId: String = BuildConfig.MOVIE_DB_ACCESS_KEY // chú ý dòng này BuildConfig.
    ): MovieResult

MOVIE_DB_ACCESS_KEY được inject vào tại build time, nên có thể tuỳ chỉnh, thay đổi cho các job build khác nhau.

Một ví dụ đơn giản, mình chọn lưu ra gradle.properties

moviedb_access_key="PLACE YOUR KEY IN HERE"

Trên file build.gradle

  defaultConfig {
        ...
        buildConfigField("String", "MOVIE_DB_ACCESS_KEY", "\"" + getMovieDBAccessKey() + "\"")
    }
.....
def getMovieDBAccessKey() {
    return project.findProperty("moviedb_access_key")
}

Cụ thể, nếu các bạn cần sample, thì có thể refer tại https://github.com/baka3k/CleanArchitecture/blob/main/data/build.gradle

Tương tự đối với các sensitive information như user/password của private repository, pass của keystore..etc chúng ta cũng có thể inject trong build time như vậy

Authors

[email protected]

Series NavigationRaw String in Swift >>

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.