mcpField
D
data-classification

data-classification

github.com/happy-technologies-llcセキュリティ4141 回インストール2 日前に更新
リポジトリ

data-classification について

ServiceNowの管理者やセキュリティチーム向けに、機密フィールドの検出、データマスキングの設定、分類ラベルの適用を支援するスキルです。PII/PHIスキャンとコンプライアンスレポートにも対応します。

主な機能

  1. 01機密データフィールドの検出
  2. 02PII/PHIのスキャン
  3. 03動的またはロールベースのマスキング設定
  4. 04テーブルとフィールドの分類
  5. 05分類・GDPRレポートの生成

活用シーン

  1. 01ServiceNowインスタンスのセキュリティ評価
  2. 02GDPR、HIPAA、PCI-DSS監査の準備
  3. 03非本番環境向けデータマスキングの設定
  4. 04データガバナンスポリシーの策定

MCP 対応

SN-Discover-Table-Schema、SN-Query-Table、SN-Execute-Background-ScriptなどのMCPツールでスキーマを調査し、データをスキャンして分類情報を管理します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/happy-technologies-llc/happy-platform-skills --skill data-classification

インストール後、Claude Code で/data-classificationを入力して読み込まれたか確認します。

詳細

データ分類

概要

データ分類は、セキュリティ、コンプライアンス、ガバナンスにとって重要です。このスキルでは、次の内容を扱います。

  • ServiceNow 全体で機密データフィールドを検出し、特定する
  • PII/PHI を保護するためのデータマスキング戦略を実施する
  • パターンとヒューリスティックを用いて PII/PHI を自動検出する
  • テーブルとフィールドに分類ラベルを適用する
  • GDPR、HIPAA、PCI-DSS、その他の規制に準拠する

使用する場面:

  • ServiceNow インスタンスの初期セキュリティ評価
  • コンプライアンス監査(GDPR、HIPAA、SOX、PCI-DSS)の準備
  • データプライバシー制御の実施
  • 非本番環境向けのデータマスキングの設定
  • データガバナンスポリシーの作成

前提条件

  • ロール: admin、security_admin、data_privacy_admin
  • アクセス権: sys_dictionary、sys_db_object、フィールドレベルのメタデータ
  • プラグイン: Data Classification(任意)、Encryption(保存データの暗号化用)
  • 知識: 規制要件(GDPR、HIPAA、PCI-DSS)

データ分類フレームワーク

分類レベル
レベルラベル説明例
1公開制限なしKB 記事、会社情報
2社内従業員のみアクセス可能ポリシー、組織図
3機密知る必要がある人のみ財務データ、人事情報
4制限付き厳格な管理PII、PHI、認証情報
5秘密最大限の保護暗号化キー、SSN
機密データのカテゴリ
カテゴリ説明規制
PII個人を特定できる情報GDPR、CCPA
PHI保護対象の医療情報HIPAA
PCIペイメントカード業界のデータPCI-DSS
財務情報銀行/財務記録SOX、GLBA
認証情報パスワード、トークン、キーすべて

手順

フェーズ 1:機密フィールドの検出
1.1 PII フィールドの自動検出

一般的な PII フィールドのパターンをスキャンします:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var piiPatterns = [
      // Name patterns
      { pattern: /^(first|last|middle|full)_?name$/i, category: 'PII', type: 'Name' },
      { pattern: /^name$/i, category: 'PII', type: 'Name' },

      // Contact patterns
      { pattern: /email/i, category: 'PII', type: 'Email' },
      { pattern: /phone|mobile|fax|tel/i, category: 'PII', type: 'Phone' },
      { pattern: /address|street|city|state|zip|postal|country/i, category: 'PII', type: 'Address' },

      // Identity patterns
      { pattern: /ssn|social.*security|sin|national.*id/i, category: 'PII', type: 'National ID' },
      { pattern: /passport/i, category: 'PII', type: 'Passport' },
      { pattern: /driver.*license|licence/i, category: 'PII', type: 'License' },
      { pattern: /dob|birth.*date|date.*birth/i, category: 'PII', type: 'DOB' },

      // Financial patterns
      { pattern: /credit.*card|card.*number|pan|cc_/i, category: 'PCI', type: 'Credit Card' },
      { pattern: /cvv|cvc|security.*code/i, category: 'PCI', type: 'Card Security' },
      { pattern: /bank.*account|account.*number|routing/i, category: 'Financial', type: 'Bank Account' },
      { pattern: /salary|wage|compensation|income/i, category: 'Financial', type: 'Compensation' },

      // Health patterns
      { pattern: /medical|health|diagnosis|treatment|condition/i, category: 'PHI', type: 'Medical' },
      { pattern: /insurance.*id|policy.*number|member.*id/i, category: 'PHI', type: 'Insurance' },
      { pattern: /prescription|medication|drug/i, category: 'PHI', type: 'Medication' },

      // Authentication patterns
      { pattern: /password|passwd|pwd/i, category: 'Credentials', type: 'Password' },
      { pattern: /secret|token|api.*key/i, category: 'Credentials', type: 'Secret' },
      { pattern: /private.*key|cert|certificate/i, category: 'Credentials', type: 'Certificate' }
    ];

    var findings = [];
    var dict = new GlideRecord('sys_dictionary');
    dict.addQuery('internal_type', '!=', 'collection');
    dict.addNullQuery('element').addOrCondition('element', '!=', '');
    dict.query();

    while (dict.next()) {
      var fieldName = dict.element.toString();
      var tableName = dict.name.toString();

      if (!fieldName || fieldName.startsWith('sys_')) continue;

      for (var i = 0; i < piiPatterns.length; i++) {
        if (piiPatterns[i].pattern.test(fieldName)) {
          findings.push({
            table: tableName,
            field: fieldName,
            category: piiPatterns[i].category,
            type: piiPatterns[i].type,
            label: dict.column_label.toString()
          });
          break;
        }
      }
    }

    gs.info('PII FIELD DISCOVERY RESULTS: ' + findings.length + ' potential PII fields found');
    gs.info(JSON.stringify(findings, null, 2));
  description: "Scan instance for potential PII fields"
1.2 機密データが含まれるテーブルスキーマの検出

特定のテーブルを詳しく調査します:

Tool: SN-Discover-Table-Schema
Parameters:
  table_name: sys_user
  include_inherited: true

フィールドの詳細をクエリします:

Tool: SN-Query-Table
Parameters:
  table_name: sys_dictionary
  query: name=sys_user^elementISNOTEMPTY
  fields: element,column_label,internal_type,max_length,read_only
  limit: 100
1.3 実データが含まれるフィールドの検索

機密フィールドの値のサンプル(マスキング済み):

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var sensitiveFields = [
      { table: 'sys_user', field: 'email', mask: true },
      { table: 'sys_user', field: 'phone', mask: true },
      { table: 'sys_user', field: 'mobile_phone', mask: true },
      { table: 'customer_contact', field: 'email', mask: true }
    ];

    var results = [];

    for (var i = 0; i < sensitiveFields.length; i++) {
      var config = sensitiveFields[i];

      var gr = new GlideRecord(config.table);
      gr.addNotNullQuery(config.field);
      gr.setLimit(5);
      gr.query();

      var samples = [];
      while (gr.next()) {
        var value = gr[config.field].toString();
        if (config.mask && value) {
          // Mask all but first/last 2 characters
          if (value.length > 4) {
            value = value.substring(0, 2) + '***' + value.substring(value.length - 2);
          } else {
            value = '***';
          }
        }
        samples.push(value);
      }

      if (samples.length > 0) {
        results.push({
          table: config.table,
          field: config.field,
          populated: true,
          sample_count: samples.length,
          samples: samples
        });
      }
    }

    gs.info('SENSITIVE FIELD SAMPLE (MASKED):');
    gs.info(JSON.stringify(results, null, 2));
  description: "Sample sensitive field data (masked)"
フェーズ 2:データマスキング戦略
2.1 動的データマスキング

マスク表示用のビジネスルールを作成します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Example: Create a script include for data masking
    var scriptContent =
      "var DataMasker = Class.create();\n" +
      "DataMasker.prototype = {\n" +
      "  initialize: function() {},\n" +
      "  \n" +
      "  maskEmail: function(email) {\n" +
      "    if (!email) return '';\n" +
      "    var parts = email.split('@');\n" +
      "    if (parts.length != 2) return '***@***';\n" +
      "    var user = parts[0];\n" +
      "    var domain = parts[1];\n" +
      "    if (user.length > 2) {\n" +
      "      user = user.charAt(0) + '***' + user.charAt(user.length-1);\n" +
      "    }\n" +
      "    return user + '@' + domain;\n" +
      "  },\n" +
      "  \n" +
      "  maskPhone: function(phone) {\n" +
      "    if (!phone) return '';\n" +
      "    var digits = phone.replace(/\\D/g, '');\n" +
      "    if (digits.length >= 10) {\n" +
      "      return '***-***-' + digits.slice(-4);\n" +
      "    }\n" +
      "    return '***';\n" +
      "  },\n" +
      "  \n" +
      "  maskSSN: function(ssn) {\n" +
      "    if (!ssn) return '';\n" +
      "    var digits = ssn.replace(/\\D/g, '');\n" +
      "    if (digits.length >= 4) {\n" +
      "      return '***-**-' + digits.slice(-4);\n" +
      "    }\n" +
      "    return '***-**-****';\n" +
      "  },\n" +
      "  \n" +
      "  maskCreditCard: function(cc) {\n" +
      "    if (!cc) return '';\n" +
      "    var digits = cc.replace(/\\D/g, '');\n" +
      "    if (digits.length >= 4) {\n" +
      "      return '****-****-****-' + digits.slice(-4);\n" +
      "    }\n" +
      "    return '****-****-****-****';\n" +
      "  },\n" +
      "  \n" +
      "  type: 'DataMasker'\n" +
      "};";

    gs.info('Data Masker Script Include:\n' + scriptContent);
  description: "Generate data masking script include"
2.2 フィールドレベルの暗号化

暗号化機能を確認します:

Tool: SN-Query-Table
Parameters:
  table_name: sys_properties
  query: nameLIKEencrypt
  fields: name,value,description
  limit: 20

フィールドの暗号化を有効にします:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Check if encryption is configured
    var encryptProp = gs.getProperty('glide.security.encryption.module');
    gs.info('Encryption Module: ' + encryptProp);

    // To encrypt a field, update sys_dictionary
    // Set 'encrypt_on_write' = true for the field
    var dict = new GlideRecord('sys_dictionary');
    dict.addQuery('name', 'u_custom_table');
    dict.addQuery('element', 'ssn_field');
    dict.query();

    if (dict.next()) {
      gs.info('Current encryption setting: ' + dict.encrypt_context);
      // dict.encrypt_context = 'system';
      // dict.update();
    }
  description: "Check and configure field encryption"
2.3 ロールベースのデータマスキング

マスキング用の表示ビジネスルールを作成します:

Tool: SN-Create-Record
Parameters:
  table_name: sys_script
  data:
    name: "Mask Sensitive Data Display"
    table: sys_user
    when: display
    order: 100
    active: true
    script: |
      (function executeRule(current, previous /*null when async*/) {

        // Only mask for non-privileged users
        if (gs.hasRole('security_admin') || gs.hasRole('hr_admin')) {
          return; // Full access
        }

        // Mask email for general users
        if (current.email && !gs.hasRole('itil')) {
          var email = current.email.toString();
          var parts = email.split('@');
          if (parts.length == 2) {
            current.email.setDisplayValue(parts[0].charAt(0) + '***@' + parts[1]);
          }
        }

        // Mask phone for general users
        if (current.phone && !gs.hasRole('hr_user')) {
          var phone = current.phone.toString().replace(/\D/g, '');
          if (phone.length >= 4) {
            current.phone.setDisplayValue('***-***-' + phone.slice(-4));
          }
        }

      })(current, previous);
    description: "Masks sensitive user data based on viewer's roles"
フェーズ 3:PII/PHI の検出
3.1 包括的な PII スキャン

インスタンス全体の PII スキャンを実行します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var piiReport = {
      scan_date: new GlideDateTime().toString(),
      tables_scanned: 0,
      fields_checked: 0,
      pii_fields_found: [],
      phi_fields_found: [],
      pci_fields_found: [],
      recommendations: []
    };

    // PII field name patterns
    var piiPatterns = [
      /email/i, /phone/i, /mobile/i, /address/i, /zip/i, /postal/i,
      /first.*name/i, /last.*name/i, /birth/i, /dob/i, /ssn/i,
      /social.*security/i, /passport/i, /driver/i, /license/i
    ];

    var phiPatterns = [
      /medical/i, /health/i, /diagnos/i, /treatment/i, /condition/i,
      /prescription/i, /medication/i, /insurance/i, /patient/i
    ];

    var pciPatterns = [
      /credit.*card/i, /card.*number/i, /cvv/i, /cvc/i, /pan/i,
      /expir/i, /bank.*account/i, /routing/i
    ];

    // Get all custom tables
    var tables = new GlideRecord('sys_db_object');
    tables.addQuery('name', 'STARTSWITH', 'u_');
    tables.addOrCondition('name', 'IN', 'sys_user,customer_contact,customer_account,hr_core_profile');
    tables.query();

    while (tables.next()) {
      piiReport.tables_scanned++;
      var tableName = tables.name.toString();

      var dict = new GlideRecord('sys_dictionary');
      dict.addQuery('name', tableName);
      dict.addNotNullQuery('element');
      dict.query();

      while (dict.next()) {
        piiReport.fields_checked++;
        var fieldName = dict.element.toString();
        var fieldLabel = dict.column_label.toString();

        // Check against patterns
        for (var i = 0; i < piiPatterns.length; i++) {
          if (piiPatterns[i].test(fieldName) || piiPatterns[i].test(fieldLabel)) {
            piiReport.pii_fields_found.push({
              table: tableName,
              field: fieldName,
              label: fieldLabel
            });
            break;
          }
        }

        for (var j = 0; j < phiPatterns.length; j++) {
          if (phiPatterns[j].test(fieldName) || phiPatterns[j].test(fieldLabel)) {
            piiReport.phi_fields_found.push({
              table: tableName,
              field: fieldName,
              label: fieldLabel
            });
            break;
          }
        }

        for (var k = 0; k < pciPatterns.length; k++) {
          if (pciPatterns[k].test(fieldName) || pciPatterns[k].test(fieldLabel)) {
            piiReport.pci_fields_found.push({
              table: tableName,
              field: fieldName,
              label: fieldLabel
            });
            break;
          }
        }
      }
    }

    // Generate recommendations
    if (piiReport.pii_fields_found.length > 0) {
      piiReport.recommendations.push('Review PII fields for GDPR/CCPA compliance');
      piiReport.recommendations.push('Implement data masking for non-production environments');
      piiReport.recommendations.push('Create ACLs to restrict PII field access');
    }

    if (piiReport.phi_fields_found.length > 0) {
      piiReport.recommendations.push('Ensure HIPAA BAA is in place with ServiceNow');
      piiReport.recommendations.push('Enable audit logging on all PHI tables');
      piiReport.recommendations.push('Implement PHI encryption at rest');
    }

    if (piiReport.pci_fields_found.length > 0) {
      piiReport.recommendations.push('PCI data should NOT be stored in ServiceNow');
      piiReport.recommendations.push('Consider tokenization for payment data');
      piiReport.recommendations.push('Review PCI-DSS compliance requirements');
    }

    gs.info('PII/PHI/PCI DISCOVERY REPORT:\n' + JSON.stringify(piiReport, null, 2));
  description: "Comprehensive PII/PHI/PCI discovery scan"
3.2 データコンテンツのスキャン

実データをスキャンして機密パターンを検出します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Regex patterns for detecting sensitive data in content
    var patterns = {
      email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
      phone: /(\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g,
      ssn: /\d{3}[-\s]?\d{2}[-\s]?\d{4}/g,
      creditCard: /\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}/g,
      ipv4: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g
    };

    // Tables and fields to scan for embedded PII
    var scanTargets = [
      { table: 'incident', field: 'description' },
      { table: 'incident', field: 'comments' },
      { table: 'sc_req_item', field: 'special_instructions' },
      { table: 'kb_knowledge', field: 'text' }
    ];

    var findings = [];

    for (var i = 0; i < scanTargets.length; i++) {
      var target = scanTargets[i];

      var gr = new GlideRecord(target.table);
      gr.setLimit(100); // Limit for performance
      gr.query();

      while (gr.next()) {
        var content = gr[target.field].toString();

        for (var patternName in patterns) {
          var matches = content.match(patterns[patternName]);
          if (matches && matches.length > 0) {
            findings.push({
              table: target.table,
              field: target.field,
              record: gr.getDisplayValue(),
              sys_id: gr.sys_id.toString(),
              pattern: patternName,
              count: matches.length
            });
          }
        }
      }
    }

    if (findings.length > 0) {
      gs.warn('EMBEDDED PII DETECTED: ' + findings.length + ' records contain potential PII');
      gs.info(JSON.stringify(findings.slice(0, 20), null, 2)); // First 20
    } else {
      gs.info('No embedded PII detected in scanned content');
    }
  description: "Scan record content for embedded PII patterns"
フェーズ 4:分類ラベル
4.1 分類ラベルの作成

分類ラベルを定義します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Check if data classification table exists
    if (GlideTableDescriptor.isValid('sys_data_classification')) {
      var labels = [
        { name: 'Public', level: 1, description: 'Information that can be freely shared' },
        { name: 'Internal', level: 2, description: 'For internal use only' },
        { name: 'Confidential', level: 3, description: 'Sensitive business information' },
        { name: 'Restricted', level: 4, description: 'PII, PHI, or regulated data' },
        { name: 'Secret', level: 5, description: 'Maximum protection required' }
      ];

      for (var i = 0; i < labels.length; i++) {
        var gr = new GlideRecord('sys_data_classification');
        gr.addQuery('name', labels[i].name);
        gr.query();

        if (!gr.hasNext()) {
          gr.initialize();
          gr.name = labels[i].name;
          gr.level = labels[i].level;
          gr.description = labels[i].description;
          gr.insert();
          gs.info('Created classification: ' + labels[i].name);
        } else {
          gs.info('Classification exists: ' + labels[i].name);
        }
      }
    } else {
      // Use custom table or properties
      gs.info('Data Classification plugin not installed');
      gs.info('Consider using sys_properties or custom table for labels');
    }
  description: "Create data classification labels"
4.2 テーブルへの分類の適用

テーブルの分類を保存します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Use sys_properties to store table classifications
    var tableClassifications = {
      'sys_user': 'Restricted',
      'customer_contact': 'Restricted',
      'incident': 'Internal',
      'kb_knowledge': 'Public',
      'hr_core_profile': 'Secret',
      'ast_contract': 'Confidential'
    };

    for (var table in tableClassifications) {
      var propName = 'data.classification.' + table;
      var propValue = tableClassifications[table];

      var prop = new GlideRecord('sys_properties');
      prop.addQuery('name', propName);
      prop.query();

      if (prop.next()) {
        prop.value = propValue;
        prop.update();
        gs.info('Updated: ' + propName + ' = ' + propValue);
      } else {
        prop.initialize();
        prop.name = propName;
        prop.value = propValue;
        prop.description = 'Data classification for ' + table + ' table';
        prop.type = 'string';
        prop.insert();
        gs.info('Created: ' + propName + ' = ' + propValue);
      }
    }
  description: "Apply data classifications to tables"
4.3 フィールドレベルの分類

機密フィールドに分類を適用します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var fieldClassifications = [
      { table: 'sys_user', field: 'email', classification: 'Restricted', reason: 'PII - Contact' },
      { table: 'sys_user', field: 'phone', classification: 'Restricted', reason: 'PII - Contact' },
      { table: 'sys_user', field: 'home_phone', classification: 'Restricted', reason: 'PII - Contact' },
      { table: 'sys_user', field: 'mobile_phone', classification: 'Restricted', reason: 'PII - Contact' },
      { table: 'hr_core_profile', field: 'ssn', classification: 'Secret', reason: 'PII - National ID' },
      { table: 'hr_core_profile', field: 'date_of_birth', classification: 'Restricted', reason: 'PII - DOB' },
      { table: 'customer_contact', field: 'email', classification: 'Restricted', reason: 'PII - Contact' }
    ];

    for (var i = 0; i < fieldClassifications.length; i++) {
      var fc = fieldClassifications[i];
      var propName = 'data.classification.' + fc.table + '.' + fc.field;

      var prop = new GlideRecord('sys_properties');
      prop.addQuery('name', propName);
      prop.query();

      if (prop.next()) {
        prop.value = fc.classification;
        prop.update();
      } else {
        prop.initialize();
        prop.name = propName;
        prop.value = fc.classification;
        prop.description = fc.reason;
        prop.type = 'string';
        prop.insert();
      }

      gs.info('Classified: ' + fc.table + '.' + fc.field + ' as ' + fc.classification);
    }
  description: "Apply field-level data classifications"
フェーズ 5:コンプライアンスレポート
5.1 分類レポートの生成

データ分類インベントリを作成します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var report = {
      generated: new GlideDateTime().toString(),
      summary: {
        total_tables: 0,
        classified_tables: 0,
        total_fields: 0,
        classified_fields: 0
      },
      by_level: {
        'Public': { tables: [], fields: [] },
        'Internal': { tables: [], fields: [] },
        'Confidential': { tables: [], fields: [] },
        'Restricted': { tables: [], fields: [] },
        'Secret': { tables: [], fields: [] }
      },
      unclassified: {
        sensitive_tables: [],
        sensitive_fields: []
      }
    };

    // Get table classifications
    var props = new GlideRecord('sys_properties');
    props.addQuery('name', 'STARTSWITH', 'data.classification.');
    props.query();

    while (props.next()) {
      var propName = props.name.toString();
      var classification = props.value.toString();
      var parts = propName.replace('data.classification.', '').split('.');

      if (parts.length === 1) {
        // Table classification
        report.summary.classified_tables++;
        if (report.by_level[classification]) {
          report.by_level[classification].tables.push(parts[0]);
        }
      } else if (parts.length === 2) {
        // Field classification
        report.summary.classified_fields++;
        if (report.by_level[classification]) {
          report.by_level[classification].fields.push(parts[0] + '.' + parts[1]);
        }
      }
    }

    // Count total tables
    var tables = new GlideAggregate('sys_db_object');
    tables.addAggregate('COUNT');
    tables.query();
    if (tables.next()) {
      report.summary.total_tables = parseInt(tables.getAggregate('COUNT'));
    }

    // Count total fields
    var fields = new GlideAggregate('sys_dictionary');
    fields.addNotNullQuery('element');
    fields.addAggregate('COUNT');
    fields.query();
    if (fields.next()) {
      report.summary.total_fields = parseInt(fields.getAggregate('COUNT'));
    }

    gs.info('DATA CLASSIFICATION REPORT:\n' + JSON.stringify(report, null, 2));
  description: "Generate data classification report"
5.2 GDPR データマップ

GDPR 準拠のデータマップを生成します:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var gdprMap = {
      generated: new GlideDateTime().toString(),
      data_controller: 'Your Organization Name',
      personal_data_inventory: [],
      lawful_basis: {},
      retention_periods: {},
      third_party_sharing: []
    };

    // Find all PII fields
    var piiFields = [
      { table: 'sys_user', fields: ['first_name', 'last_name', 'email', 'phone', 'mobile_phone', 'home_phone', 'location'] },
      { table: 'customer_contact', fields: ['first_name', 'last_name', 'email', 'phone', 'mobile_phone'] },
      { table: 'hr_core_profile', fields: ['first_name', 'last_name', 'date_of_birth', 'ssn', 'address', 'city', 'state', 'zip'] }
    ];

    for (var i = 0; i < piiFields.length; i++) {
      var tableInfo = piiFields[i];

      // Count records
      var gr = new GlideAggregate(tableInfo.table);
      gr.addAggregate('COUNT');
      gr.query();
      var recordCount = 0;
      if (gr.next()) {
        recordCount = parseInt(gr.getAggregate('COUNT'));
      }

      gdprMap.personal_data_inventory.push({
        table: tableInfo.table,
        fields: tableInfo.fields,
        record_count: recordCount,
        data_subjects: 'Employees/Customers',
        purpose: 'Business operations',
        lawful_basis: 'Legitimate interest / Contract performance'
      });
    }

    // Define retention periods
    gdprMap.retention_periods = {
      'sys_user': 'Duration of employment + 7 years',
      'customer_contact': 'Duration of relationship + 5 years',
      'incident': '3 years after closure',
      'hr_core_profile': 'Duration of employment + 7 years'
    };

    gs.info('GDPR DATA MAP:\n' + JSON.stringify(gdprMap, null, 2));
  description: "Generate GDPR data map"

ツールの使用概要

操作MCP ツールREST エンドポイント
スキーマの検出SN-Discover-Table-SchemaGET /sys_dictionary
フィールドのクエリSN-Query-TableGET /sys_dictionary
スキャンの実行SN-Execute-Background-Scriptsys_trigger 経由
分類の作成SN-Create-RecordPOST /sys_properties
分類の更新SN-Update-RecordPATCH /sys_properties
テーブル情報の取得SN-Get-Table-SchemaGET /sys_dictionary

ベストプラクティス

  • まずはインベントリ作成: 管理策を実装する前に、データの検出を完了します
  • リスクベースのアプローチ: 最もリスクの高いデータ(PII、PHI、PCI)を優先します
  • 多層防御: ACL、暗号化、マスキング、監査を組み合わせます
  • 定期スキャン: PII 検出スキャンを定期的に実施するようスケジュールします
  • すべてを文書化: 監査に備えてデータ分類記録を維持します
  • 非本番環境でのマスキング: 非本番環境では、本番データを必ずマスキングします
  • 最小権限: 分類済みデータへのアクセスは、必要な場合にのみ許可します

トラブルシューティング

データのマスキングが適用されない

症状: ユーザーにマスキングされていないデータが表示される
原因: 表示ビジネスルールが実行されていない
解決策: ビジネスルールの順序と条件を確認し、ロールのチェックを検証します

PII スキャンでフィールドが検出されない

症状: PII と判明しているフィールドが検出されない
原因: フィールド名がパターンに一致しない
解決策: 組織固有の命名規則に対応するカスタムパターンを追加します

分類が保持されない

症状: 分類がリセットされる、または失われる
原因: プロパティが正しいスコープに作成されていない
解決策: sys_properties のスコープと更新セットへの取り込みを確認します

暗号化によるパフォーマンスの低下

症状: 暗号化されたフィールドへのクエリが遅い
原因: 暗号化によりインデックスが使用できない
解決策: 本当に機密性の高いフィールドのみを暗号化し、検索には参照フィールドを使用します

関連スキル

  • security/acl-management - 分類済みデータへのアクセスを制限する
  • security/audit-compliance - 機密データへのアクセスを監査する
  • security/incident-response - データ侵害に対応する
  • grc/risk-identification - 機密データの取り扱いに関連するリスクを特定する

参考資料

コメント