openharmony-security-review について
openharmony-security-review は、OpenHarmony の C++ システムサービスを対象とする構造化されたセキュリティレビュー用スキルです。IPC やネットワーク入力、並行処理、機密情報のログ出力、権限確認のリスクを調べます。
主な機能
- 01IPC のデシリアライズと境界を確認
- 02共有状態のロックと競合をレビュー
- 03デッドロックと TOCTOU のリスクを特定
- 04ログの機密情報漏えいを確認
- 05権限検証をレビュー
活用シーン
- 01MessageParcel データを扱う OpenHarmony サービスのレビュー
- 02サービス状態への並行アクセスの監査
- 03サービスログにおける個人情報露出の確認
- 04特権操作の権限チェックの評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ openharmonyinsight/ openharmony-skills --skill openharmony-security-reviewインストール後、Claude Code で/openharmony-security-reviewを入力して読み込まれたか確認します。
詳細
OpenHarmony のセキュリティレビュー
概要
OpenHarmony のシステムサービスは高い権限で実行され、IPC やネットワークインターフェースを介して信頼できない入力を処理します。このスキルでは、外部入力の処理、マルチスレッドの競合状態、機密情報の漏えい、権限検証という4つの重要な領域で、重大なセキュリティ脆弱性を特定するための体系的なアプローチを紹介します。
使用する場面
digraph when_to_use {
"Reviewing OpenHarmony code?" [shape=diamond];
"Is it C++ system service?" [shape=diamond];
"Handles IPC/network data?" [shape=diamond];
"Has shared state?" [shape=diamond];
"Logs data?" [shape=diamond];
"Use this skill" [shape=box];
"Different skill needed" [shape=box];
"Reviewing OpenHarmony code?" -> "Different skill needed" [label="no"];
"Reviewing OpenHarmony code?" -> "Is it C++ system service?" [label="yes"];
"Is it C++ system service?" -> "Different skill needed" [label="no"];
"Is it C++ system service?" -> "Handles IPC/network data?" [label="yes"];
"Handles IPC/network data?" -> "Use this skill" [label="yes"];
"Handles IPC/network data?" -> "Has shared state?" [label="no"];
"Has shared state?" -> "Use this skill" [label="yes"];
"Has shared state?" -> "Logs data?" [label="no"];
"Logs data?" -> "Use this skill" [label="yes"];
"Logs data?" -> "Different skill needed" [label="no"];
}
このスキルを使用する場面:
- OpenHarmony の C++ システムサービスコード(xxxService、xxxStub の実装)をレビューする場合
- コードが IPC(MessageParcel)またはネットワークデータ(JSON/XML/Protobuf)を処理する場合
- コードが共有状態にマルチスレッドでアクセスする場合
- コードがユーザーデータやポインターをログに記録する場合
- コードが権限を検証する、または保護対象のリソースにアクセスする場合
以下の用途には使用しないでください:
- アプリケーション層のコード(代わりに標準的なセキュアコーディングの手法を使用してください)
- OpenHarmony 以外の C++ コード(代わりに一般的なセキュリティレビューのスキルを使用してください)
- パフォーマンスの最適化(別のスキルを使用してください)
コードの追跡方針
ヘッダーファイルが入力の場合(.h/.hpp): 対応する xxxService.cpp と xxxStub.cpp を分析します
Stub ファイルが入力の場合(xxxStub.cpp): 分析対象を xxxService.cpp(コアロジックと共有状態)に広げます
外部呼び出し: コンポーネント間の並行処理リスクとして指摘し、別途レビューします
クイックリファレンス:重大な脆弱性のチェックリスト
| カテゴリ | 重要なチェック項目 | 重大度 |
|---|---|---|
| IPC のデシリアライズ | すべての MessageParcel の読み取りで成功を確認している | 高 |
| 論理検証 | デシリアライズ後に配列の長さ/インデックスを検証している | 高 |
| 整数の境界値 | サイズ変数:0 <= size <= MAX_ALLOWED_BUFFER | 高 |
| オブジェクトのライフサイクル | RemoteObjects/fd を使用前に検証している(nullptr チェック) | 高 |
| パーサーのセキュリティ | ネットワークパーサーが不正な入力を拒否し、再帰攻撃を防いでいる | 高 |
| コンテナーのスレッドセーフ性 | すべてのコンテナー操作が保護されている(読み取り/書き込み、書き込み/書き込み) | 高 |
| イテレーターの無効化** | 反復処理中に変更していない | 高 |
| ロックの一貫性 | 共有状態へのすべてのアクセスがミューテックスで保護されている | 高 |
| デッドロックのリスク | ネストしたロックを一貫した順序で取得している | 高 |
| TOCTOU | 状態確認の直後に、それに依存する処理を実行している | 高 |
| ログ内の PII | 電話番号、連絡先、SMS、生体認証情報をログに記録していない | 高 |
| ログ内の入力イベント** | 生の KeyEvent、タッチ座標、画面境界をログに記録していない | 高 |
| ポインターアドレス | ログ内で %p または &variable を使用していない(ASLR の回避防止) | 高 |
| 権限チェック | すべての特権操作で呼び出し元の権限を検証している | 高 |
1. 外部入力の処理(IPC とネットワーク)
1.1 IPC のデシリアライズの完全性
ルール: すべての MessageParcel の読み取り操作で、戻り値を確認する必要があります。
アンチパターン:
// ❌ VULNERABLE: No validation
parcel.ReadInt32(val);
int32_t size = parcel.ReadInt32();
必須パターン:
// ✅ SECURE: Always check return value
if (!parcel.ReadInt32(val)) {
HILOG_ERROR("Failed to read value");
return ERR_INVALID_DATA;
}
if (!parcel.ReadInt32(size)) {
HILOG_ERROR("Failed to read size");
return ERR_INVALID_DATA;
}
検証: 読み取ったデータサイズが想定される型のサイズと一致することを確認します。読み取りに失敗した場合は、直ちに処理を停止します。
1.2 デシリアライズ後の論理検証
ルール: デシリアライズ後、値を使用する前に論理的な範囲内にあることを検証します。
攻撃ベクトル: 攻撃者が size = 0xFFFFFFFF を送信し、メモリ枯渇や new char[size] における整数オーバーフローを引き起こします。
アンチパターン:
// ❌ VULNERABLE: No bounds validation
int32_t size = parcel.ReadInt32();
std::vector<char> buffer(size); // May allocate gigabytes or overflow
必須パターン:
// ✅ SECURE: Validate bounds immediately
int32_t size = 0;
if (!parcel.ReadInt32(size)) {
return ERR_INVALID_DATA;
}
constexpr int32_t MAX_ALLOWED_BUFFER = 1024 * 1024; // 1MB
if (size < 0 || size > MAX_ALLOWED_BUFFER) {
HILOG_ERROR("Invalid size: %{public}d", size);
return ERR_INVALID_DATA;
}
std::vector<char> buffer(size);
検証対象: 配列の長さ、インデックス、ループカウンター、バッファーサイズ
1.3 オブジェクトのライフサイクルと所有権
ルール: RemoteObjects とファイルディスクリプターは、使用前に検証する必要があります。
必須パターン:
// ✅ SECURE: Validate before use
if (remoteObject == nullptr) {
HILOG_ERROR("Received null RemoteObject");
return ERR_NULL_OBJECT;
}
if (fd < 0) {
HILOG_ERROR("Invalid file descriptor");
return ERR_INVALID_FD;
}
1.4 外部ネットワークデータ
ルール: ネットワークパーサーは不正な入力を拒否し、再帰攻撃を防ぐ必要があります。
チェック項目:
- JSON パーサーが不正な入力を拒否するように設定されている
- 「Zip Bomb」攻撃に対する保護がある
- 深くネストした再帰に制限がある
2. マルチスレッドと並行処理
2.1 コンテナのスレッドセーフ性(最優先)
ルール: コンテナに対するすべての並行操作は、ロックで保護する必要があります。
重大なリスク:
- 読み取り/書き込み: あるスレッドが読み取っている間に、別のスレッドが書き込む
- 書き込み/書き込み: 複数のスレッドが同時に変更する
- イテレーターの無効化: あるスレッドが反復処理している間に、別のスレッドが変更する
アンチパターン:
// ❌ VULNERABLE: Unprotected concurrent access
std::vector<int> items_;
void AddItem(int item) {
items_.push_back(item); // Unsafe if called concurrently
}
void ProcessItems() {
for (auto& item : items_) { // Iteration
// If AddItem called here, iterator invalidates → CRASH
}
}
必須パターン:
// ✅ SECURE: All access protected
std::vector<int> items_;
std::mutex itemsMutex_;
void AddItem(int item) {
std::lock_guard<std::mutex> lock(itemsMutex_);
items_.push_back(item);
}
void ProcessItems() {
std::lock_guard<std::mutex> lock(itemsMutex_);
for (auto& item : items_) {
// Safe - lock held during iteration
}
}
精査対象のコンテナ型: std::vector、std::map、std::list、std::unordered_map、std::string
精査対象の操作: push_back、insert、erase、operator[]、at、反復処理
2.2 ロック機構
ルール: すべての共有リソースを一貫して保護する必要があります。
保護が必要な共有リソース:
- グローバル変数/静的変数
- クラスメンバー変数
- スレッド間で共有されるヒープ割り当てオブジェクト
ロック型: std::mutex、std::shared_mutex、SpinLock
アンチパターン:
// ❌ VULNERABLE: Inconsistent protection
class Service {
std::map<int, Data> dataMap_;
std::mutex mutex_;
void Update(int key, const Data& data) {
std::lock_guard<std::mutex> lock(mutex_);
dataMap_[key] = data;
}
Data Lookup(int key) {
// ❌ NO LOCK - race condition with Update
return dataMap_[key];
}
};
必須パターン:
// ✅ SECURE: Consistent protection
class Service {
std::map<int, Data> dataMap_;
std::mutex mutex_;
void Update(int key, const Data& data) {
std::lock_guard<std::mutex> lock(mutex_);
dataMap_[key] = data;
}
Data Lookup(int key) {
std::lock_guard<std::mutex> lock(mutex_);
return dataMap_[key];
}
};
2.3 デッドロックの防止
ルール: ロックをネストする場合は、デッドロックのリスクを評価してください。
デッドロックの条件:
- 異なるコードパスで、複数のロックが異なる順序で取得される
- ロックを保持したまま、ロックを取得する可能性のある外部関数を呼び出す
アンチパターン:
// ❌ VULNERABLE: Deadlock risk
void Path1() {
std::lock_guard<std::mutex> lock1(mutex1_);
std::lock_guard<std::mutex> lock2(mutex2_); // Order: 1 then 2
}
void Path2() {
std::lock_guard<std::mutex> lock2(mutex2_);
std::lock_guard<std::mutex> lock1(mutex1_); // Order: 2 then 1 → DEADLOCK
}
必須パターン:
// ✅ SECURE: Consistent lock order
void Path1() {
std::lock_guard<std::mutex> lock1(mutex1_);
std::lock_guard<std::mutex> lock2(mutex2_); // Order: 1 then 2
}
void Path2() {
std::lock_guard<std::mutex> lock1(mutex1_); // Same order: 1 then 2
std::lock_guard<std::mutex> lock2(mutex2_);
}
2.4 TOCTOU(確認時と使用時の競合)
ルール: 状態の確認後は、間を置かずに依存する操作を実行する必要があります。
アンチパターン:
// ❌ VULNERABLE: TOCTOU window
if (fileExists(path)) {
// Attacker can delete/replace file here
file = openFile(path); // May open different file
}
必須パターン:
// ✅ SECURE: Atomic check-and-use
file = openFile(path);
if (file.isValid()) {
// Use file
}
3. 機密情報の保護
3.1 PII の厳格なマスキング
ルール: ログでは、すべての個人を特定できる情報(PII)をマスキングする必要があります。
マスキングが必要な PII:
- ユーザーのプライバシー: 電話番号、連絡先、SMS の内容、生体認証データ
- 入力イベント: 生の KeyEvent、タッチ座標(x、y)、画面上の位置範囲(Rect)
アンチパターン:
// ❌ VULNERABLE: Logs PII
HILOG_INFO("Phone: %{public}s", phoneNumber.c_str());
HILOG_ERROR("Touch at (%{public}d, %{public}d)", x, y);
必須パターン:
// ✅ SECURE: Redact or suppress
HILOG_INFO("Phone: %{private}s", phoneNumber.c_str()); // Redacted
// Better: Don't log PII at all
HILOG_INFO("Processing phone number");
ポリシー: リリースビルドでは、完全に出力を抑制することを優先してください。デバッグにどうしても必要な場合に限り、%{private} 形式指定子を使用します。
3.2 メモリレイアウトの漏えい(ASLR の回避)
ルール: 生のポインターアドレスをログに記録してはいけません。
アンチパターン:
// ❌ VULNERABLE: Leaks addresses for ASLR bypass
HILOG_INFO("Object at %p", &object);
HILOG_DEBUG("Buffer address: %{public}p", buffer);
必須パターン:
// ✅ SECURE: No address logging
HILOG_INFO("Object created");
// Use opaque IDs instead of pointers
HILOG_INFO("Object ID: %{public}d", object.id_);
理由: ヒープ/スタックアドレスの漏えいは、攻撃者が ASLR(アドレス空間配置のランダム化)を回避する助けになります。
4. 権限の検証
ルール: すべての特権操作で、呼び出し元の権限を検証する必要があります。
アンチパターン:
// ❌ VULNERABLE: No permission check
int32_t Service::DeleteFile(const std::string& path) {
// Any caller can delete any file!
return unlink(path.c_str());
}
必須パターン:
// ✅ SECURE: Validate permissions
int32_t Service::DeleteFile(const std::string& path) {
// Verify caller has permission to access this path
if (!HasPermission(callerToken_, path, Permission::DELETE)) {
HILOG_ERROR("Permission denied for path: %{private}s", path.c_str());
return ERR_PERMISSION_DENIED;
}
// Verify path is within allowed sandbox
if (!IsPathInSandbox(path)) {
HILOG_ERROR("Path outside sandbox: %{private}s", path.c_str());
return ERR_INVALID_PATH;
}
return unlink(path.c_str());
}
権限チェックが必要な操作:
- ファイルシステム操作(読み取り、書き込み、削除)
- システム設定の変更
- ハードウェアへのアクセス(カメラ、マイク、位置情報)
- プロセス間通信
- ネットワーク操作
よくある間違い
| 間違い | 結果 | 修正方法 |
|---|---|---|
| MessageParcel の戻り値チェックを忘れる | 初期化されていないデータの使用、クラッシュ | 常に戻り値を確認する |
| サイズを検証するが、下限(負の値)を確認しない | 負のサイズが検証を通過する | 0 <= size <= MAX を確認する |
| 共有コンテナで書き込みを保護するが、読み取りを保護しない | 読み取り中に競合状態が発生する | すべてのアクセスを保護する |
| コンテナの変更後にイテレーターを使用する | イテレーターの無効化、クラッシュ | 反復処理中はロックを保持する |
PII のログ出力に %{public} を使用する | 情報漏えい | %{private} を使用するか、ログに記録しない |
| デバッグ用にポインターアドレスを出力する | ASLR の回避 | 不透明な ID を使用する |
| 「内部処理だから」という理由で権限チェックを省略する | 外部から呼び出された場合に権限昇格が起きる | 常に検証する |
レビュー出力形式
違反は次の形式で指摘してください。
**[HIGH SECURITY RISK] <Category>**
Location: <file_path>:<line_number>
Issue: <description>
Anti-pattern:
```cpp
<code>
必要な修正:
<code>
影響:悪用された場合に攻撃者が可能になること
## Real-World Impact
**Container race conditions:** Use-after-free crashes, privilege escalation through corrupted state
**IPC validation failures:** Remote code execution via deserialization exploits, memory exhaustion DoS
**PII leakage:** Privacy violations, GDPR compliance failures
**ASLR bypass:** Enables exploitation of memory corruption vulnerabilities
**Missing permission checks:** Unauthorized access to sensitive data, system modification