Cấu hình Bảo mật mạng

Android N có tính năng Cấu hình Bảo mật mạng cho phép ứng dụng tùy chỉnh các cài đặt bảo mật mạng trong một tệp cấu hình khai báo an toàn mà không cần sửa đổi mã nguồn ứng dụng. Các cài đặt này có thể được cấu hình cho các miền cụ thể và cho một ứng dụng cụ thể. Các khả năng chính của tính năng này như sau:

  • Nguồn tin cậy tùy chỉnh: Tùy chính các Nhà cung cấp chứng chỉ (CA) nào được tin cậy cho các kết nối bảo mật của một ứng dụng. Ví dụ, tin cậy các chứng chỉ tự ký đặc biệt hoặc hạn chế bộ CA công khai mà ứng dụng tin cậy.
  • Chỉ khống chế khi gỡ lỗi: Gỡ lỗi các kết nối bảo mật một cách an toàn trong một ứng dụng mà không thêm rủi ro cho cơ sở cài đặt.
  • Không sử dụng truyền gửi văn bản chưa mã hóa: Bảo vệ các ứng dụng khỏi việc vô tình sử dụng truyền gửi văn bản chưa mã hóa.
  • Ghim chứng chỉ: Giới hạn kết nối bảo mật của ứng dụng trong các chứng chỉ đặc biệt.

Thêm một Tệp Cấu hình Bảo mật mạng

Tính năng Cấu hình Bảo mật mạng sử dụng một tệp XML làm nơi bạn sẽ chỉ định các cài đặt cho ứng dụng của mình. Bạn phải bổ sung một mục nhập trong bản kê khai của ứng dụng để trỏ đến tệp này. Đoạn mã sau của một bản kê khai minh họa cách tạo mục nhập này:

<?xml version="1.0" encoding="utf-8"?>
...
<app ...>
    <meta-data android:name="android.security.net.config"
               android:resource="@xml/network_security_config" />
    ...
</app>

Tùy chỉnh các CA đáng tin cậy

Một ứng dụng có thể muốn tin cậy một bộ các CA tùy chỉnh thay vì mặc định của nền tảng. Những lý do phổ biến nhất cho điều này là:

  • Kết nối tới một máy chủ có nhà cung cấp chứng chỉ riêng (tự ký, được cấp bởi một CA nội bộ của công ty, v.v.).
  • Giới hạn bộ CA chỉ trong các CA mà bạn tin cậy thay vì mọi CA được cài đặt trước.
  • Tin cậy các CA bổ sung không được kèm theo trong hệ thống.

Theo mặc định, các kết nối bảo mật (vd: TLS, HTTPS) từ mọi ứng dụng sẽ tin cậy các CA của hệ thống được cài đặt trước và các ứng dụng nhắm mục tiêu mức API 23 (Android M) và thấp hơn theo mặc định cũng tin cậy kho lưu trữ CA được người dùng bổ sung. Một ứng dụng có thể tùy chỉnh các kết nối của riêng nó bằng cách sử dụng base-config (dành cho tùy chỉnh trên phạm vi ứng dụng) hoặc domain-config (tùy chỉnh cho mỗi miền).

Cấu hình một CA tùy chỉnh

Giả sử bạn muốn kết nối tới máy chủ của mình có sử dụng các chứng chỉ SSL tự ký hoặc tới một máy chủ có chứng chỉ SSL được cấp bởi một CA không công khai mà bạn tin cậy, chẳng hạn như CA nội bộ của công ty.

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

Thêm chứng chỉ CA tự ký hoặc không công khai theo định dạng PEM hoặc DER vào res/raw/my_ca.

Giới hạn bộ CA đáng tin cậy

Một ứng dụng không muốn tin cậy mọi CA được hệ thống tin cậy có thể chỉ định bộ CA hạn chế của riêng nó để tin cậy. Điều này sẽ bảo vệ ứng dụng khỏi các chứng chỉ lừa đảo được cấp bởi bất kỳ CA nào khác.

Cấu hình để giới hạn bộ CA đáng tin cậy cũng giống như Tin cậy một CA tùy chỉnh cho một miền cụ thể ngoại trừ việc nhiều CA được cung cấp trong tài nguyên.

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example.com</domain>
        <trust-anchors>
            <certificates src="@raw/trusted_roots"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

Thêm các CA đáng tin cậy theo định dạng PEM hoặc DER vào res/raw/trusted_roots. Lưu ý rằng nếu sử dụng định dạng PEM thì tệp chỉ được chứa dữ liệu PEM và không có thêm dữ liệu văn bản. Bạn cũng có thể cung cấp nhiều phần tử <certificates> thay vì một phần tử.

Tin cậy các CA bổ sung

Một ứng dụng có thể muốn tin cậy các CA bổ sung không được hệ thống tin cậy, điều này có thể do hệ thống chưa thêm CA đó hoặc một CA không đáp ứng các yêu cầu để đưa vào hệ thống Android. Một ứng dụng có thể thực hiện điều này bằng cách chỉ định nhiều nguồn chứng chỉ cho một cấu hình.

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/extracas"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

Cấu hình các CA để gỡ lỗi

Khi gỡ lỗi một ứng dụng kết nối qua HTTPS thì bạn có thể muốn kết nối tới một máy chủ phát triển cục bộ không có chứng chỉ SSL dành cho máy chủ thương mại của bạn. Để hỗ trợ cho trường hợp này mà không cần chỉnh sửa mã nguồn ứng dụng của bạn thì bạn có thể chỉ định các CA chỉ dùng cho gỡ lỗi mà chỉ được tin cậy khi android:debuggabletrue bằng cách sử dụng debug-overrides. Thông thường các IDE và công cụ dựng sẽ đặt cờ này tự động đối với các bản dựng không dùng để phát hành.

Làm như vậy an toàn hơn so với mã điều kiện thông thường bởi, là điều kiện bảo mật tiên quyết, các cửa hàng ứng dụng không chấp nhận các ứng dụng được đánh dấu là hỗ trợ gỡ lỗi.

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cas"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Không sử dụng truyền gửi văn bản chưa mã hóa

Các ứng dụng có ý định kết nối tới các điểm đích chỉ sử dụng các kết nối bảo mật có thể bỏ hỗ trợ truyền gửi văn bản không mã hóa (bằng giao thức HTTP không mã hóa thay vì HTTPS) tới các đích đó. Tùy chọn này giúp ngăn các hồi quy tiềm tàng trong ứng dụng do thay đổi trong các URL được cung cấp bởi các nguồn bên ngoài như các máy chủ phụ trợ. Hãy xem NetworkSecurityPolicy.isCleartextTrafficPermitted() để biết thêm chi tiết.

Ví dụ, một ứng dụng có thể đảm bảo rằng mọi kết nối tới secure.example.com luôn được thực hiện qua HTTPS để bảo vệ việc truyền gửi dữ liệu nhạy cảm khỏi các mạng có hại.

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config usesCleartextTraffic="false">
        <domain includeSubdomains="true">secure.example.com</domain>
    </domain-config>
</network-security-config>

Ghim chứng chỉ

Thông thường thì một ứng dụng sẽ tin cậy mọi CA được cài đặt sẵn. Nếu bất kỳ CA nào trong số này sẽ phát hành một chứng chỉ lừa đảo thì ứng dụng sẽ gặp phải rủi ro từ một cuộc tấn công MiTM. Một số ứng dụng chọn cách giới hạn bộ chứng chỉ được chúng chấp nhận bằng cách giới hạn bộ CA được ứng dụng tin cậy hoặc bằng cách ghim chứng chỉ.

Ghim chứng chỉ được thực hiện bằng cách cung cấp một bộ chứng chỉ theo mã hash của khóa công khai (SubjectPublicKeyInfo của chứng chỉ X.509). Một chuỗi chứng chỉ khi đó chỉ hợp lệ nếu như chuỗi chứng chỉ có chứa ít nhất một trong các khóa công khai được ghim.

Lưu ý rằng khi sử dụng ghim chứng chỉ bạn phải luôn kèm thêm một khóa dự phòng để nếu bạn bị buộc phải chuyển sang các khóa mới hoặc thay đổi các CA (khi ghim vào một chứng chỉ CA hoặc một chứng chỉ trung gian của CA đó) thì kết nối của ứng dụng sẽ không bị ảnh hưởng. Nếu không bạn phải đưa ra một bản cập nhật cho ứng dụng để khôi phục khả năng kết nối.

Ngoài ra, có thể đặt thời gian hết hạn cho các ghim, sau thời gian đó sẽ không tiến hành ghim được. Điều này giúp phòng ngừa các vấn đề về kết nối trong ứng dụng chưa được cập nhật. Tuy nhiên, việc đặt thời gian hết hạn trên các ghim này có thể khiến ghim bị bỏ qua.

res/xml/network_security_config.xml:

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

Hành vi Kế thừa cấu hình

Các giá trị chưa được đặt trong một cấu hình cụ thể sẽ được kế thừa. Hành vi này cho phép tạo ra các cấu hình phức tạp hơn trong khi vẫn giữ cho tệp cấu hình có thể đọc được.

Nếu một giá trị không được đặt trong một mục nhập cụ thể thì giá trị thuộc mục nhập bao quát hơn tiếp theo sẽ được sử dụng. Các giá trị chưa được đặt trong domain-config sẽ được lấy từ phần tử cha domain-config nếu như được lồng, hoặc từ base-config nếu không được lồng. Các giá trị chưa được đặt trong base-config sẽ sử dụng các giá trị mặc định của nền tảng.

Ví dụ: hãy xem xét trường hợp tất cả các kết nối tới miền con của example.com phải sử dụng một bộ CA tùy chỉnh. Ngoài ra, truyền gửi văn bản không mã hóa tới các miền này được cho phép trừ khi kết nối tới secure.example.com. Bằng cách lồng cấu hình cho secure.example.com bên trong cấu hình cho example.com thì trust-anchors không cần phải được sao lặp.

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="false">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>

Định dạng Tệp cấu hình

Tính năng Cấu hình Bảo mật mạng sử dụng một định dạng tệp XML. Cấu trúc chung của tệp này được thể hiện trong đoạn mẫu mã nguồn sau:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </base-config>

    <domain-config>
        <domain>android.com</domain>
        ...
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
        <pin-set>
            <pin digest="...">...</pin>
            ...
        </pin-set>
    </domain-config>
    ...
    <debug-overrides>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Các phần sau sẽ mô tả cú pháp và các chi tiết khác của định dạng tệp này.

<network-security-config>

có thể chứa:
0 hoặc 1 của <base-config>
Bất kỳ số nào của <domain-config>
0 hoặc 1 của <debug-overrides>

<base-config>

cú pháp:
<base-config usesCleartextTraffic=["true" | "false"]>
    ...
</base-config>
có thể chứa:
<trust-anchors>
mô tả:
Cấu hình mặc định được sử dụng bởi mọi kết nối có đích đến không được bao gồm bởi một domain-config.

Bất kỳ giá trị nào chưa được đặt sẽ sử dụng các giá trị mặc định của nền tảng. Cấu hình mặc định cho các ứng dụng nhắm mục tiêu API mức 24 trở lên:

<base-config usesCleartextTraffic="true">
    <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
</base-config>
Cấu hình mặc định cho các ứng dụng nhắm mục tiêu API mức 23 trở xuống:
<base-config usesCleartextTraffic="true">
    <trust-anchors>
        <certificates src="system" />
        <certificates src="user" />
    </trust-anchors>
</base-config>

<domain-config>

cú pháp:
<domain-config usesCleartextTraffic=["true" | "false"]>
    ...
</domain-config>
Có thể chứa:
1 hoặc nhiều <domain>
0 hoặc 1 <trust-anchors>
0 hoặc 1 <pin-set>
Bất kỳ số nào của <domain-config> được lồng
Mô tả
Cấu hình được sử dụng cho các kết nối tới các điểm đích cụ thể theo như định nghĩa bởi các phần tử domain.

Lưu ý rằng nếu nhiều phần tử domain-config chứa một điểm đích thì cấu hình có quy tắc miền trùng khớp cụ thể nhất (dài nhất) sẽ được sử dụng.

<domain>

cú pháp:
<domain includeSubdomains=["true" | "false"]>example.com</domain>
Thuộc tính:
includeSubdomains
Nếu "true" thì quy tắc miền này sẽ trùng với miền đó và tất cả các miền con, bao gồm các miền con của miền con, nếu không quy tắc đó chỉ áp dụng cho các trùng khớp tuyệt đối.
Mô tả:

<debug-overrides>

cú pháp:
<debug-overrides>
    ...
</debug-overrides>
Có thể chứa:
0 hoặc 1 <trust-anchors>
Mô tả:
Ghi đè được thực hiện khi android:debuggable"true" thì thường là trường hợp dành cho các bản dựng không phát hành được tạo ra bởi các IDE hoặc công cụ dựng. Các nguồn tin cậy được chỉ định trong debug-overrides được thêm vào tất cả các cấu hình khác và ghim chứng chỉ không được thực hiện khi chuỗi chứng chỉ của máy chủ sử dụng một trong các nguồn tin cậy chỉ dành cho gỡ lỗi này. Nếu android:debuggable"false" thì phần này bị bỏ qua hoàn toàn.

<trust-anchors>

cú pháp:
<trust-anchors>
...
</trust-anchors>
Có thể chứa:
Bất kỳ số nào của <certificates>
Mô tả:
Đặt nguồn tin cậy cho các kết nối bảo mật:

<certificates>

cú pháp:
<certificates src=["system" | "user" | "raw resource"]
              overridePins=["true" | "false"] />
mô tả:
Bộ các chứng chỉ X.509 cho các phần tử trust-anchors.
thuộc tính:
src
Nguồn của các chứng chỉ CA, có thể là một
  • id nguồn thô trỏ tới một tệp có chứa các chứng chỉ X.509. Các chứng chỉ phải được mã hóa theo định dạng DER hoặc PEM. Trong trường hợp của các chứng chỉ PEM thì tệp đó không được chứa dữ liệu không phải PEM khác như các chú thích.
  • "system" cho các chứng chỉ CA hệ thống được cài đặt sẵn
  • "user" cho các chứng chỉ CA do người dùng thêm vào
overridePins

Xác định xem liệu các CA từ nguồn này có bỏ qua việc ghim chứng chỉ hay không. Nếu "true" thì các chuỗi chứng chỉ mà xâu qua một trong các CA từ nguồn này thì khi đó ghim sẽ không được thực hiện. Đây có thể là điều có ích cho các CA gỡ lỗi hoặc để hỗ trợ cho người dùng tấn công MiTM hoạt động truyền gửi bảo mật của ứng dụng.

Mặc định là "false" trừ khi được chỉ định trong một phần tử debug-overrides , khi đó mặc định là "true".

<pin-set>

cú pháp:
<pin-set expiration="date">
...
</pin-set>
Có thể chứa:
Bất kỳ số nào của <pin>
Mô tả:
Một bộ các ghim khóa công khai. Để một kết nối bảo mật được tin cậy, một trong các khóa công khai trong chuỗi tin cậy phải nằm trong bộ các ghim này. Xem <pin> để biết định dạng của các ghim.
Thuộc tính:
expiration
Ngày tháng, theo định dạng yyyy-MM-dd, vào và sau thời điểm các ghim hết hạn và do đó vô hiệu hóa ghim. Nếu thuộc tính này chưa được đặt thì khi đó các ghim không hết hạn.

Việc hết hạn giúp phòng ngừa các vấn đề về khả năng kết nối trong ứng dụng làm cho không nhận được các bản cập nhật cho bộ ghim, chẳng hạn như do người dùng vô hiệu hóa các bản cập nhật ứng dụng.

<pin>

cú pháp:
<pin digest=["SHA-256"]>base64 encoded digest of X.509
    SubjectPublicKeyInfo (SPKI)</pin>
Thuộc tính:
digest
Thuật toán băm được sử dụng để tạo ghim. Hiện tại, chỉ có "SHA-256" được hỗ trợ.