Security Operations

Insider Threat Prevention: Technical Controls and Detection Patterns

A comprehensive guide to insider threat prevention covering malicious, negligent, and compromised insider categories, detection signals, data loss prevention, UEBA, and secure offboarding procedures.

January 15, 20267 min readShipSafer Team

Insider threats are among the most damaging and difficult-to-detect security risks an organization faces. Unlike external attackers who must first breach the perimeter, insiders already have legitimate access to systems, data, and physical spaces. According to Verizon's DBIR, insider incidents take an average of 197 days to detect and cost significantly more than external breaches — partly because organizations are reluctant to assume malicious intent in colleagues.

Three Categories of Insider Threats

Understanding the motivation type shapes which controls are most effective.

Malicious Insiders

Employees, contractors, or partners who intentionally cause harm. Motivations include financial gain (selling data), revenge (after discipline or termination), competitive intelligence (data theft for a future employer), or sabotage.

Indicators:

  • Accessing data outside normal job function
  • Bulk downloading of customer records, source code, or intellectual property
  • Unusual after-hours access patterns
  • Copying large volumes of data to personal devices or external storage
  • Searching for proprietary information not related to current projects
  • Using unapproved cloud services for file transfer

Negligent Insiders

No malicious intent — just careless behavior that creates security incidents. This is the most common category: the developer who pushes secrets to a public GitHub repository, the executive who emails customer data to a personal Gmail account before a conference, the finance employee who falls for a phishing email.

Indicators:

  • Repeated security policy violations
  • Sensitive data appearing in unapproved locations (personal email, unauthorized cloud storage)
  • Unencrypted sensitive data on laptops or portable drives
  • Sharing credentials with colleagues

Compromised Insiders

Legitimate users whose accounts have been taken over by external attackers. The attacker now has all the access of the compromised employee — plus the ability to use that access from inside the trust perimeter. This is the most difficult category to detect because the behavior may initially look legitimate.

Indicators:

  • Login from an unusual geographic location
  • Simultaneous logins from different locations
  • Access at unusual hours inconsistent with the user's time zone
  • Activity that doesn't match the user's normal behavioral baseline

Technical Detection Controls

User and Entity Behavior Analytics (UEBA)

UEBA establishes behavioral baselines for each user and alerts on deviations. The core concept is peer grouping: compare a user's behavior to their cohort (job role, department, access tier) and flag significant deviations.

Key behavioral signals to baseline:

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

@dataclass
class UserBehaviorEvent:
    user_id: str
    event_type: str           # login, file_access, email_send, api_call
    timestamp: datetime
    resource: str             # What was accessed
    source_ip: str
    bytes_transferred: Optional[int]
    destination: Optional[str]

# Behavioral signals to compute per user
BEHAVIORAL_SIGNALS = [
    "login_hour_distribution",          # When does this user normally work?
    "login_geolocation",                # Where do they normally log in from?
    "data_access_volume",               # How many records do they normally access?
    "data_transfer_volume",             # How much data do they normally move?
    "application_usage_pattern",        # What applications do they use?
    "peer_group_deviation",             # How different from their cohort?
    "privilege_usage_rate",             # How often do they use elevated permissions?
]

def calculate_risk_score(
    user_id: str,
    events: list[UserBehaviorEvent],
    baseline: dict[str, float]
) -> float:
    score = 0.0

    # Access outside normal hours (2x baseline risk)
    after_hours_events = [
        e for e in events
        if e.timestamp.hour < 7 or e.timestamp.hour > 20
    ]
    if len(after_hours_events) > baseline.get("after_hours_avg", 0) * 3:
        score += 20.0

    # Bulk data access
    total_bytes = sum(e.bytes_transferred or 0 for e in events)
    if total_bytes > baseline.get("daily_bytes_avg", 0) * 10:
        score += 35.0

    # New geolocation
    current_locations = {e.source_ip for e in events}
    known_locations: set[str] = baseline.get("known_ips", set())
    if current_locations - known_locations:
        score += 25.0

    return min(score, 100.0)

Database Activity Monitoring

Database activity monitoring (DAM) captures every query and identifies anomalous patterns. Key detection rules:

-- PostgreSQL: detect bulk SELECT (potential data exfiltration)
-- Run this query against your audit log
SELECT
    user_name,
    date_trunc('hour', event_time) AS hour,
    COUNT(*) AS query_count,
    SUM(rows_returned) AS total_rows
FROM database_audit_log
WHERE event_type = 'SELECT'
  AND event_time > NOW() - INTERVAL '24 hours'
GROUP BY user_name, hour
HAVING SUM(rows_returned) > 10000
ORDER BY total_rows DESC;

Alert conditions:

  • Any user accessing tables outside their job function (e.g., an engineer querying the payments table)
  • Queries returning more than 1,000 rows from a PII table
  • SELECT * queries on sensitive tables (no column filtering suggests bulk export intent)
  • Queries from a service account at hours when automated jobs don't run

Data Loss Prevention (DLP)

DLP inspects data in motion (email, web uploads, API calls) and at rest (file shares, endpoint storage) for sensitive content.

Classifying sensitive data to protect:

# DLP policy configuration (conceptual)
classification_rules:
  - name: "SSN"
    pattern: '\b\d{3}-\d{2}-\d{4}\b'
    risk_level: critical

  - name: "Credit Card"
    pattern: '\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\b'
    risk_level: critical
    luhn_check: true

  - name: "Internal API Keys"
    pattern: 'sk_live_[a-zA-Z0-9]{24}'
    risk_level: critical

  - name: "Customer Email List"
    threshold: 50  # More than 50 email addresses in a document
    pattern: '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
    risk_level: high

enforcement:
  email_outbound:
    - rule: critical
      action: block_and_alert
    - rule: high
      action: encrypt_and_alert

  web_upload:
    - rule: critical
      action: block
    - rule: high
      action: alert

  cloud_storage:
    - rule: critical
      action: block
    - rule: high
      action: alert

Endpoint DLP: Monitor for USB device connections and large file copies:

# Linux: monitor USB storage device connections via udev
# /etc/udev/rules.d/99-usb-monitor.rules
ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd*",
  RUN+="/usr/local/bin/usb-alert.sh '%k' '%E{ID_VENDOR}' '%E{ID_SERIAL}'"
#!/bin/bash
# usb-alert.sh
DEVICE="$1"
VENDOR="$2"
SERIAL="$3"
USER=$(who | grep -v "root" | head -1 | awk '{print $1}')
HOST=$(hostname)

# Log and alert
logger -t USB-MONITOR "USB device connected: device=${DEVICE} vendor=${VENDOR} serial=${SERIAL} user=${USER}"

curl -s -X POST "${SLACK_WEBHOOK}" \
  -H 'Content-type: application/json' \
  --data "{\"text\":\"USB device connected on ${HOST} by ${USER}: ${VENDOR} (${SERIAL})\"}"

Privilege Access Management

Insider threats are amplified by over-provisioned access. Enforce least privilege with just-in-time (JIT) access:

# AWS IAM Identity Center: request temporary elevated access
# User requests access to production database for 4 hours
aws sso-admin create-account-assignment \
  --instance-arn arn:aws:sso:::instance/ssoins-abc123 \
  --target-id 123456789012 \
  --target-type AWS_ACCOUNT \
  --permission-set-arn arn:aws:sso:::permissionSet/ssoins-abc123/ps-prod-db \
  --principal-type USER \
  --principal-id user-id-abc123

# Automatically revoke after 4 hours via EventBridge + Lambda

All privileged access requests should:

  • Require a ticket number or business justification
  • Be time-limited (never permanent elevation)
  • Be logged with the requester, approver, and justification
  • Trigger an alert to the security team

Secure Offboarding

Malicious insider incidents frequently occur in the window between when an employee gives notice and when their access is actually revoked. A 2023 survey found that 63% of offboarding security incidents involve employees who retained access after their departure.

Automated Offboarding Checklist

from datetime import datetime, timezone
from typing import Protocol

class IdentityProvider(Protocol):
    def disable_user(self, user_id: str) -> None: ...
    def revoke_all_sessions(self, user_id: str) -> None: ...
    def remove_group_memberships(self, user_id: str) -> None: ...

class OffboardingOrchestrator:
    def execute_immediate_revocation(self, employee_id: str) -> dict[str, bool]:
        """Execute on day of termination or immediately on resignation."""
        results: dict[str, bool] = {}

        # 1. Disable SSO account (disables access to all SSO-connected apps)
        results["sso_disabled"] = self._disable_sso_account(employee_id)

        # 2. Revoke all active sessions
        results["sessions_revoked"] = self._revoke_active_sessions(employee_id)

        # 3. Invalidate all API keys and tokens
        results["api_keys_revoked"] = self._revoke_api_keys(employee_id)

        # 4. Remove from all cloud IAM roles (AWS, GCP, Azure)
        results["cloud_access_revoked"] = self._revoke_cloud_access(employee_id)

        # 5. Disable VPN certificates
        results["vpn_revoked"] = self._revoke_vpn_access(employee_id)

        # 6. Disable physical access badges
        results["badge_disabled"] = self._disable_physical_badge(employee_id)

        # 7. Transfer ownership of shared secrets (GitHub, AWS shared accounts)
        results["secrets_transferred"] = self._initiate_secret_rotation(employee_id)

        # Audit trail
        self._log_offboarding_event(employee_id, results)

        return results

Critical Offboarding Items Often Missed

  • Shared accounts: Rotate passwords for any shared service accounts the employee had access to
  • SSH keys: Remove their public keys from all servers and cloud providers
  • Personal devices: Initiate remote wipe if company data was on personal devices (BYOD policy)
  • Service accounts: Check if any service accounts were created in their name (common for CI/CD)
  • GitHub/GitLab: Remove from organization AND revoke any personal access tokens (PATs) they generated
  • Third-party SaaS: Manually check every SaaS tool — many don't get SSO access and require individual deprovisioning

Monitor departing employees' access patterns for the 30 days before their departure date. A spike in data access or unusual downloading activity before resignation is a strong indicator of data theft intent. Having a defensible record of what was accessed also allows you to assess the scope of any incident.

insider threat
dlp
ueba
security operations
access control
offboarding
detection

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.