Communitygithub.com

アイデンティティガバナンスライフサイクルプロセスの構築

SailPoint、Saviynt、Entra ID Governanceなどのプラットフォームで、ジョイナー・ムーバー・リーバー(JML)自動化、ロールマイニング、アクセス要求、定期的な再認定、HRフィードをソースとする孤立アカウントの修復を含む、アイデンティティガバナンスとライフサイクル(IGA)プログラムを設計します。クロスシステムのJMLプロビジョニングの自動化、退職者のアクセス修復、SOX、HIPAA、GDPRコンプライアンスのためのライフサイクルプロセス構築の際に使用します。

アイデンティティガバナンスライフサイクルプロセスの構築 とは?

このスキルは、エンタープライズ向けのアイデンティティガバナンスおよびライフサイクル(IGA)プログラムを設計および実装するための包括的なガイドを提供します。主な焦点は、ジョイナー・ムーバー・リーバー(JML)プロセスの自動化、ロールマイニング、アクセス要求、定期的な再認定、孤立アカウントの修復です。これにより、手動のアクセスプロビジョニング、ロールの乱立、およびコンプライアンス要件(SOX、HIPAA、GDPR)に関連する組織的な課題に対処します。HRシステム(Workday、SAP SuccessFactors、BambooHR)を信頼できる唯一の情報源として使用し、それをSailPoint、Saviynt、Microsoft Entra ID GovernanceなどのIGAプラットフォームと統合して、Active Directoryやクラウドアプリケーションなどのターゲットシステム全体での自動プロビジョニングを調整します。ワークフローは、ライフサイクル状態の定義、HR統合の実装、ロールマイニング、アクセス要求の設定、再認定キャンペーンのスケジュール設定、コンプライアンスレポートの生成を含む段階的なプロセスをカバーしています。このスキルは、直接使用できるPythonコードスニペットを提供しており、アイデンティティライフサイクルステートマシン、ロールマイニングエンジン、HRコネクタ、アクセス要求API、再認定ワークフロー、監査レポートなどを備えています。このスキルを適用することで、ユーザーはアイデンティティ管理プロセスを自動化および標準化し、セキュリティギャップを低減し、コンプライアンスを維持できます。主要なプラットフォーム(SailPoint、Saviynt、Entra ID Governance)をサポートし、AD、Azure AD、カスタムアプリケーションにプロビジョニングします。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mukul975/Anthropic-Cybersecurity-Skills/tree/main/skills/building-identity-governance-lifecycle-process

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

アイデンティティガバナンスライフサイクルプロセスの構築 は何をしますか?

使用する場合

  • 組織にアイデンティティ管理のための自動化されたジョイナー・ムーバー・リーバー(JML)プロセスが欠けている場合
  • アクセスプロビジョニングが手動で日数を要し、生産性の損失とセキュリティギャップを生み出している場合
  • 退職後も元従業員がシステムへのアクセスを保持している場合(孤立アカウント)
  • ロールの乱立により、所有権が不明確で権限が重複する何千ものロールが発生している場合
  • コンプライアンス要件により、文書化されたアイデンティティライフサイクルプロセス(SOX、HIPAA、GDPR)が義務付けられている場合
  • 企業全体で誰がどのアクセス権を持っているかについて、一元的な可視性がない場合

使用しない場合: 単一アプリケーションのユーザー管理には使用しないでください。アイデンティティガバナンスは、信頼できるHRソースとダウンストリームアプリケーションプロビジョニングの相関を必要とするクロスシステムのライフサイクル管理に対応します。

前提条件

  • 権威あるHRシステム(Workday、SAP SuccessFactors、BambooHR)をアイデンティティの信頼できる情報源として使用すること
  • IGAプラットフォーム(SailPoint、Saviynt、One Identity)またはMicrosoft Entra ID Governance
  • プライマリディレクトリサービスとしてのActive Directoryおよび/またはAzure AD
  • 自動プロビジョニングを必要とするターゲットシステム用のアプリケーションコネクタ
  • 定義された組織ロール構造と報告階層
  • HR、IT、セキュリティ、事業部門マネージャーからのステークホルダーの合意

ワークフロー

ステップ 1: アイデンティティライフサイクルの状態と遷移を定義する

採用から退職までのアイデンティティライフサイクルをマッピングします:

"""
アイデンティティライフサイクルステートマシン
自動化アクションを含むすべてのアイデンティティ状態と有効な遷移を定義します。
"""

IDENTITY_LIFECYCLE = {
    "states": {
        "PRE_HIRE": {
            "description": "開始日前にHRフィードから作成されたアイデンティティ",
            "automated_actions": [
                "IGAプラットフォームにアイデンティティレコードを作成",
                "一意の従業員IDを生成",
                "メールボックス予約を作成",
                "職務コードに基づいて生得権ロールを割り当て",
                "バックグラウンドチェックワークフローを開始"
            ],
            "valid_transitions": ["ACTIVE", "CANCELLED"]
        },
        "ACTIVE": {
            "description": "従業員が入社し、フルアクセスがプロビジョニングされた状態",
            "automated_actions": [
                "Active Directoryアカウントを作成",
                "メールメールボックスを作成",
                "生得権アプリケーションアクセスをプロビジョニング",
                "部門固有のロールを割り当て",
                "配布グループに追加",
                "MFAトークン/セキュリティキーを発行",
                "リモートワーカーの場合、VPNアカウントを作成"
            ],
            "valid_transitions": ["ROLE_CHANGE", "LEAVE_OF_ABSENCE", "TERMINATED"]
        },
        "ROLE_CHANGE": {
            "description": "従業員が異動、昇進、または部門変更を行った状態",
            "automated_actions": [
                "新しい職務コードに基づいてロール割り当てを再計算",
                "以前の部門アプリケーションからのアクセスを削除",
                "新しい部門アプリケーションへのアクセスをプロビジョニング",
                "グループメンバーシップを更新",
                "ディレクトリ内のマネージャーを転送",
                "保持された権限に対するアクセスレビューをトリガー",
                "継承されたアクセスについて新しいマネージャーに通知"
            ],
            "valid_transitions": ["ACTIVE", "LEAVE_OF_ABSENCE", "TERMINATED"]
        },
        "LEAVE_OF_ABSENCE": {
            "description": "従業員が長期休暇中(医療、育児、サバティカル)",
            "automated_actions": [
                "インタラクティブログインを無効化(アカウントは保持)",
                "VPNアクセスを一時停止",
                "不在時自動返信を設定",
                "メールボックスをマネージャーに委任",
                "復帰時のためにすべてのロール割り当てを保持",
                "HRフィードから再有効化日を設定"
            ],
            "valid_transitions": ["ACTIVE", "TERMINATED"]
        },
        "TERMINATED": {
            "description": "従業員が組織を退職した状態",
            "automated_actions": [
                "ADアカウントを即座に無効化",
                "すべてのアプリケーションアクセスを撤回",
                "VPNおよびリモートアクセスを撤回",
                "メールボックスを共有に変換(マネージャーが90日間アクセス)",
                "OneDriveファイルをマネージャーに転送",
                "すべてのセキュリティグループと配布グループから削除",
                "OAuthトークンとAPIキーを撤回",
                "モバイルデバイスから企業データをワイプ",
                "アイデンティティレコードをアーカイブ",
                "保持期間後にアカウント削除をスケジュール"
            ],
            "valid_transitions": ["REHIRE", "DELETED"]
        },
        "REHIRE": {
            "description": "以前退職した従業員が復帰する状態",
            "automated_actions": [
                "既存のアイデンティティレコードを再有効化",
                "資格情報をリセットし、MFA再登録を要求",
                "新しい職務コードに基づいてプロビジョニング(以前のアクセスではない)",
                "最初の30日間は強化されたアクセスレビュー用にフラグを設定"
            ],
            "valid_transitions": ["ACTIVE"]
        },
        "DELETED": {
            "description": "保持期間後にアカウントが完全に削除された状態",
            "automated_actions": [
                "ADアカウントを削除",
                "メールメールボックスアーカイブを削除",
                "IGAからアイデンティティレコードを削除",
                "削除監査ログを生成"
            ],
            "valid_transitions": []
        }
    },
    "retention_periods": {
        "terminated_to_deleted": "90日(デフォルト)",
        "mailbox_retention": "共有メールボックスとして90日",
        "onedrive_retention": "マネージャーアクセス30日、その後アーカイブ",
        "audit_log_retention": "コンプライアンスのため7年間"
    }
}

ステップ 2: 信頼できるソース統合を実装する

HRシステムをアイデンティティデータの単一の信頼できる情報源として接続します:

"""
HRソース統合 - Workday to IGA Platform Connector
従業員ライフサイクルイベントについてWorkdayをポーリングし、プロビジョニングをトリガーします。
"""
import requests
from datetime import datetime, timedelta
import logging

class WorkdayIdentityConnector:
    def __init__(self, config):
        self.base_url = config["workday_api_url"]
        self.tenant = config["tenant"]
        self.client_id = config["client_id"]
        self.client_secret = config["client_secret"]
        self.session = requests.Session()
        self.logger = logging.getLogger("workday_connector")

    def get_access_token(self):
        """Workday REST APIに認証します。"""
        token_url = f"{self.base_url}/ccx/oauth2/{self.tenant}/token"
        response = self.session.post(token_url, data={
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret
        })
        response.raise_for_status()
        return response.json()["access_token"]

    def fetch_worker_changes(self, since_datetime):
        """前回の同期以降のすべての従業員ライフサイクルイベントを取得します。"""
        headers = {"Authorization": f"Bearer {self.get_access_token()}"}
        params = {
            "Updated_From": since_datetime.isoformat(),
            "Updated_Through": datetime.utcnow().isoformat(),
            "Count": 100
        }

        workers = []
        url = f"{self.base_url}/ccx/api/v1/{self.tenant}/workers"

        while url:
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            data = response.json()
            workers.extend(data.get("data", []))
            url = data.get("next", None)
            params = {}

        return workers

    def map_lifecycle_event(self, worker):
        """Workdayの従業員データをアイデンティティライフサイクルイベントにマッピングします。"""
        worker_data = worker.get("workerData", {})
        employment = worker_data.get("employmentData", {})
        personal = worker_data.get("personalData", {})

        event = {
            "employee_id": worker.get("id"),
            "first_name": personal.get("legalName", {}).get("firstName"),
            "last_name": personal.get("legalName", {}).get("lastName"),
            "email": worker_data.get("emailAddress"),
            "job_code": employment.get("jobProfile", {}).get("id"),
            "job_title": employment.get("jobProfile", {}).get("name"),
            "department": employment.get("organization", {}).get("name"),
            "department_code": employment.get("organization", {}).get("id"),
            "manager_id": employment.get("managerId"),
            "location": employment.get("location", {}).get("name"),
            "cost_center": employment.get("costCenter", {}).get("id"),
            "hire_date": employment.get("hireDate"),
            "termination_date": employment.get("terminationDate"),
            "status": employment.get("status"),
            "worker_type": employment.get("workerType"),
        }

        # ライフサイクル遷移を決定
        if event["status"] == "Active" and event["hire_date"]:
            hire_date = datetime.fromisoformat(event["hire_date"])
            if hire_date > datetime.utcnow():
                event["lifecycle_event"] = "PRE_HIRE"
            else:
                event["lifecycle_event"] = "JOINER"
        elif event["status"] == "Active":
            event["lifecycle_event"] = "MOVER"  # 部門またはロールの変更
        elif event["status"] == "Terminated":
            event["lifecycle_event"] = "LEAVER"
        elif event["status"] == "On Leave":
            event["lifecycle_event"] = "LEAVE_OF_ABSENCE"

        return event

    def process_lifecycle_events(self, since_datetime):
        """アイデンティティライフサイクルイベントのメイン処理ループ。"""
        workers = self.fetch_worker_changes(since_datetime)
        events = []

        for worker in workers:
            event = self.map_lifecycle_event(worker)
            events.append(event)
            self.logger.info(
                f"ライフサイクルイベント: {event['lifecycle_event']} for "
                f"{event['first_name']} {event['last_name']} "
                f"(EmpID: {event['employee_id']})"
            )

        return events

ステップ 3: ロールマイニングと生得権アクセスを実装する

自動プロビジョニングのために職務に基づいたロールを定義します:

"""
ロールマイニングエンジン
既存のアクセスパターンを分析して、生得権(自動)プロビジョニングのための
ロール定義を導き出します。
"""
import pandas as pd
from collections import Counter
from itertools import combinations

class RoleMiningEngine:
    def __init__(self, access_data):
        """
        access_data: 次のカラムを持つDataFrame
        [employee_id, job_code, department, application, entitlement]
        """
        self.access_data = access_data

    def mine_birthright_roles(self, min_assignment_pct=0.8):
        """
        職務コードに基づいて自動的に割り当てるべき権限を特定します。
        同じ職務コードを持つユーザーの80%以上が権限を持っている場合、
        それは生得権アクセスになります。
        """
        birthright_roles = {}

        for job_code, group in self.access_data.groupby("job_code"):
            total_users = group["employee_id"].nunique()
            entitlement_counts = group.groupby(
                ["application", "entitlement"]
            )["employee_id"].nunique()

            birthright_entitlements = []
            for (app, ent), count in entitlement_counts.items():
                pct = count / total_users
                if pct >= min_assignment_pct:
                    birthright_entitlements.append({
                        "application": app,
                        "entitlement": ent,
                        "assignment_percentage": round(pct * 100, 1),
                        "user_count": count
                    })

            if birthright_entitlements:
                birthright_roles[job_code] = {
                    "job_code": job_code,
                    "total_users": total_users,
                    "birthright_entitlements": birthright_entitlements
                }

        return birthright_roles

    def detect_role_explosion(self):
        """統合の必要性を示す過度な重複があるロールを特定します。"""
        roles = self.access_data.groupby("job_code").apply(
            lambda x: set(zip(x["application"], x["entitlement"]))
        )

        overlap_report = []
        for (role1, ents1), (role2, ents2) in combinations(roles.items(), 2):
            if len(ents1) == 0 or len(ents2) == 0:
                continue
            overlap = len(ents1 & ents2)
            max_size = max(len(ents1), len(ents2))
            overlap_pct = overlap / max_size * 100

            if overlap_pct > 70:
                overlap_report.append({
                    "role_1": role1,
                    "role_2": role2,
                    "role_1_entitlements": len(ents1),
                    "role_2_entitlements": len(ents2),
                    "overlapping_entitlements": overlap,
                    "overlap_percentage": round(overlap_pct, 1),
                    "recommendation": "CONSOLIDATE" if overlap_pct > 90 else "REVIEW"
                })

        return sorted(overlap_report, key=lambda x: x["overlap_percentage"], reverse=True)

    def find_orphaned_access(self):
        """
        どのロール定義とももはや一致しない権限を見つけます。
        これらは時間の経過とともに蓄積された例外です。
        """
        # 生得権定義を取得
        birthright = self.mine_birthright_roles(min_assignment_pct=0.5)

        orphaned = []
        for _, row in self.access_data.iterrows():
            job_birthright = birthright.get(row["job_code"], {})
            expected_ents = set()
            for ent in job_birthright.get("birthright_entitlements", []):
                expected_ents.add((ent["application"], ent["entitlement"]))

            current_ent = (row["application"], row["entitlement"])
            if current_ent not in expected_ents:
                orphaned.append({
                    "employee_id": row["employee_id"],
                    "job_code": row["job_code"],
                    "application": row["application"],
                    "entitlement": row["entitlement"],
                    "department": row["department"]
                })

ステップ 4: アクセス要求と承認ワークフローを実装する

ユーザーが追加のアクセスを要求し、承認されるメカニズムを作成します:

"""
アクセス要求API
ユーザーが追加のアクセスを要求し、承認者は承認/拒否できます。
"""
from datetime import datetime
from enum import Enum
import uuid

class RequestStatus(Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"
    CANCELLED = "cancelled"
    ESCALATED = "escalated"

class AccessRequest:
    def __init__(self, requester_id, application, entitlement, justification):
        self.request_id = str(uuid.uuid4())
        self.requester_id = requester_id
        self.application = application
        self.entitlement = entitlement
        self.justification = justification
        self.status = RequestStatus.PENDING
        self.created_at = datetime.utcnow()
        self.approval_chain = []
        self.current_approver = None
        self.escalation_level = 0

class AccessRequestWorkflow:
    def __init__(self, approval_policies):
        """
        approval_policies: 承認ロジックを定義するポリシーの辞書
        形式: { "application:entitlement": {
                    "approvers": ["manager", "department_head", "app_owner"],
                    "escalation_minutes": [0, 60, 120]
                }
        }
        """
        self.approval_policies = approval_policies
        self.requests = {}

    def submit_request(self, requester_id, application, entitlement, justification):
        """新しいアクセス要求を送信します。"""
        request = AccessRequest(
            requester_id, application, entitlement, justification
        )
        self.requests[request.request_id] = request

        # 最初の承認者を決定
        policy = self.approval_policies.get(
            f"{application}:{entitlement}",
            self.approval_policies.get("default", {
                "approvers": ["manager"],
                "escalation_minutes": [0]
            })
        )

        request.approval_chain = policy["approvers"]
        request.current_approver = policy["approvers"][0]
        request.escalation_level = 0

        return request.request_id

    def approve_request(self, request_id, approver_id, comments=""):
        """保留中の要求を承認します。"""
        request = self.requests.get(request_id)
        if not request or request.status != RequestStatus.PENDING:
            return False

        # 承認者が現在の承認者であることを確認
        if request.current_approver != approver_id:
            return False

        # 次の承認者に進むか、完了する
        current_index = request.approval_chain.index(approver_id)
        if current_index + 1 < len(request.approval_chain):
            request.current_approver = request.approval_chain[current_index + 1]
            request.status = RequestStatus.PENDING
        else:
            request.status = RequestStatus.APPROVED
            request.approved_at = datetime.utcnow()
            # IGAプラットフォームでプロビジョニングをトリガー
            self._provision_access(request)

        return True

    def reject_request(self, request_id, approver_id, reason=""):
        """保留中の要求を拒否します。"""
        request = self.requests.get(request_id)
        if not request or request.status != RequestStatus.PENDING:
            return False

        request.status = RequestStatus.REJECTED
        request.rejection_reason = reason
        request.rejected_by = approver_id
        request.rejected_at = datetime.utcnow()
        return True

    def check_escalations(self):
        """タイムアウトした要求をエスカレーションします。"""
        now = datetime.utcnow()
        for request_id, request in self.requests.items():
            if request.status != RequestStatus.PENDING:
                continue

            elapsed = (now - request.created_at).total_seconds() / 60
            escalation_minutes = self.approval_policies.get(
                f"{request.application}:{request.entitlement}",
                self.approval_policies.get("default", {"escalation_minutes": [60]})
            )["escalation_minutes"]

            if request.escalation_level < len(escalation_minutes):
                if elapsed > escalation_minutes[request.escalation_level]:
                    request.escalation_level += 1
                    if request.escalation_level < len(request.approval_chain):
                        request.current_approver = request.approval_chain[request.escalation_level]
                        request.status = RequestStatus.ESCALATED

    def _provision_access(self, request):
        """承認されたアクセスプロビジョニングをトリガーします。"""
        # 実際の実装では、ここでIGAプラットフォームAPIを呼び出します
        self.logger.info(f"プロビジョニングアクセス: {request.requester_id} -> {request.application}:{request.entitlement}")

ステップ 5: 定期的な再認定(アクセスレビュー)を実装する

定期的なアクセスレビューキャンペーンを設定します:

"""
アクセスレビューキャンペーンエンジン
定期的なアクセスレビューをスケジュールおよび管理します。
"""
from datetime import datetime, timedelta
from enum import Enum

class ReviewStatus(Enum):
    SCHEDULED = "scheduled"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    OVERDUE = "overdue"

class CertificationCampaign:
    def __init__(self, name, campaign_type, start_date, end_date, reviewers):
        self.name = name
        self.campaign_type = campaign_type  # "SOX", "HIPAA", "GDPR", "GENERAL"
        self.start_date = start_date
        self.end_date = end_date
        self.reviewers = reviewers  # { reviewer_id: [entitlements_to_review] }
        self.status = ReviewStatus.SCHEDULED
        self.review_results = {}  # { (user_id, entitlement): "approve"|"revoke" }
        self.reminder_schedule = [7, 3, 1]  # 残り日数でのリマインダー送信

class CertEngine:
    def __init__(self, access_data, compliance_rules):
        """
        access_data: 現在のアクセス権限
        compliance_rules: コンプライアンス要件の定義
        """
        self.access_data = access_data
        self.compliance_rules = compliance_rules
        self.campaigns = []

    def create_campaign(self, name, campaign_type, due_date):
        """特定のコンプライアンス要件のための新しい再認定キャンペーンを作成します。"""
        # レビュー対象のアクセス決定
        if campaign_type == "SOX":
            # SOXアプリケーションにアクセスできるすべてのユーザーを含める
            relevant_access = self.access_data[
                self.access_data["application"].isin(
                    self.compliance_rules.get("SOX", {}).get("applications", [])
                )
            ]
        elif campaign_type == "HIPAA":
            relevant_access = self.access_data[
                self.access_data["application"].isin(
                    self.compliance_rules.get("HIPAA", {}).get("applications", [])
                )
            ]
        elif campaign_type == "GDPR":
            # EU在住のユーザー、またはEUユーザーデータを処理するすべての人
            relevant_access = self.access_data[
                (self.access_data["region"] == "EU") |
                (self.access_data["application"].isin(
                    self.compliance_rules.get("GDPR", {}).get("data_processing_apps", [])
                ))
            ]
        else:
            relevant_access = self.access_data

        # アクセス所有権に基づいてレビュー担当者に割り当て
        reviews = {}
        for _, row in relevant_access.iterrows():
            # アプリケーションの所有者またはアプリケーションの管理者をレビュー担当者として割り当て
            reviewer = self._determine_reviewer(row["application"])
            if reviewer not in reviews:
                reviews[reviewer] = []
            reviews[reviewer].append(
                (row["employee_id"], row["application"], row["entitlement"])
            )

        campaign = CertificationCampaign(
            name=name,
            campaign_type=campaign_type,
            start_date=datetime.utcnow(),
            end_date=due_date,
            reviewers=reviews
        )

        self.campaigns.append(campaign)
        return campaign

    def submit_review_decision(self, campaign, reviewer_id, decisions):
        """レビュー担当者がアクセスレビューの決定を提出します。"""
        """
        decisions: [{ "user_id": "...", "entitlement": "...", "decision": "approve"|"revoke" }]
        """
        if campaign.status not in [ReviewStatus.IN_PROGRESS, ReviewStatus.OVERDUE]:
            return False

        for decision in decisions:
            key = (decision["user_id"], decision["entitlement"])
            campaign.review_results[key] = decision["decision"]

            if decision["decision"] == "revoke":
                # IGA APIを介してアクセス削除をトリガー
                self._revoke_access(
                    decision["user_id"],
                    decision["application"],
                    decision["entitlement"]
                )

        # すべてのレビューが完了したかどうかを確認
        total_items = sum(len(items) for items in campaign.reviewers.values())
        reviewed_items = len(campaign.review_results)

        if reviewed_items >= total_items:
            campaign.status = ReviewStatus.COMPLETED
            self._generate_certification_report(campaign)

        return True

    def _generate_certification_report(self, campaign):
        """コンプライアンス監査用の再認定レポートを生成します。"""
        report = {
            "campaign_name": campaign.name,
            "campaign_type": campaign.campaign_type,
            "start_date": campaign.start_date.isoformat(),
            "end_date": campaign.end_date.isoformat(),
            "status": campaign.status.value,
            "total_entitlements_reviewed": len(campaign.review_results),
            "approved_count": sum(
                1 for d in campaign.review_results.values() if d == "approve"
            ),
            "revoked_count": sum(
                1 for d in campaign.review_results.values() if d == "revoke"
            ),
            "reviewers": list(campaign.reviewers.keys()),
            "completion_rate": 100.0
        }
        return report

ステップ 6: コンプライアンスレポートと監査証跡を実装する

規制要件のための監査証跡とレポートを維持します:

"""
監査レポートエンジン
コンプライアンス監査のためのアイデンティティアクティビティと権限の包括的なレポートを生成します。
"""
from datetime import datetime
import json

class AuditReportGenerator:
    def __init__(self, identity_store, access_history):
        """
        identity_store: 現在のアイデンティティとアクセスのデータベース
        access_history: アクセス変更の時系列ログ
        """
        self.identity_store = identity_store
        self.access_history = access_history

    def generate_sox_report(self, fiscal_year, fiscal_quarter):
        """SOXコンプライアンス監査のためのレポートを生成します。
        これにはアクセスレビューの完了、ユーザーアクセス権限、
        特権アカウントの変更が含まれます。"""
        report = {
            "report_type": "SOX",
            "fiscal_year": fiscal_year,
            "fiscal_quarter": fiscal_quarter,
            "generated_at": datetime.utcnow().isoformat(),
            "sox_applications": self._get_sox_applications(),
            "user_access_summary": self._get_user_access_summary(),
            "privileged_access_report": self._get_privileged_access(),
            "access_review_completion": self._get_review_completion_rate(),
            "segregation_of_duties_conflicts": self._get_sod_conflicts(),
            "significant_changes": self._get_significant_changes(fiscal_year, fiscal_quarter)
        }
        return report

    def generate_hipaa_report(self, date_range):
        """HIPAAコンプライアンス監査のためのレポートを生成します。
        これにはPHIデータへのアクセス、不正アクセス試行、
        アクセス権限の変更が含まれます。"""
        report = {
            "report_type": "HIPAA",
            "date_range": date_range,
            "generated_at": datetime.utcnow().isoformat(),
            "phi_application_access": self._get_phi_access(),
            "unauthorized_access_attempts": self._get_unauthorized_attempts(),
            "access_modifications": self._get_access_modifications(date_range),
            "terminated_employee_access": self._get_terminated_access(),
            "breach_assessment": self._assess_breach_risk()
        }
        return report

    def generate_gdpr_report(self, data_subject_id=None):
        """GDPRコンプライアンスのためのレポートを生成します。
        データ主体のアクセス要求(DSAR)とデータ処理アクティビティをサポートします。"""
        if data_subject_id:
            # 特定のデータ主体のレポート
            report = {
                "report_type": "GDPR_DSAR",
                "data_subject_id": data_subject_id,
                "generated_at": datetime.utcnow().isoformat(),
                "personal_data": self._get_personal_data(data_subject_id),
                "processing_activities": self._get_processing_activities(data_subject_id),
                "data_retention": self._get_retention_details(data_subject_id),
                "third_party_sharing": self._get_third_party_sharing(data_subject_id)
            }
        else:
            # 組織全体のレポート
            report = {
                "report_type": "GDPR_ORGANIZATIONAL",
                "generated_at": datetime.utcnow().isoformat(),
                "data_processing_register": self._get_processing_register(),
                "data_retention_policies": self._get_retention_policies(),
                "consent_management": self._get_consent_records(),
                "data_breach_log": self._get_breach_log(),
                "data_protection_impact_assessments": self._get_dpias()
            }
        return report

    def export_to_csv(self, report, filename):
        """監査レポートをCSVファイルにエクスポートします。"""
        import csv
        with open(filename, 'w', newline='') as csvfile:
            writer = csv.writer(csvfile)
            # フラット化されたレポートデータを書き込む
            self._flatten_and_write(report, writer)
        return filename

設定リファレンス

以下は、本番環境用にIGAソリューションを設定するためのサンプル設定です:

# IGAプラットフォーム設定
iga:
  platform: "sailpoint"  # または "saviynt", "entra_id"
  api_version: "v3"
  base_url: "https://identity.company.com/api"

# プロビジョニング設定
provisioning:
  batch_size: 100  # バッチあたりのユーザー数
  retry_attempts: 3
  retry_delay_seconds: 60
  timeouts:
    ad_account_creation: 30  # 秒
    mailbox_provisioning: 120
    application_provisioning: 300

# 再認定スケジュール
certification:
  schedules:
    - name: "SOX Quarterly Review"
      frequency: "QUARTERLY"
      months: [3, 6, 9, 12]
      due_day: 15
      reviewer_assignment: "app_owner"
    - name: "HIPAA Monthly Review"
      frequency: "MONTHLY"
      days: [1]
      due_day: 7
      reviewer_assignment: "department_head"
    - name: "GDPR Semi-Annual Review"
      frequency: "SEMI_ANNUAL"
      months: [6, 12]
      due_day: 31
      reviewer_assignment: "data_protection_officer"

# HRソース設定
hr_source:
  type: "workday"  # または "successfactors", "bamboo"
  sync_interval_minutes: 15
  retry_on_failure: true
  fields_to_map:
    employee_id: "Employee_ID"
    first_name: "FirstName"
    last_name: "LastName"
    email: "Email"
    job_code: "Job_Code"
    department: "Department"
    manager_id: "Manager_ID"
    hire_date: "Hire_Date"
    termination_date: "Termination_Date"
    status: "Employment_Status"

トラブルシューティング

一般的な問題と解決策

問題考えられる原因解決策
HR同期がイベントを検出しないAPI認証エラーAPIトークンを更新し、アクセス許可を確認
ユーザーに生得権アクセスがプロビジョニングされないロールマッピングがないroleMiningEngineを使用してジョブコードロールを確認
アクセス要求が承認されない承認ワークフローが不完全approval_policiesの構成を確認
再認定キャンペーンが期限を過ぎているレビュー担当者が割り当てられていないキャンペーン作成を検証し、レビュー担当者を手動で割り当て
退職した従業員がまだアクティブAD無効化が失敗したADコネクタのログを確認し、アカウントを手動で無効化
ロールの乱立が多すぎる役割定義の重複detect_role_explosion()を実行し、推奨に従って統合

ログ取得

# IGAコネクタのログを表示
tail -f /var/log/iga/connector.log

# デバッグモードを有効化
export IGA_LOG_LEVEL=DEBUG

# 監査ログを確認
cat /var/log/iga/audit.log | grep "TERMINATED"

# プロビジョニングエラーを監視
journalctl -u iga-provisioner -f --since "5 minutes ago"

ベストプラクティス

設計上の考慮事項

  1. HRを信頼できる情報源として維持する: アイデンティティデータをHRシステム以外で直接変更しないでください。IGAはHRから変更を読み取り、反映する必要があります。

  2. 最小権限の原則から始める: 新しいロールには生得権アクセスを最小限に抑え、ユーザーが要求を通じて適切なアクセスをリクエストできるようにします。

  3. 変更管理を実装する: ロールの定義、アクセスポリシー、プロビジョニングルールへのすべての変更は、展開前にレビューおよび承認される必要があります。

  4. コンプライアンスのための監査: 規制監査のための明確な証跡を提供するために、すべてのアイデンティティ変更、アクセス要求、承認、レビュー決定をログに記録します。

  5. パフォーマンスを計画する: 大量のHR同期(例:合併、大量採用)中にシステムパフォーマンスを確保するために、バッチ処理と非同期プロビジョニングを使用します。

  6. 例外ケースをテストする: アカウントがHRにないがIGAには存在する場合(シャドウIT)、または重複レコードがある場合など、エッジケースを処理するために堅牢なエラーハンドリングを実装します。

セキュリティ考慮事項

  • IGAプラットフォームAPIへのすべてのアクセスには、サービスアカウントと最小権限のアクセストークンを使用します
  • HRとIGAの間のAPI通信にはTLS 1.2+を強制します
  • 機密性の高い権限(特権アクセス、管理者ロール)には、分離された義務(SoD)チェックを実装します
  • 個人を特定できる情報(PII)を含む監査ログは、アクセス制御された安全な場所に保存します
  • IGAプラットフォーム自体へのアクセスには、多要素認証(MFA)を要求します

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

abusing-dpapi-for-credential-access

Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys) online or offline using SharpDPAPI, SharpChrome, Mimikatz, or Impacket

abusing-shadow-credentials-for-privesc

Take over Active Directory accounts by writing attacker-controlled public keys to msDS-KeyCredentialLink (Shadow Credentials) with pyWhisker, Whisker, or Certipy, then authenticate via PKINIT to recover the target

achieving-cmmc-level-2-compliance

>-

acquiring-disk-image-with-dd-and-dcfldd

Create forensically sound bit-for-bit disk images with dd or dcfldd on a Linux forensic workstation, preserving evidence integrity through hash verification (MD5/SHA) during acquisition. Use when imaging a suspect drive, USB device, or memory card for investigation, preserving volatile disk evidence during incident response, or producing a verified copy for legal or law-enforcement proceedings before any destructive analysis.

analyzing-active-directory-acl-abuse

Detect dangerous ACL misconfigurations in Active Directory using ldap3

analyzing-android-malware-with-apktool

Perform static analysis of Android APK malware using apktool for resource decompilation, jadx for Java source recovery, and androguard for manifest inspection, dangerous permission-combination detection, and identification of obfuscated code, dynamic code loading, and reflection-based API calls. Use to statically triage a suspicious APK without executing it or to build mobile malware detection rules.

analyzing-api-gateway-access-logs

Parses API Gateway access logs (AWS API Gateway, Kong, Nginx) to detect

analyzing-apt-group-with-mitre-navigator

Query ATT&CK data with attackcti, mitreattack-python, and stix2, then build MITRE ATT&CK Navigator layers and multi-layer heatmap overlays mapping one or more APT groups

analyzing-azure-activity-logs-for-threats

Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query

analyzing-bootkit-and-rootkit-samples

Analyzes bootkit and advanced rootkit malware infecting the Master

analyzing-browser-forensics-with-hindsight

Parse Chromium-based browser databases with Hindsight to extract and correlate browsing history, downloads, cookies, cached content, autofill data, saved passwords, and extensions from Chrome, Edge, Brave, Opera, and Vivaldi into a unified timeline (XLSX, JSON, or SQLite output). Use during incident response, insider-threat investigations, or criminal cases when you need to reconstruct a user

analyzing-campaign-attribution-evidence

Systematically evaluate cyber-campaign evidence to attribute an operation to a threat actor, using the Diamond Model and Analysis of Competing Hypotheses (ACH) to weigh infrastructure overlaps, TTP consistency, malware code similarity, and timing/language artifacts into confidence-weighted attribution assessments. Use when an incident investigation needs a defensible attribution confidence level.

analyzing-certificate-transparency-for-phishing

Monitor Certificate Transparency logs using crt.sh and Certstream to

analyzing-cloud-storage-access-patterns

Detect abnormal access in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics for after-hours bulk downloads, new-IP access, and API-call spikes (e.g. GetObject) via statistical baselines and time-series anomaly detection. Use when investigating suspected cloud data exfiltration or building related detection rules.

analyzing-cobalt-strike-beacon-configuration

Extract and analyze Cobalt Strike beacon configuration from PE files

analyzing-cobaltstrike-malleable-c2-profiles

Parse and analyze Cobalt Strike Malleable C2 profiles with dissect.cobaltstrike (profiles and beacon-payload configs) and pyMalleableC2 (AST parsing) to extract HTTP/DNS transforms, URIs, headers, sleep/jitter, and injection behavior, then generate network detection signatures. Use when reverse-engineering a captured malleable profile or building detections against Cobalt Strike Beacon traffic.

analyzing-command-and-control-communication

Analyzes malware C2 communication over HTTP, HTTPS, DNS, and custom

analyzing-cyber-kill-chain

Analyzes intrusion activity against the Lockheed Martin Cyber Kill Chain

analyzing-disk-image-with-autopsy

Perform comprehensive forensic analysis of raw (dd), E01, or AFF disk images with Autopsy and The Sleuth Kit, recovering deleted files, examining metadata and embedded artifacts, keyword searching, and building investigation timelines with visual reports. Use for structured analysis of a forensic disk image or when stakeholders need visual reports from evidence.

analyzing-dns-logs-for-exfiltration

Analyzes DNS query logs to detect data exfiltration via DNS tunneling,

関連スキル