ServiceNow Hardening Guide
IT service management platform hardening for ServiceNow including SSO configuration, Security Center, and high-security plugins
Overview
ServiceNow is a leading IT service management and business workflow platform used by thousands of enterprises worldwide. As a platform managing critical IT operations and business processes, ServiceNow security configurations directly impact operational integrity.
Intended Audience
- Security engineers managing ITSM platforms
- IT administrators configuring ServiceNow
- GRC professionals assessing IT operations security
- Platform administrators managing instance security
How to Use This Guide
- L1 (Baseline): Essential controls for all organizations
- L2 (Hardened): Enhanced controls for security-sensitive environments
- L3 (Maximum Security): Strictest controls for regulated industries
Scope
This guide covers ServiceNow instance security including SAML SSO, Security Center, high-security plugins, and RBAC.
Table of Contents
- Authentication & SSO
- Security Center & Hardening
- Access Controls
- Monitoring & Compliance
- Compliance Quick Reference
1. Authentication & SSO
1.1 Configure SAML Single Sign-On
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 6.3, 12.5 |
| NIST 800-53 | IA-2, IA-8 |
Description
Configure SAML SSO to centralize authentication for ServiceNow users.
Rationale
Why This Matters:
- ServiceNow recommends SSO with SAML or OIDC
- Enables organizational MFA enforcement
- Required for enterprise security
Prerequisites
- ServiceNow admin access
- SAML 2.0 or OIDC compatible IdP
- Multi-Provider SSO plugin activated
ClickOps Implementation
Step 1: Activate Multi-Provider SSO
- Navigate to: System Definition → Plugins
- Search for Multi-Provider SSO
- Activate plugin if not enabled
Step 2: Create SAML Configuration
- Navigate to: Multi-Provider SSO → Identity Providers
- Click New
- Select SAML as the type
Step 3: Configure IdP Settings
- Enter IdP metadata
- Use your own certificates (recommended)
- FIPS mode requires separate Encryption and Signing certificates
Time to Complete: ~2 hours
Code Pack: API Script
info "1.1 Checking Multi-Provider SSO plugin status..."
SSO_PLUGIN=$(sn_table_get "sys_plugins" \
"sysparm_query=id=com.snc.integration.sso.multi&sysparm_fields=id,active" \
| jq -r '.result[0].active // empty' 2>/dev/null || true)
if [ "${SSO_PLUGIN}" = "active" ]; then
pass "1.1 Multi-Provider SSO plugin is active"
else
fail "1.1 Multi-Provider SSO plugin is NOT active — enable via Plugins"
increment_failed
fi
info "1.1 Checking SSO properties..."
SSO_ENABLED=$(sn_table_get "sys_properties" \
"sysparm_query=name=glide.authenticate.sso.redirect.url&sysparm_fields=name,value" \
| jq -r '.result[0].value // empty' 2>/dev/null || true)
if [ -n "${SSO_ENABLED}" ]; then
pass "1.1 SAML SSO redirect URL is configured: ${SSO_ENABLED}"
else
warn "1.1 SAML SSO redirect URL not found — SSO may not be configured"
fi
# Verify SAML 2.0 IdP records exist
IDP_COUNT=$(sn_table_get "sso_properties" \
"sysparm_query=protocol=saml2&sysparm_fields=name,protocol,is_default" \
| jq -r '.result | length' 2>/dev/null || echo "0")
if [ "${IDP_COUNT}" -gt 0 ]; then
pass "1.1 Found ${IDP_COUNT} SAML 2.0 Identity Provider configuration(s)"
increment_applied
else
fail "1.1 No SAML 2.0 IdP configurations found — configure SSO in Multi-Provider SSO"
increment_failed
fi
1.2 Configure Account Recovery Administrator
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6 |
Description
Configure Account Recovery (ACR) administrator for SSO fallback.
Rationale
Why This Matters:
- ACR provides fallback when SSO fails
- Must be registered before SSO activation
ClickOps Implementation
Step 1: Register ACR Administrator
- Before enabling SSO, register ACR admin
- Navigate to: System Security → Account Recovery
Step 2: Configure ACR Settings
- Enable MFA for ACR users
- Restrict ACR to authorized personnel only
- Document ACR procedures
1.3 Enable Multi-Factor Authentication
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 6.5 |
| NIST 800-53 | IA-2(1) |
Description
Enforce MFA for all authentication methods.
ClickOps Implementation
Step 1: Verify MFA Settings
- MFA enabled by default for local logins
- Navigate to: System Security → Multi-factor Authentication
Step 2: Configure via IdP (SSO)
- Enable MFA in identity provider
- Use phishing-resistant methods for admins (PIV/CAC, FIDO2)
2. Security Center & Hardening
2.1 Configure Security Center
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 4.1 |
| NIST 800-53 | CM-6 |
Description
Use Security Center to monitor and improve instance security.
ClickOps Implementation
Step 1: Access Security Center
- Navigate to: Security Center
- Review overall security score
Step 2: Review Hardening Settings
- Review hardening compliance score
- Determine alignment with SSC recommendations
Step 3: Address Findings
- Test fixes in sub-production first
- Document accepted risks
2.2 Enable High-Security Plugins
Profile Level: L2 (Hardened)
| Framework | Control |
|---|---|
| CIS Controls | 4.1 |
| NIST 800-53 | CM-6 |
Description
Activate high-security plugins for enhanced protection.
ClickOps Implementation
Step 1: Verify Plugin Status
- Navigate to: System Definition → Plugins
- Search for high-security plugins
Step 2: Enable High-Security Settings
- Plugin enables:
- Centralized security settings
- Security administrator role
- Default deny for ACLs
Code Pack: API Script
info "2.2 Checking High Security Settings plugin..."
HS_PLUGIN=$(sn_table_get "sys_plugins" \
"sysparm_query=id=com.glide.security.high&sysparm_fields=id,active" \
| jq -r '.result[0].active // empty' 2>/dev/null || true)
if [ "${HS_PLUGIN}" = "active" ]; then
pass "2.2 High Security Settings plugin is active"
increment_applied
else
fail "2.2 High Security Settings plugin is NOT active"
fail "2.2 Navigate to System Definition > Plugins and activate 'High Security Settings'"
increment_failed
fi
info "2.2 Checking security system properties..."
check_property() {
local prop_name="$1"
local expected="$2"
local description="$3"
local value
value=$(sn_property "${prop_name}")
if [ "${value}" = "${expected}" ]; then
pass "2.2 ${description}: ${prop_name}=${value}"
elif [ -z "${value}" ]; then
warn "2.2 ${description}: ${prop_name} not set (expected: ${expected})"
else
fail "2.2 ${description}: ${prop_name}=${value} (expected: ${expected})"
fi
}
check_property "glide.security.default.deny" "true" \
"Default deny ACLs"
check_property "glide.security.use_secure_cookies" "true" \
"Secure cookies"
check_property "glide.security.strict.xframe" "true" \
"X-Frame-Options strict mode"
check_property "glide.basicauth.required.user_password" "true" \
"Require password for basic auth"
check_property "glide.security.csrf_previous.enabled" "true" \
"CSRF protection"
Code Pack: Sigma Detection Rule
detection:
selection:
tablename: 'sys_properties'
fieldname: 'value'
filter_security_properties:
documentkey|contains:
- 'glide.security.default.deny'
- 'glide.security.use_secure_cookies'
- 'glide.security.strict.xframe'
- 'glide.basicauth.required.user_password'
- 'glide.security.csrf_previous.enabled'
- 'glide.authenticate.multifactor'
condition: selection and filter_security_properties
fields:
- sys_created_on
- user
- tablename
- fieldname
- oldvalue
- newvalue
- documentkey
3. Access Controls
3.1 Configure Role-Based Access Control
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6 |
Description
Implement least privilege using ServiceNow’s role model.
ClickOps Implementation
Step 1: Review Role Structure
- Navigate to: User Administration → Roles
- Review role hierarchy
Step 2: Apply Least Privilege
- Create custom roles for specific functions
- Avoid over-assigning admin roles
- Use role contains for hierarchies
Step 3: Configure ACLs
- Use default deny (high-security plugin)
- Create explicit allows
3.2 Limit Admin Access
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6(1) |
Description
Minimize and protect administrator accounts.
ClickOps Implementation
Step 1: Inventory Admin Users
- Navigate to: User Administration → Users
- Filter by admin roles
Step 2: Apply Least Privilege
- Limit admin to 2-3 users
- Use security_admin for security tasks
Code Pack: API Script
info "3.2 Querying admin role assignments..."
ADMIN_ROLES=$(sn_table_get "sys_user_has_role" \
"sysparm_query=role.name=admin^state=active&sysparm_fields=user.user_name,user.name,user.active,user.last_login_time" \
2>/dev/null || true)
ADMIN_COUNT=$(echo "${ADMIN_ROLES}" | jq -r '.result | length' 2>/dev/null || echo "0")
info "3.2 Found ${ADMIN_COUNT} active admin role assignment(s)"
# List each admin
if [ "${ADMIN_COUNT}" -gt 0 ]; then
echo "${ADMIN_ROLES}" | jq -r '.result[] | " - \(.["user.user_name"] // "unknown") (\(.["user.name"] // "N/A")) — last login: \(.["user.last_login_time"] // "never")"' 2>/dev/null || true
fi
MAX_ADMINS=5
if [ "${ADMIN_COUNT}" -le "${MAX_ADMINS}" ]; then
pass "3.2 Admin count (${ADMIN_COUNT}) is within threshold (<= ${MAX_ADMINS})"
increment_applied
else
fail "3.2 Excessive admin count: ${ADMIN_COUNT} (threshold: ${MAX_ADMINS})"
fail "3.2 Review admin assignments and remove unnecessary privileges"
increment_failed
fi
Code Pack: Sigma Detection Rule
detection:
selection:
tablename: 'sys_user_has_role'
fieldname: 'role'
newvalue|contains: 'admin'
condition: selection
fields:
- sys_created_on
- user
- tablename
- fieldname
- newvalue
- documentkey
4. Monitoring & Compliance
4.1 Configure Audit Logging
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 8.2 |
| NIST 800-53 | AU-2 |
Description
Enable and monitor audit logs for security events.
ClickOps Implementation
Step 1: Review Audit Configuration
- Navigate to: System Logs → Audit
- Verify auditing enabled
Step 2: Configure Audit Policies
- Audit critical tables
- Audit configuration changes
- Audit user management
Step 3: Monitor Audit Logs
- Create audit dashboards
- Set up alerts for suspicious activity
Code Pack: API Script
info "4.1 Checking audit-related system properties..."
AUDIT_ACTIVE=$(sn_property "glide.sys.audit.active")
if [ "${AUDIT_ACTIVE}" = "true" ]; then
pass "4.1 System audit is active (glide.sys.audit.active=true)"
increment_applied
else
fail "4.1 System audit is NOT active — set glide.sys.audit.active=true"
increment_failed
fi
AUDIT_DELETE=$(sn_property "glide.sys.audit.delete")
if [ "${AUDIT_DELETE}" = "true" ]; then
pass "4.1 Delete auditing is enabled (glide.sys.audit.delete=true)"
else
warn "4.1 Delete auditing is not enabled — set glide.sys.audit.delete=true"
fi
LOGIN_TRACKING=$(sn_property "glide.ui.login_tracking")
if [ "${LOGIN_TRACKING}" = "true" ]; then
pass "4.1 Login tracking is enabled (glide.ui.login_tracking=true)"
else
warn "4.1 Login tracking is not enabled — set glide.ui.login_tracking=true"
fi
info "4.1 Querying recent audit log entries..."
RECENT_AUDITS=$(sn_table_get "sys_audit" \
"sysparm_query=ORDERBYDESCsys_created_on&sysparm_limit=10&sysparm_fields=sys_created_on,user,tablename,fieldname,newvalue,oldvalue" \
2>/dev/null || true)
AUDIT_COUNT=$(echo "${RECENT_AUDITS}" | jq -r '.result | length' 2>/dev/null || echo "0")
if [ "${AUDIT_COUNT}" -gt 0 ]; then
pass "4.1 Audit log is operational — found ${AUDIT_COUNT} recent entries"
info "4.1 Latest audit entries:"
echo "${RECENT_AUDITS}" | jq -r '.result[:5][] | " - \(.sys_created_on) | \(.user) | \(.tablename).\(.fieldname) | \(.oldvalue // "-") -> \(.newvalue // "-")"' 2>/dev/null || true
else
fail "4.1 No recent audit entries found — verify audit configuration"
increment_failed
fi
Code Pack: Sigma Detection Rule
detection:
selection_audit_access:
tablename: 'sys_audit'
selection_deletion:
tablename: 'sys_audit_delete'
selection_export:
tablename|contains: 'sys_audit'
fieldname: 'export'
condition: selection_audit_access or selection_deletion or selection_export
fields:
- sys_created_on
- user
- tablename
- fieldname
- newvalue
- oldvalue
5. Compliance Quick Reference
SOC 2 Trust Services Criteria Mapping
| Control ID | ServiceNow Control | Guide Section |
|---|---|---|
| CC6.1 | SSO/MFA | 1.1 |
| CC6.2 | RBAC | 3.1 |
| CC7.1 | Security Center | 2.1 |
| CC7.2 | Audit logging | 4.1 |
NIST 800-53 Rev 5 Mapping
| Control | ServiceNow Control | Guide Section |
|---|---|---|
| IA-2 | SSO | 1.1 |
| IA-2(1) | MFA | 1.3 |
| AC-6 | Least privilege | 3.1 |
| CM-6 | Security hardening | 2.2 |
| AU-2 | Audit logging | 4.1 |
Appendix A: References
Official ServiceNow Documentation:
- ServiceNow Documentation
- ServiceNow Security Best Practices Guide
- Security Center Hardening
- Instance Security Hardening Reference
- SAML 2.0 Configuration
API & Developer Resources:
Trust & Compliance:
- ServiceNow Trust and Compliance Center
- ServiceNow TrustShare Certifications
- SOC 1 Type II, SOC 2 Type II, ISO 27001, ISO 27017, ISO 27018, ISO 27701, ISO 9001, ISO 22301, ISO 42001 – via ServiceNow Compliance
Security Incidents:
- BodySnatcher / CVE-2025-12420 (October 2025): A critical vulnerability in the ServiceNow Virtual Agent API and Now Assist AI Agents allowed unauthenticated attackers to impersonate any user (including admins) using only an email address, bypassing MFA and SSO. Patched by ServiceNow on October 30, 2025. No evidence of exploitation in the wild.
- Template Injection CVEs (May 2024): Three vulnerabilities (CVE-2024-4879, CVE-2024-5178, CVE-2024-5217) were patched same day of disclosure but saw in-the-wild exploitation attempts across 6,000+ sites before patching was complete, primarily targeting financial services.
Changelog
| Date | Version | Maturity | Changes | Author |
|---|---|---|---|---|
| 2025-02-05 | 0.1.0 | draft | Initial guide with SSO, Security Center, and access controls | Claude Code (Opus 4.5) |
Contributing
Found an issue or want to improve this guide?
- Report outdated information: Open an issue with tag
content-outdated - Propose new controls: Open an issue with tag
new-control - Submit improvements: See Contributing Guide