mcpField
I
instance-security

instance-security

github.com/serac-labs安全7758 次安装9 周前更新
代码仓库

关于 instance-security

面向 ServiceNow 实例安全管理的技能,涵盖认证、授权、会话与安全加固,并提供 ES5 示例及相关 MCP 工具工作流。

主要功能

  1. 01密码策略与 MFA 检查
  2. 02会话安全检查
  3. 03输入校验与注入防护
  4. 04安全属性与事件审计
  5. 05安全健康检查

适用场景

  1. 01检查实例密码与会话设置
  2. 02审查 ACL 和安全事件
  3. 03测试安全脚本与健康检查

MCP 相关

文档介绍了通过 snow_property_manage、snow_execute_script 和 snow_query_table 查询安全设置、测试脚本及审查 ACL。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serac-labs/serac --skill instance-security

安装后在 Claude Code 中输入/instance-security确认已加载。

详细介绍

ServiceNow 实例安全

实例安全涵盖身份验证、授权和安全加固。

安全层

Network Security
    ↓
Authentication (SSO, MFA)
    ↓
Session Management
    ↓
Authorization (ACLs, Roles)
    ↓
Data Protection
    ↓
Audit & Logging

关键表

表用途
sys_user用户帐户
sys_user_role角色
sys_security_acl访问控制
sysevent_log安全事件
sys_properties安全设置

身份验证安全(ES5)

密码策略配置
// Check password strength (ES5 ONLY!)
function validatePasswordStrength(password) {
  var issues = []

  // Minimum length
  var minLength = parseInt(gs.getProperty("glide.security.password.min_length", "8"), 10)
  if (password.length < minLength) {
    issues.push("Password must be at least " + minLength + " characters")
  }

  // Complexity requirements
  if (!/[A-Z]/.test(password)) {
    issues.push("Password must contain uppercase letter")
  }
  if (!/[a-z]/.test(password)) {
    issues.push("Password must contain lowercase letter")
  }
  if (!/[0-9]/.test(password)) {
    issues.push("Password must contain number")
  }
  if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
    issues.push("Password must contain special character")
  }

  return {
    valid: issues.length === 0,
    issues: issues,
  }
}
会话安全
// Check session validity (ES5 ONLY!)
function isSessionSecure() {
  var session = gs.getSession()

  // Check session age
  var maxAge = parseInt(gs.getProperty("glide.security.session.timeout", "60"), 10)
  var sessionAge = session.getSessionAge() / 60000 // Convert to minutes

  if (sessionAge > maxAge) {
    return { valid: false, reason: "Session expired" }
  }

  // Check for session fixation
  var clientIP = gs.getSession().getClientIP()
  var originalIP = session.getValue("original_ip")

  if (originalIP && clientIP !== originalIP) {
    return { valid: false, reason: "IP address changed" }
  }

  return { valid: true }
}

// Force re-authentication
function requireReauthentication(reason) {
  gs.getSession().invalidate()
  gs.addErrorMessage(reason)
  // Redirect to login
  response.sendRedirect("/login.do")
}

MFA 实现(ES5)

检查 MFA 状态
// Check if user has MFA enabled (ES5 ONLY!)
function hasMFAEnabled(userSysId) {
  var user = new GlideRecord("sys_user")
  if (!user.get(userSysId)) {
    return false
  }

  // Check MFA settings
  var mfa = new GlideRecord("sys_user_mfa")
  mfa.addQuery("user", userSysId)
  mfa.addQuery("active", true)
  mfa.query()

  return mfa.hasNext()
}

// Enforce MFA for sensitive operations
function requireMFA(operation) {
  var userId = gs.getUserID()

  if (!hasMFAEnabled(userId)) {
    gs.addErrorMessage("MFA required for " + operation)
    return false
  }

  // Check if MFA verified this session
  var session = gs.getSession()
  var mfaVerified = session.getValue("mfa_verified")

  if (mfaVerified !== "true") {
    // Trigger MFA challenge
    gs.eventQueue("user.mfa.challenge", null, userId, operation)
    return false
  }

  return true
}

输入验证(ES5)

防止 XSS
// Sanitize user input (ES5 ONLY!)
function sanitizeInput(input) {
  if (!input) return ""

  // Encode HTML entities
  var sanitized = input
    .toString()
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#x27;")

  return sanitized
}

// Validate and sanitize before storage
function validateUserInput(tableName, fieldName, value) {
  // Check field type
  var dict = new GlideRecord("sys_dictionary")
  dict.addQuery("name", tableName)
  dict.addQuery("element", fieldName)
  dict.query()

  if (!dict.next()) {
    return { valid: false, error: "Field not found" }
  }

  var fieldType = dict.getValue("internal_type")

  // Validate based on type
  if (fieldType === "string" || fieldType === "html") {
    // Check for script injection
    if (/<script/i.test(value)) {
      return { valid: false, error: "Script tags not allowed" }
    }
    // Check for event handlers
    if (/on\w+\s*=/i.test(value)) {
      return { valid: false, error: "Event handlers not allowed" }
    }
  }

  return { valid: true, sanitized: sanitizeInput(value) }
}
防止 SQL 注入
// Safe query building (ES5 ONLY!)
// NEVER concatenate user input directly into queries

// BAD - Vulnerable to injection
// gr.addEncodedQuery('name=' + userInput);

// GOOD - Use parameterized queries
function safeQuery(tableName, fieldName, value) {
  var gr = new GlideRecord(tableName)

  // GlideRecord methods handle escaping
  gr.addQuery(fieldName, value)
  gr.query()

  return gr
}

// For encoded queries, validate input
function safeEncodedQuery(tableName, userQuery) {
  // Whitelist allowed fields
  var allowedFields = ["number", "short_description", "state", "priority"]

  // Parse and validate query
  var parts = userQuery.split("^")
  var safeQuery = []

  for (var i = 0; i < parts.length; i++) {
    var part = parts[i]
    var match = part.match(/^(\w+)(=|!=|LIKE|CONTAINS)(.+)$/)

    if (match) {
      var field = match[1]
      if (allowedFields.indexOf(field) !== -1) {
        safeQuery.push(part)
      }
    }
  }

  var gr = new GlideRecord(tableName)
  if (safeQuery.length > 0) {
    gr.addEncodedQuery(safeQuery.join("^"))
  }
  gr.query()

  return gr
}

安全属性(ES5)

关键安全设置
// Get security-related properties (ES5 ONLY!)
function getSecuritySettings() {
  return {
    // Session settings
    session_timeout: gs.getProperty("glide.security.session.timeout"),
    session_cookie_secure: gs.getProperty("glide.security.session.cookie.secure"),

    // Password settings
    password_min_length: gs.getProperty("glide.security.password.min_length"),
    password_history: gs.getProperty("glide.security.password.history"),
    password_expiry: gs.getProperty("glide.security.password.expiry"),

    // Login settings
    max_failed_logins: gs.getProperty("glide.security.password.max_failed_logins"),
    lockout_duration: gs.getProperty("glide.security.password.lockout_duration"),

    // General security
    csrf_protection: gs.getProperty("glide.security.csrf.strict_validation"),
    xss_protection: gs.getProperty("glide.security.xss.strict"),
  }
}

// Update security property
function setSecurityProperty(name, value, requireRestart) {
  gs.setProperty(name, value)

  if (requireRestart) {
    gs.warn("Security property changed: " + name + ". Restart may be required.")
  }

  // Log security change
  gs.eventQueue("security.property.changed", null, name, value)
}

安全审计(ES5)

记录安全事件
// Log security event (ES5 ONLY!)
function logSecurityEvent(eventType, details) {
  var log = new GlideRecord("syslog")
  log.initialize()

  log.setValue("level", "warning")
  log.setValue("source", "Security")
  log.setValue("message", eventType + ": " + JSON.stringify(details))

  log.insert()

  // Also queue for security monitoring
  gs.eventQueue("security.event", null, eventType, JSON.stringify(details))
}

// Track failed login attempts
function trackFailedLogin(username, ipAddress) {
  logSecurityEvent("failed_login", {
    username: username,
    ip: ipAddress,
    timestamp: new GlideDateTime().getDisplayValue(),
  })

  // Check for brute force
  var recentFailures = countRecentFailures(username, 5) // Last 5 minutes
  var maxFailures = parseInt(gs.getProperty("glide.security.password.max_failed_logins", "5"), 10)

  if (recentFailures >= maxFailures) {
    lockAccount(username)
    logSecurityEvent("account_locked", {
      username: username,
      reason: "Too many failed login attempts",
      failures: recentFailures,
    })
  }
}
安全运行状况检查
// Check instance security health (ES5 ONLY!)
function securityHealthCheck() {
  var issues = []

  // Check for default admin password
  var admin = new GlideRecord("sys_user")
  if (admin.get("user_name", "admin")) {
    if (admin.getValue("password") === gs.getProperty("glide.security.default.admin.password")) {
      issues.push({ severity: "critical", issue: "Default admin password not changed" })
    }
  }

  // Check session timeout
  var timeout = parseInt(gs.getProperty("glide.security.session.timeout", "0"), 10)
  if (timeout === 0 || timeout > 60) {
    issues.push({ severity: "high", issue: "Session timeout too long or disabled" })
  }

  // Check password policy
  var minLength = parseInt(gs.getProperty("glide.security.password.min_length", "0"), 10)
  if (minLength < 12) {
    issues.push({ severity: "medium", issue: "Password minimum length less than 12" })
  }

  // Check HTTPS enforcement
  if (gs.getProperty("glide.security.session.cookie.secure") !== "true") {
    issues.push({ severity: "high", issue: "Secure cookies not enforced" })
  }

  return {
    healthy: issues.length === 0,
    issues: issues,
  }
}

MCP 工具集成

可用工具
工具用途
snow_property_manage (action=get)检查安全属性
snow_execute_script测试安全脚本
snow_query_table审查 ACL
示例工作流
// 1. Check security properties
await snow_property_manage({
  action: "get",
  name: "glide.security.session.timeout",
})

// 2. Run security health check
await snow_execute_script({
  script: `
        var health = securityHealthCheck();
        gs.info(JSON.stringify(health));
    `,
})

// 3. Review access controls
//    truncate_output is REQUIRED here: `script` is on snow_query_table's
//    truncation list, so without it every ACL script comes back as its first
//    200 characters plus "... [truncated, N chars total]".
await snow_query_table({
  table: "sys_security_acl",
  query: "active=true^admin_overrides=true",
  fields: ["name", "operation", "type", "script"],
  truncate_output: false,
})

如需完整读取一个 ACL 脚本,且完全不受截断规则影响,请使用 snow_get_by_sysid({ table: "sys_security_acl", sys_id })——该方法不会进行截断。关于 Table API 读取会返回和不会返回的其他内容,请参阅 table-api-reads 技能。

最佳实践

  1. 强密码 - 强制实施复杂度要求
  2. MFA - 为特权用户启用
  3. 会话超时 - 15-30 分钟
  4. 输入验证 - 对所有输入进行清理
  5. 最小权限 - 仅分配最低限度的角色
  6. 审计日志记录 - 记录安全事件
  7. 定期审查 - 开展安全评估
  8. 仅使用 ES5 - 不使用现代 JavaScript 语法

评论

支持 Markdown