osquery for Linux Security: Threat Hunting, Fleet Monitoring, and SIEM Integration in 2026

osquery turns your Linux fleet into a SQL-queryable database for real-time threat hunting and endpoint telemetry. This practical guide walks through osquery 5.22 deployment, high-value security queries, FleetDM at scale, SIEM integration, and a frank take on osquery vs auditd in 2026.

osquery Linux Threat Hunting Guide 2026

osquery is the quiet backbone of modern open-source endpoint detection on Linux. Instead of writing custom agents or wrestling with audit.rules syntax (which, honestly, has aged like milk), you query your servers the way you'd query a database: with plain old SQL. In 2026, with osquery 5.22 shipping native BPF process auditing, startup-run scheduled queries, and improved socket telemetry, it's become a genuine alternative to commercial EDR for teams who prefer transparent, self-hosted tooling.

So, this guide is a practical field manual — not a marketing overview. You'll deploy osquery on Debian/Ubuntu and RHEL-family systems, write SQL queries aligned with MITRE ATT&CK, centralize results with FleetDM, ship logs to Elasticsearch or Splunk, and figure out exactly where osquery fits next to auditd, Falco, and Wazuh — tools we've already covered in our intrusion detection and runtime security articles.

Why osquery Still Wins in 2026

Three properties make osquery durable where plenty of other agents have churned or outright disappeared:

  • SQL is the API. Every table — processes, listening_ports, users, deb_packages, shell_history, kernel_modules — is queryable with standard SELECT, JOIN, and WHERE. There's no DSL to learn, which matters more than people admit.
  • One agent, many platforms. The same query runs on Ubuntu, RHEL, Debian, Amazon Linux, Alpine (via extensions), macOS, and Windows. That's genuinely rare for Linux-centric tooling.
  • Open, inspectable, airgap-friendly. osquery is Apache-2.0 licensed, signed releases since 5.14 use a Microsoft Azure-issued signing key, and the daemon phones home nowhere by default.

The trade-off? osquery isn't a kernel-resident agent. A root-level attacker can tamper with osqueryd — full stop. Pair it with eBPF-based runtime detection (Falco or Tetragon) when you need tamper-resistance. Our Falco vs Tetragon comparison digs into that layer.

Architecture: How osquery Sees Your System

osquery ships three binaries:

  • osqueryi — an interactive REPL for ad-hoc queries. Your first stop for learning (and, frankly, the most fun part).
  • osqueryd — the long-running daemon that executes scheduled query packs and streams results to configured logger plugins.
  • osqueryctl — a small helper for starting, stopping, and configuring the daemon.

Under the hood, tables are backed by virtual tables implemented in C++ or loaded as extensions. Each one reads from a specific OS subsystem: /proc, Netlink, the Audit framework, BPF programs, D-Bus, and so on. Queries get parsed by SQLite and dispatched to the relevant table implementations.

On Linux, osquery can power its process_events and socket_events tables from three sources:

  1. Audit subsystem (classic, stable; conflicts with auditd)
  2. BPF (enabled with --enable_bpf_events=true; low overhead, non-conflicting)
  3. OpenBSM / EndpointSecurity (macOS only)

For new deployments on modern kernels (5.8+), BPF is the right default. I'd honestly skip the Audit backend entirely unless you have a specific compliance reason not to.

Installing osquery 5.22 on Linux

Use the official repository. Avoid third-party builds — the packages are signed, and the repo is mirrored on GitHub releases for airgapped environments.

Debian and Ubuntu (22.04, 24.04, 26.04)

# Add the osquery signing key and repository
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://pkg.osquery.io/deb/pubkey.gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/osquery.gpg

echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/osquery.gpg] https://pkg.osquery.io/deb deb main" \
  | sudo tee /etc/apt/sources.list.d/osquery.list

sudo apt-get update
sudo apt-get install -y osquery

osqueryi --version
# osqueryi version 5.22.1

RHEL, AlmaLinux, Rocky, Fedora

sudo rpm --import https://pkg.osquery.io/rpm/GPG

sudo tee /etc/yum.repos.d/osquery-s3.repo <<'EOF'
[osquery-s3-rpm]
name=osquery RPM repository
baseurl=https://pkg.osquery.io/rpm
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://pkg.osquery.io/rpm/GPG
EOF

sudo dnf install -y osquery

Minimal production configuration

Drop this into /etc/osquery/osquery.conf. It enables BPF-backed events, sets sane logging, and schedules a couple of high-value queries to get you started.

{
  "options": {
    "config_plugin": "filesystem",
    "logger_plugin": "filesystem",
    "logger_path": "/var/log/osquery",
    "disable_logging": "false",
    "log_result_events": "true",
    "schedule_splay_percent": "10",
    "pidfile": "/var/osquery/osquery.pidfile",
    "events_expiry": "3600",
    "database_path": "/var/osquery/osquery.db",
    "verbose": "false",
    "worker_threads": "2",
    "enable_monitor": "true",
    "disable_events": "false",
    "enable_bpf_events": "true",
    "audit_allow_process_events": "false",
    "audit_allow_sockets": "false"
  },
  "schedule": {
    "process_events_bpf": {
      "query": "SELECT pid, path, cmdline, cwd, auid, uid, gid, parent, time FROM process_events;",
      "interval": 60,
      "removed": false
    },
    "listening_ports_snapshot": {
      "query": "SELECT pid, port, protocol, address, path FROM listening_ports WHERE address NOT IN ('127.0.0.1','::1');",
      "interval": 300,
      "snapshot": true
    }
  },
  "packs": {
    "incident-response": "/opt/osquery/packs/incident-response.conf",
    "vuln-management":   "/opt/osquery/packs/vuln-management.conf",
    "hardware-monitoring": "/opt/osquery/packs/hardware-monitoring.conf"
  }
}

Enable and start the daemon:

sudo systemctl enable --now osqueryd
sudo systemctl status osqueryd
sudo tail -f /var/log/osquery/osqueryd.results.log

Important: if auditd is already running and you plan to use the Audit backend, stop and mask it. Both osqueryd and auditd open the audit netlink socket and they will fight over it. With enable_bpf_events=true you can leave auditd alone — which is another reason BPF is the friendlier default.

The 20 osquery Queries Every Linux Security Engineer Should Know

Drop into osqueryi and try these. They map directly to MITRE ATT&CK techniques and cover the highest-value detection surfaces on Linux.

Persistence & initial access

-- T1053.003 Cron jobs (system + per-user)
SELECT * FROM crontab;

-- T1543.002 systemd services modified in last 7 days
SELECT name, path, source, user
FROM systemd_units
WHERE source LIKE '/etc/systemd/%'
  AND (SELECT mtime FROM file WHERE path = systemd_units.source) > (strftime('%s','now') - 604800);

-- T1546.004 Shell init files touched by non-root
SELECT f.path, f.uid, f.mtime, u.username
FROM file f
JOIN users u ON f.uid = u.uid
WHERE f.path IN ('/etc/profile','/etc/bashrc','/etc/bash.bashrc')
   OR f.path LIKE '/home/%/.bashrc'
   OR f.path LIKE '/home/%/.profile'
   OR f.path LIKE '/home/%/.bash_profile';

-- T1136 New local users in the last day
SELECT username, uid, gid, shell, directory
FROM users
WHERE uid >= 1000 AND uid < 65534;

Process and execution

-- T1059 Processes running from world-writable or tmp paths
SELECT p.pid, p.name, p.path, p.cmdline, p.cwd, u.username
FROM processes p
LEFT JOIN users u ON p.uid = u.uid
WHERE p.path LIKE '/tmp/%'
   OR p.path LIKE '/dev/shm/%'
   OR p.path LIKE '/var/tmp/%';

-- T1055 Orphaned processes (parent died - possible injection / daemon)
SELECT pid, name, path, cmdline, parent
FROM processes
WHERE parent = 1 AND name NOT IN ('systemd','init','dbus-daemon');

-- T1562.001 Deleted binaries still executing (classic LD_PRELOAD / fileless)
SELECT pid, name, path
FROM processes
WHERE on_disk = 0;

Network and lateral movement

-- T1021 Outbound connections by non-system users
SELECT p.pid, p.name, p.cmdline,
       pos.remote_address, pos.remote_port, u.username
FROM process_open_sockets pos
JOIN processes p   ON p.pid  = pos.pid
JOIN users     u   ON u.uid  = p.uid
WHERE pos.remote_address NOT IN ('','0.0.0.0','::','127.0.0.1','::1')
  AND pos.family IN (2, 10)
  AND u.uid >= 1000;

-- T1571 Listening ports not bound to loopback
SELECT pid, port, protocol, address, path
FROM listening_ports
WHERE address NOT IN ('127.0.0.1','::1');

Credential access

-- T1552.003 Readable shell history with secrets-like strings
SELECT username, history_file, command
FROM shell_history
WHERE command LIKE '%AWS_SECRET_ACCESS_KEY%'
   OR command LIKE '%BEGIN RSA PRIVATE KEY%'
   OR command REGEXP '(api[_-]?key|token|password)\s*=';

-- T1552.004 World-readable private keys
SELECT path, (SELECT mode FROM file WHERE file.path = user_ssh_keys.path) AS mode,
       uid, encrypted
FROM user_ssh_keys
WHERE encrypted = 0;

Defense evasion & rootkits

-- T1547.006 Non-stock kernel modules
SELECT name, size, used_by, status, address
FROM kernel_modules
WHERE name NOT IN (SELECT name FROM kernel_info); -- refine per distro baseline

-- T1014 Preloaded libraries (LD_PRELOAD persistence)
SELECT * FROM process_envs WHERE key IN ('LD_PRELOAD','LD_AUDIT','LD_LIBRARY_PATH');

-- Hidden files in /etc or /var
SELECT path FROM file
WHERE (path LIKE '/etc/.%' OR path LIKE '/var/.%')
  AND type = 'regular';

Integrity & package state

-- Packages installed outside the package manager (Debian)
SELECT * FROM deb_packages WHERE source = '';

-- Binaries on PATH whose hash changed in last 24h
SELECT f.path, f.mtime, h.sha256
FROM file f
JOIN hash h ON h.path = f.path
WHERE f.path IN ('/usr/bin/sshd','/usr/sbin/sshd','/usr/bin/sudo','/usr/bin/su','/bin/login')
  AND f.mtime > (strftime('%s','now') - 86400);

Those thirteen queries already cover a meaningful slice of the ATT&CK Linux matrix. Save them into query packs so they run on a schedule across the fleet, not as one-off curiosities.

Building Query Packs

A pack is a versioned JSON file that groups related queries, each with its own interval, platform filter, and ATT&CK annotation. Here's a lean incident-response pack:

{
  "platform": "linux",
  "version": "1.6.0",
  "queries": {
    "suid_binaries_non_stock": {
      "query": "SELECT f.path, f.mode, f.uid, h.sha256 FROM file f JOIN hash h ON h.path = f.path WHERE f.path LIKE '/usr/bin/%' AND (f.mode LIKE '%4755%' OR f.mode LIKE '%6755%') AND f.path NOT IN ('/usr/bin/sudo','/usr/bin/passwd','/usr/bin/chsh','/usr/bin/chfn','/usr/bin/gpasswd','/usr/bin/newgrp','/usr/bin/mount','/usr/bin/umount','/usr/bin/su','/usr/bin/pkexec');",
      "interval": 3600,
      "description": "Non-baseline SUID binaries",
      "snapshot": true
    },
    "rogue_containers": {
      "query": "SELECT id, name, image, command, created, state, status FROM docker_containers WHERE state='running' AND privileged=1;",
      "interval": 600,
      "description": "Privileged running Docker containers"
    },
    "rogue_listeners": {
      "query": "SELECT p.name, p.path, lp.port, lp.address, lp.protocol FROM listening_ports lp JOIN processes p ON p.pid = lp.pid WHERE lp.address NOT IN ('127.0.0.1','::1') AND p.name NOT IN ('sshd','nginx','systemd-resolve','chronyd');",
      "interval": 300,
      "description": "Unexpected network listeners"
    },
    "auth_failures_burst": {
      "query": "SELECT username, path, time FROM last WHERE type=6 AND time > (strftime('%s','now') - 3600);",
      "interval": 300,
      "description": "Failed logins in the last hour"
    }
  }
}

Reference the pack in osquery.conf via the packs key. Community-maintained packs — ChainGuard's threat-hunting queries, the official osquery-defense pack, and AWS's aws-linux-baseline — are excellent starting material. Fork them, trim the parts that don't apply to you, and adapt.

Managing osquery at Scale with FleetDM

Running osqueryd on one box is useful. Running it on 500 is where FleetDM earns its keep. Fleet is an open-source (MIT) control plane that distributes configs, runs live queries, schedules packs, and aggregates results — basically the glue you'd otherwise end up writing yourself.

Deploying FleetDM on Linux

Fleet needs MySQL 8.0+ and Redis 6+. Here's a minimal single-node deployment with Docker Compose:

mkdir -p /opt/fleet && cd /opt/fleet

cat > docker-compose.yml <<'EOF'
services:
  mysql:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: fleet
      MYSQL_USER: fleet
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes: [ "mysql:/var/lib/mysql" ]
    command: --default-authentication-plugin=caching_sha2_password
  redis:
    image: redis:7-alpine
    volumes: [ "redis:/data" ]
  fleet:
    image: fleetdm/fleet:latest
    depends_on: [ mysql, redis ]
    environment:
      FLEET_MYSQL_ADDRESS: mysql:3306
      FLEET_MYSQL_DATABASE: fleet
      FLEET_MYSQL_USERNAME: fleet
      FLEET_MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      FLEET_REDIS_ADDRESS: redis:6379
      FLEET_SERVER_TLS: "true"
      FLEET_SERVER_CERT: /certs/fleet.crt
      FLEET_SERVER_KEY:  /certs/fleet.key
    ports: [ "8080:8080" ]
    volumes: [ "./certs:/certs:ro" ]
volumes:
  mysql: {}
  redis: {}
EOF

# One-time DB prep
docker compose run --rm fleet fleet prepare db
docker compose up -d

Initialize the first admin at https://fleet.example.com:8080, then generate an enroll secret and push the Fleet-issued osquery.flags and secret file to each host. The fleetctl CLI makes this declarative, which is where it really starts to shine:

# Install fleetctl
curl -L https://github.com/fleetdm/fleet/releases/latest/download/fleetctl_linux.tar.gz | tar xz
sudo install fleetctl /usr/local/bin/

fleetctl config set --address https://fleet.example.com:8080
fleetctl login --email [email protected]

# Apply a query pack as code
cat > incident-response.yml <<'EOF'
apiVersion: v1
kind: pack
spec:
  name: incident-response
  targets:
    labels: [ "All Linux Hosts" ]
  queries:
    - query: suid_binaries_non_stock
      interval: 3600
      snapshot: true
EOF
fleetctl apply -f incident-response.yml

Everything in Fleet — users, teams, labels, queries, packs, MDM profiles — is GitOps-able. Store the manifests in a repo, gate changes behind review, and apply with fleetctl apply from CI. Once you've done this once, going back to clicking through a web UI feels barbaric.

Live queries for incident response

When a breach alert fires, you need answers in seconds, not minutes. Fleet's live query feature fans a query out to matching hosts and streams results back in real time:

fleetctl query --hosts label:linux-prod \
  --query "SELECT pid, name, path, cmdline FROM processes WHERE path LIKE '/tmp/%';"

This is the killer feature over pure file-based logging. During the 2024 XZ backdoor disclosure (remember that panic?), responders used nearly identical osquery live queries to find vulnerable liblzma versions across tens of thousands of hosts in minutes. Try doing that by SSH-ing into each box.

Shipping Results to a SIEM

Filesystem logging is fine on a laptop. In production you want every osqueryd.results.log line parsed, indexed, and correlated. Three paths dominate:

Option 1 — Elastic Stack via Filebeat

# /etc/filebeat/filebeat.yml
filebeat.inputs:
  - type: filestream
    id: osquery-results
    paths: [ /var/log/osquery/osqueryd.results.log ]
    parsers:
      - ndjson:
          keys_under_root: true
          add_error_key: true
processors:
  - add_host_metadata: ~
  - add_fields:
      target: osquery
      fields:
        data_stream.dataset: osquery.results
output.elasticsearch:
  hosts: [ "https://elastic.internal:9200" ]
  api_key: "${ELASTIC_API_KEY}"

Elastic ships a prebuilt osquery integration with ECS-compliant dashboards and detection rules — install it from Kibana's Fleet UI and point Filebeat at it. That's it. You're done.

Option 2 — Splunk via the TA for osquery

Install the Splunk Technology Add-on for osquery, configure a monitor stanza on /var/log/osquery/, and the data lands as the osquery:results sourcetype. Splunk Security Content ships dozens of osquery-powered correlation rules (e.g., Linux Auditd Osquery Service Stop), so you can get detections live on day one.

Option 3 — Loki / Grafana for cost-sensitive shops

# promtail config fragment
scrape_configs:
  - job_name: osquery
    static_configs:
      - targets: [ localhost ]
        labels:
          job: osquery
          host: ${HOSTNAME}
          __path__: /var/log/osquery/osqueryd.results.log
    pipeline_stages:
      - json:
          expressions:
            name: name
            action: action
            hostIdentifier: hostIdentifier
            unixTime: unixTime
      - timestamp:
          source: unixTime
          format: Unix

Loki's log-based metrics make it trivial to alert on something like rate of new SUID binaries per host per hour — a cheap and surprisingly effective detection.

osquery vs auditd: When to Use Which

This is the single most common question we get, and it deserves a straight answer. The honest truth is that they solve overlapping but not identical problems.

Dimensionosqueryauditd
InterfaceSQL over virtual tablesRule-based audit.rules
Data shapePoint-in-time state + event streamsSyscall-level event stream
Cross-platformLinux, macOS, Windows, FreeBSDLinux only
Fleet managementNative (FleetDM)Bring-your-own (Ansible + SIEM)
Forensic fidelityMedium (BPF or Audit-backed)High (raw syscall data, tamper-evident)
Compliance mappingEasy (SQL + packs)Strong for CIS / STIG rule IDs
Resource overhead~50–150 MB RSS, ~1–3% CPULow, but logs are voluminous
ConflictCannot share audit netlink with auditdDedicated audit subscriber

Our recommendation for 2026:

  • Use osquery with the BPF backend as your primary visibility and threat-hunting layer. It gives analysts an ergonomic SQL interface across the fleet.
  • Keep auditd running for compliance when a regulator explicitly names it (STIG, PCI DSS) — but narrow the rule set ruthlessly and forward only the rule IDs that actually map to controls. A noisy auditd is nobody's friend.
  • Add Falco or Tetragon for tamper-resistant, kernel-level runtime detection — a layer osquery simply can't match.

Hardening osquery Itself

osquery is a privileged daemon with read access to the entire system. Treat it accordingly.

1. Lock down the configuration and database

sudo chown -R root:root /etc/osquery /var/osquery /var/log/osquery
sudo chmod 0750 /etc/osquery /var/osquery
sudo chmod 0640 /etc/osquery/osquery.conf
sudo chmod 0750 /var/log/osquery
# Ensure the osquery user cannot read results logs; only root and your log shipper's group.

2. Restrict who can stop or reconfigure the service

On RHEL with SELinux, confine osqueryd in its own domain and only let the auditadm_r role stop it. On Ubuntu, use AppArmor profiles plus systemd's ProtectSystem=strict, NoNewPrivileges=true, and CapabilityBoundingSet=CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_NET_ADMIN CAP_SYS_ADMIN. See our systemd service hardening guide for the full template.

3. Detect tampering with osquery from osquery

-- Alert if osqueryd was killed or restarted outside of package upgrades
SELECT name, path, start_time, parent FROM processes WHERE name = 'osqueryd';

-- Alert on config file edits
SELECT path, mtime, size FROM file
WHERE path = '/etc/osquery/osquery.conf'
  AND mtime > (strftime('%s','now') - 3600);

Combine with a Falco rule for process killed or traced by non-root to catch the scenario where an attacker tries to stop osqueryd to clear their tracks. (Yes, they'll try. Assume they'll try.)

4. Verify package signatures

Since osquery 5.14, releases are signed with a Microsoft Azure-issued key. Your package manager already verifies this; do not bypass with --nogpgcheck or --allow-unauthenticated. I don't care how much of a hurry you're in.

Common Pitfalls and How to Avoid Them

  • Leaving the audit backend and auditd both enabled. They fight over the netlink socket. Pick one. If you truly need both, run auditd in auditd -n mode on a separate rule set and use enable_bpf_events=true for osquery.
  • Scheduling SELECT * FROM processes every 10 seconds. Processes is an expensive table. Use process_events (a delta stream) instead of snapshotting the entire process table every few seconds — your ops team will thank you.
  • Ignoring schedule_splay_percent. Without splay, every host in the fleet runs the same heavy query at the same second and brownouts your SIEM. Default to 10%.
  • No baseline for SUID / kernel modules / systemd units. Detection queries like "non-stock SUID" are only useful when you know what stock is. Generate the baseline from a freshly built golden image, check it into git, and diff at query time.
  • Shipping raw results without an index strategy. osquery results are verbose. In Elasticsearch, use ILM (hot/warm/cold) and keep only the tables you actually query for more than 7 days. Your infra bill will notice.

When osquery Is Not the Right Tool

Be honest with yourself about these cases:

  • Sub-second detection latency. osquery events have an inherent buffer. For true real-time kernel enforcement, use Tetragon or a commercial EDR with kernel hooks.
  • Blocking / prevention. osquery is read-only. It can detect a malicious process, but it can't kill it. Pair it with Falco-sidekick or a SOAR that can actually issue remediation.
  • Ephemeral serverless / FaaS. A daemon with state doesn't fit AWS Lambda. Reach for CloudTrail, runtime sandboxing, and code signing instead.
  • Hostile insiders with root. A sufficiently privileged attacker will disable or blind osquery. Assume compromise and put your tamper-detection somewhere they can't reach.

FAQ

Is osquery still maintained in 2026?

Yes. The Linux Foundation hosts the project, osquery 5.22.1 shipped in February 2026, and minor releases land roughly every two months. The project switched to a Microsoft Azure-issued signing key in 5.14, which put earlier concerns about release-key custody to bed.

Can osquery replace my commercial EDR?

For small-to-medium Linux fleets with a capable security team — yes, paired with Falco or Tetragon for kernel-level runtime detection and a SIEM for correlation. For larger environments, or teams that need 24/7 MDR, commercial EDR still wins on response automation and SOC workflows. Just not on raw visibility.

Does osquery need the internet to function?

No. osqueryd has zero outbound dependencies by default. Packages can be mirrored, and FleetDM deploys fully on-prem or airgapped. This is a frequent deciding factor for regulated and sovereign environments — and, honestly, a refreshing change from most modern security tools.

What's the performance overhead of osquery on a busy Linux server?

Typical production hosts see 50–150 MB RSS and 1–3% sustained CPU with the default pack set and BPF events enabled. Heavy scheduled queries (like hashing every file in /usr/bin hourly) can push this higher — profile with SELECT * FROM osquery_schedule;, which includes per-query execution stats.

Should I use FleetDM or Kolide K2?

Use FleetDM (self-hosted, MIT-licensed) when you need full data control, airgap support, or want to GitOps your endpoint management. Use Kolide K2 (SaaS) when you value end-user privacy transparency, Slack-based compliance nudges, and you'd rather outsource the infrastructure. For context: Kolide discontinued the original self-hosted Fleet repository back in 2019 — FleetDM is the actively maintained fork.

Can I use osquery for compliance audits (CIS, PCI, HIPAA)?

Yes. The community maintains osquery packs aligned to CIS Benchmarks for Ubuntu, RHEL, and Amazon Linux. For PCI and HIPAA, map each query to the relevant control ID in a pack metadata field, then export the results as evidence. Pair with OpenSCAP (covered in our CIS benchmark article) for full control attestation.

Wrap Up

osquery isn't new, but 2026 is a great time to deploy it. BPF-backed events, a mature FleetDM ecosystem, signed release keys, and broad SIEM integrations have closed the gaps that held back earlier adopters. Start with a handful of high-value packs, centralize with FleetDM, ship to your SIEM of choice, and pair with a kernel-level runtime layer for tamper resistance. The result? Observability that an auditor can read, an analyst can query, and an attacker can't trivially bypass. Hard to beat that combo.

About the Author Editorial Team

Our team of expert writers and editors.