Module: Aikido::Zen
- Defined in:
- lib/aikido/zen.rb,
lib/aikido/zen/sql.rb,
lib/aikido/zen/scan.rb,
lib/aikido/zen/sink.rb,
lib/aikido/zen/actor.rb,
lib/aikido/zen/agent.rb,
lib/aikido/zen/cache.rb,
lib/aikido/zen/event.rb,
lib/aikido/zen/route.rb,
lib/aikido/zen/attack.rb,
lib/aikido/zen/config.rb,
lib/aikido/zen/errors.rb,
lib/aikido/zen/worker.rb,
lib/aikido/zen/context.rb,
lib/aikido/zen/helpers.rb,
lib/aikido/zen/ipc/rpc.rb,
lib/aikido/zen/package.rb,
lib/aikido/zen/payload.rb,
lib/aikido/zen/request.rb,
lib/aikido/zen/version.rb,
lib/aikido/zen/sinks/pg.rb,
lib/aikido/zen/api_cache.rb,
lib/aikido/zen/collector.rb,
lib/aikido/zen/internals.rb,
lib/aikido/zen/sinks_dsl.rb,
lib/aikido/zen/api_client.rb,
lib/aikido/zen/api_stream.rb,
lib/aikido/zen/sinks/curb.rb,
lib/aikido/zen/sinks/file.rb,
lib/aikido/zen/sinks/http.rb,
lib/aikido/zen/attack_wave.rb,
lib/aikido/zen/sinks/excon.rb,
lib/aikido/zen/sinks/httpx.rb,
lib/aikido/zen/system_info.rb,
lib/aikido/zen/rails_engine.rb,
lib/aikido/zen/rate_limiter.rb,
lib/aikido/zen/sinks/kernel.rb,
lib/aikido/zen/sinks/mysql2.rb,
lib/aikido/zen/sinks/patron.rb,
lib/aikido/zen/sinks/resolv.rb,
lib/aikido/zen/sinks/socket.rb,
lib/aikido/zen/sinks/em_http.rb,
lib/aikido/zen/sinks/sqlite3.rb,
lib/aikido/zen/sinks/trilogy.rb,
lib/aikido/zen/idor/protector.rb,
lib/aikido/zen/request/schema.rb,
lib/aikido/zen/sinks/net_http.rb,
lib/aikido/zen/sinks/typhoeus.rb,
lib/aikido/zen/synchronizable.rb,
lib/aikido/zen/collector/event.rb,
lib/aikido/zen/collector/hosts.rb,
lib/aikido/zen/collector/stats.rb,
lib/aikido/zen/collector/users.rb,
lib/aikido/zen/collector/routes.rb,
lib/aikido/zen/runtime_settings.rb,
lib/aikido/zen/sinks/async_http.rb,
lib/aikido/zen/sinks/httpclient.rb,
lib/aikido/zen/background_worker.rb,
lib/aikido/zen/capped_collections.rb,
lib/aikido/zen/attack_wave/helpers.rb,
lib/aikido/zen/outbound_connection.rb,
lib/aikido/zen/rate_limiter/bucket.rb,
lib/aikido/zen/rate_limiter/result.rb,
lib/aikido/zen/collector/sink_stats.rb,
lib/aikido/zen/context/rack_request.rb,
lib/aikido/zen/idor/analysis_result.rb,
lib/aikido/zen/rate_limiter/breaker.rb,
lib/aikido/zen/request/rails_router.rb,
lib/aikido/zen/context/rails_request.rb,
lib/aikido/zen/scanners/ssrf_scanner.rb,
lib/aikido/zen/request/schema/builder.rb,
lib/aikido/zen/runtime_settings/ip_set.rb,
lib/aikido/zen/sinks/action_controller.rb,
lib/aikido/zen/agent/heartbeats_manager.rb,
lib/aikido/zen/middleware/fork_detector.rb,
lib/aikido/zen/request/heuristic_router.rb,
lib/aikido/zen/runtime_settings/domains.rb,
lib/aikido/zen/runtime_settings/ip_list.rb,
lib/aikido/zen/middleware/context_setter.rb,
lib/aikido/zen/middleware/rack_throttler.rb,
lib/aikido/zen/scanners/ssrf/dns_lookups.rb,
lib/aikido/zen/middleware/ip_list_checker.rb,
lib/aikido/zen/middleware/request_tracker.rb,
lib/aikido/zen/runtime_settings/endpoints.rb,
lib/aikido/zen/middleware/attack_protector.rb,
lib/aikido/zen/request/schema/auth_schemas.rb,
lib/aikido/zen/request/schema/empty_schema.rb,
lib/aikido/zen/scanners/stored_ssrf_scanner.rb,
lib/aikido/zen/middleware/user_agent_checker.rb,
lib/aikido/zen/request/schema/auth_discovery.rb,
lib/aikido/zen/scanners/sql_injection_scanner.rb,
lib/aikido/zen/scanners/path_traversal/helpers.rb,
lib/aikido/zen/scanners/path_traversal_scanner.rb,
lib/aikido/zen/middleware/attack_wave_protector.rb,
lib/aikido/zen/runtime_settings/domain_settings.rb,
lib/aikido/zen/scanners/shell_injection_scanner.rb,
lib/aikido/zen/scanners/ssrf/private_ip_checker.rb,
lib/aikido/zen/middleware/allowed_address_checker.rb,
lib/aikido/zen/runtime_settings/protection_settings.rb,
lib/aikido/zen/runtime_settings/rate_limit_settings.rb,
lib/aikido/zen/ipc/ipc.rb
Defined Under Namespace
Modules: AttackWave, Attacks, Events, Helpers, IDOR, IPC, Internals, Middleware, RPC, Rails, SQL, Scanners, Sinks, WorkerProcess Classes: APICache, APIClient, APIError, APIStream, Actor, Agent, Attack, BackgroundWorker, Cache, CacheEntry, CappedMap, CappedSet, Collector, Config, Context, DecodeError, Event, InternalsError, NetworkError, OutboundConnection, OutboundConnectionBlockedError, Package, PathTraversalError, Payload, RailsEngine, RateLimitedError, RateLimiter, Request, Route, RuntimeSettings, SQLInjectionError, SSRFDetectedError, Scan, ShellInjectionError, Sink, SystemInfo, UnderAttackError, Worker
Constant Summary collapse
- VERSION =
"1.8.0"- LIBZEN_VERSION =
The version of libzen_internals that we build against.
"0.1.61"
Class Method Summary collapse
-
.Actor(data) ⇒ Object
Converts an object into an Actor for reporting back to the Aikido Dashboard.
- .agent ⇒ Object
- .api_cache ⇒ Object
-
.attack_wave_detector ⇒ Aikido::Zen::AttackWave::Detector
The attack wave detector.
-
.block_outbound?(connection) ⇒ Boolean
Whether this outbound connection should be blocked.
-
.blocking_mode? ⇒ Boolean
Whether the Aikido agent is currently blocking requests.
- .calculate_rate_limits(request) ⇒ Object
-
.collector ⇒ Object
Manages runtime metrics extracted from your app, which are uploaded to the Aikido servers if configured to do so.
-
.config ⇒ Aikido::Zen::Config
The agent configuration.
-
.current_context ⇒ Aikido::Zen::Context?
Gets the current context object that holds all information about the current request.
-
.current_context=(context) ⇒ Aikido::Zen::Context?
Sets the current context object that holds all information about the current request, or
nilto clear the current context. -
.detect_attack_wave(context, status_code = nil) ⇒ Array<Aikido::Zen::AttackWave::Sample>?
Classifies the current request and, if it is classified as suspicious, records a sample against the attack wave detector.
-
.enable_idor_protection ⇒ void
Enable IDOR protection for the current context.
- .fork! ⇒ Object
- .idor_protect(sql, dialect_name, params = nil) ⇒ void
- .idor_protector ⇒ Aikido::Zen::IDOR::Protector
-
.middleware_installed! ⇒ Object
Marks that the Zen middleware was installed properly.
-
.protect! ⇒ void
Enable protection.
- .rate_limiter ⇒ Object
-
.record_attack_wave(client_ip, sample) ⇒ Array<Aikido::Zen::AttackWave::Sample>?
Records a suspicious sample and, if the threshold for triggering an attack wave has been crossed, flags the client IP as having just triggered an attack wave.
-
.request_bypassed! ⇒ void
Marks the current request as bypassed.
-
.request_bypassed? ⇒ Boolean
Whether the current request is bypassed.
-
.runtime_settings ⇒ Aikido::Zen::RuntimeSettings
The firewall configuration sourced from your Aikido dashboard.
- .runtime_settings=(settings) ⇒ Object
- .secret ⇒ Object
-
.set_tenant_id(tenant_id) ⇒ void
Set the tenant ID for the current request.
- .start! ⇒ Object
- .start? ⇒ Boolean
-
.system_info ⇒ Object
Gets information about the current system configuration, which is sent to the server along with any events.
-
.track_attack_wave(_attack_wave) ⇒ void
Track statistics about an attack wave the app is handling.
-
.track_discovered_route(request) ⇒ void
Track statistics about a route that the app has discovered.
-
.track_ip_list(ip_list_keys) ⇒ void
Track blocked and monitored IP lists.
-
.track_outbound(connection) ⇒ void
Tracks a network connection made to an external service.
- .track_rate_limited_request(_request) ⇒ Object
-
.track_request(_request) ⇒ void
Track statistics about an HTTP request the app is handling.
-
.track_scan(scan) ⇒ void
Track statistics about the result of a Sink's scan, and report it as an Attack if one is detected.
-
.track_user(user) ⇒ void
(also: set_user)
Track the user making the current request.
-
.track_user_agent(user_agent_keys) ⇒ void
Track blocked and monitored user agents.
-
.without_idor_protection { ... } ⇒ Object
Execute a block with the IDOR protection disabled.
- .worker_process_server ⇒ Object
Class Method Details
.Actor(actor) ⇒ Object .Actor(data) ⇒ Object
Converts an object into an Actor for reporting back to the Aikido Dashboard.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/aikido/zen/actor.rb', line 19 def self.Actor(data) return if data.nil? return data.to_aikido_actor if data.respond_to?(:to_aikido_actor) attrs = {} if data.respond_to?(:to_hash) attrs = data.to_hash .slice("id", "name", :id, :name) .compact .transform_keys(&:to_sym) .transform_values(&:to_s) else return nil end return nil if attrs[:id].nil? || attrs[:id].to_s.strip.empty? Actor.new(**attrs) end |
.agent ⇒ Object
420 421 422 |
# File 'lib/aikido/zen.rb', line 420 def self.agent @agent end |
.api_cache ⇒ Object
91 92 93 |
# File 'lib/aikido/zen.rb', line 91 def self.api_cache @api_cache ||= APICache.new end |
.attack_wave_detector ⇒ Aikido::Zen::AttackWave::Detector
Returns the attack wave detector.
273 274 275 |
# File 'lib/aikido/zen.rb', line 273 def self.attack_wave_detector @attack_wave_detector ||= AttackWave::Detector.new end |
.block_outbound?(connection) ⇒ Boolean
Returns whether this outbound connection should be blocked.
229 230 231 232 233 |
# File 'lib/aikido/zen.rb', line 229 def self.block_outbound?(connection) return false if current_context&.protection_disabled? runtime_settings.block_outbound?(connection) end |
.blocking_mode? ⇒ Boolean
Returns whether the Aikido agent is currently blocking requests. Blocking mode is configured at startup and can be controlled through the Aikido dashboard at runtime.
120 121 122 123 124 125 |
# File 'lib/aikido/zen.rb', line 120 def self.blocking_mode? blocking_mode = runtime_settings.blocking_mode return blocking_mode unless blocking_mode.nil? config.blocking_mode end |
.calculate_rate_limits(request) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/aikido/zen.rb', line 99 def self.calculate_rate_limits(request) worker_process_client = @worker_process_client if worker_process_client begin worker_process_client.calculate_rate_limits(request) rescue rate_limiter.calculate_rate_limits(request) end else rate_limiter.calculate_rate_limits(request) end end |
.collector ⇒ Object
Manages runtime metrics extracted from your app, which are uploaded to the Aikido servers if configured to do so.
135 136 137 |
# File 'lib/aikido/zen.rb', line 135 def self.collector @collector ||= Collector.new end |
.config ⇒ Aikido::Zen::Config
Returns the agent configuration.
77 78 79 |
# File 'lib/aikido/zen.rb', line 77 def self.config @config ||= Config.new end |
.current_context ⇒ Aikido::Zen::Context?
Gets the current context object that holds all information about the current request.
143 144 145 |
# File 'lib/aikido/zen.rb', line 143 def self.current_context Fiber.current.aikido_current_context end |
.current_context=(context) ⇒ Aikido::Zen::Context?
Sets the current context object that holds all information about the
current request, or nil to clear the current context.
152 153 154 |
# File 'lib/aikido/zen.rb', line 152 def self.current_context=(context) Fiber.current.aikido_current_context = context end |
.detect_attack_wave(context, status_code = nil) ⇒ Array<Aikido::Zen::AttackWave::Sample>?
Classifies the current request and, if it is classified as suspicious, records a sample against the attack wave detector.
283 284 285 286 287 288 289 290 |
# File 'lib/aikido/zen.rb', line 283 def self.detect_attack_wave(context, status_code = nil) client_ip = context.request.client_ip return nil unless client_ip return nil unless AttackWave::Helpers.web_scanner?(context, status_code) record_attack_wave(client_ip, AttackWave::Helpers.sample_for(context)) end |
.enable_idor_protection ⇒ void
This method returns an undefined value.
Enable IDOR protection for the current context.
340 341 342 343 344 345 |
# File 'lib/aikido/zen.rb', line 340 def self.enable_idor_protection context = current_context return unless context context.idor_protection_enabled = true end |
.fork! ⇒ Object
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/aikido/zen.rb', line 448 def fork! server = @worker_process_server return unless server @worker_process_server = nil server.close client = @worker_process_client @worker_process_client = nil client&.close client = WorkerProcess::Agent::Client.new(server.host, server.port) client.start @worker_process_client = client rescue => err config.logger.error("Forked worker process #{Process.pid}: failed to start worker process client: #{err.message}") end |
.idor_protect(sql, dialect_name, params = nil) ⇒ void
This method returns an undefined value.
330 331 332 333 334 335 |
# File 'lib/aikido/zen.rb', line 330 def self.idor_protect(sql, dialect_name, params = nil) context = current_context return unless context idor_protector.protect(sql, dialect_name, params, context) end |
.idor_protector ⇒ Aikido::Zen::IDOR::Protector
321 322 323 |
# File 'lib/aikido/zen.rb', line 321 def self.idor_protector @idor_protector ||= IDOR::Protector.new end |
.middleware_installed! ⇒ Object
Marks that the Zen middleware was installed properly
383 384 385 |
# File 'lib/aikido/zen.rb', line 383 def self.middleware_installed! collector.middleware_installed! end |
.protect! ⇒ void
This method returns an undefined value.
Enable protection. Until this method is called no sinks are loaded and the Aikido Agent does not start.
This method should be called only once, in the application after the initialization process is complete.
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/aikido/zen.rb', line 45 def self.protect! if config.disabled? config.logger.warn("Zen has been disabled and will not run") return end unless load_sources! && load_sinks! config.logger.warn("Zen could not find any supported libraries or frameworks. Visit https://github.com/AikidoSec/firewall-ruby for more information.") return end middleware_installed! end |
.rate_limiter ⇒ Object
95 96 97 |
# File 'lib/aikido/zen.rb', line 95 def self.rate_limiter @rate_limiter ||= RateLimiter.new end |
.record_attack_wave(client_ip, sample) ⇒ Array<Aikido::Zen::AttackWave::Sample>?
Records a suspicious sample and, if the threshold for triggering an attack wave has been crossed, flags the client IP as having just triggered an attack wave.
In multiprocess deployments, on RPC failure, the worker process records against its local detector.
If RPC failures are intermittent, it is possible that an attack wave may be missed or duplicated, because state is split between the global and local detectors.
306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/aikido/zen.rb', line 306 def self.record_attack_wave(client_ip, sample) worker_process_client = @worker_process_client if worker_process_client begin worker_process_client.record_attack_wave(client_ip, sample) rescue attack_wave_detector.record(client_ip, sample) end else attack_wave_detector.record(client_ip, sample) end end |
.request_bypassed! ⇒ void
This method returns an undefined value.
Marks the current request as bypassed.
169 170 171 |
# File 'lib/aikido/zen.rb', line 169 def self.request_bypassed! current_context&.request_bypassed = true end |
.request_bypassed? ⇒ Boolean
Returns whether the current request is bypassed.
157 158 159 160 161 162 163 164 |
# File 'lib/aikido/zen.rb', line 157 def self.request_bypassed? context = current_context return unless context return context.request_bypassed unless context.request_bypassed.nil? context.request_bypassed = runtime_settings.bypassed_ip?(context.request.client_ip) end |
.runtime_settings ⇒ Aikido::Zen::RuntimeSettings
Returns the firewall configuration sourced from your Aikido dashboard. This is periodically polled for updates.
83 84 85 |
# File 'lib/aikido/zen.rb', line 83 def self.runtime_settings @runtime_settings ||= RuntimeSettings.new end |
.runtime_settings=(settings) ⇒ Object
87 88 89 |
# File 'lib/aikido/zen.rb', line 87 def self.runtime_settings=(settings) @runtime_settings = settings end |
.secret ⇒ Object
113 114 115 |
# File 'lib/aikido/zen.rb', line 113 def self.secret @secret ||= SecureRandom.bytes(32) end |
.set_tenant_id(tenant_id) ⇒ void
This method returns an undefined value.
Set the tenant ID for the current request.
351 352 353 354 355 356 |
# File 'lib/aikido/zen.rb', line 351 def self.set_tenant_id(tenant_id) context = current_context return unless context context.request.tenant_id = tenant_id end |
.start! ⇒ Object
431 432 433 434 435 436 437 438 439 440 |
# File 'lib/aikido/zen.rb', line 431 def start! return unless start? return unless @has_started.make_true @worker_process_server = WorkerProcess::Agent::Server.new @worker_process_server.start @agent = Agent.start end |
.start? ⇒ Boolean
442 443 444 445 446 |
# File 'lib/aikido/zen.rb', line 442 def start? !config.api_token.nil? || config.blocking_mode? || config.debugging? end |
.system_info ⇒ Object
Gets information about the current system configuration, which is sent to the server along with any events.
129 130 131 |
# File 'lib/aikido/zen.rb', line 129 def self.system_info @system_info ||= SystemInfo.new end |
.track_attack_wave(_attack_wave) ⇒ void
This method returns an undefined value.
Track statistics about an attack wave the app is handling.
207 208 209 |
# File 'lib/aikido/zen.rb', line 207 def self.track_attack_wave(_attack_wave) collector.track_attack_wave(being_blocked: false) end |
.track_discovered_route(request) ⇒ void
This method returns an undefined value.
Track statistics about a route that the app has discovered.
215 216 217 |
# File 'lib/aikido/zen.rb', line 215 def self.track_discovered_route(request) collector.track_route(request) end |
.track_ip_list(ip_list_keys) ⇒ void
This method returns an undefined value.
Track blocked and monitored IP lists.
199 200 201 |
# File 'lib/aikido/zen.rb', line 199 def self.track_ip_list(ip_list_keys) collector.track_ip_list(ip_list_keys) end |
.track_outbound(connection) ⇒ void
This method returns an undefined value.
Tracks a network connection made to an external service.
223 224 225 |
# File 'lib/aikido/zen.rb', line 223 def self.track_outbound(connection) collector.track_outbound(connection) end |
.track_rate_limited_request(_request) ⇒ Object
181 182 183 |
# File 'lib/aikido/zen.rb', line 181 def self.track_rate_limited_request(_request) collector.track_rate_limited_request end |
.track_request(_request) ⇒ void
This method returns an undefined value.
Track statistics about an HTTP request the app is handling.
177 178 179 |
# File 'lib/aikido/zen.rb', line 177 def self.track_request(_request) collector.track_request end |
.track_scan(scan) ⇒ void
This method returns an undefined value.
Track statistics about the result of a Sink's scan, and report it as an Attack if one is detected.
242 243 244 245 |
# File 'lib/aikido/zen.rb', line 242 def self.track_scan(scan) collector.track_scan(scan) agent.handle_attack(scan.attack) if scan.attack? end |
.track_user(user) ⇒ void Also known as: set_user
This method returns an undefined value.
Track the user making the current request.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/aikido/zen.rb', line 251 def self.track_user(user) return if config.disabled? if (actor = Aikido::Zen::Actor(user)) collector.track_user(actor) current_context.request.actor = actor if current_context else config.logger.warn(format(" Incompatible object sent to track_user: %<obj>p\n\n The object must either implement #to_aikido_actor, or be a Hash with\n an :id (or \"id\") and, optionally, a :name (or \"name\") key.\n LOG\n end\nend\n", obj: user)) |
.track_user_agent(user_agent_keys) ⇒ void
This method returns an undefined value.
Track blocked and monitored user agents.
190 191 192 |
# File 'lib/aikido/zen.rb', line 190 def self.track_user_agent(user_agent_keys) collector.track_user_agent(user_agent_keys) end |
.without_idor_protection { ... } ⇒ Object
Execute a block with the IDOR protection disabled.
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/aikido/zen.rb', line 363 def self.without_idor_protection raise ArgumentError, "block required" unless block_given? context = current_context if context begin original_idor_protection_enabled = context.idor_protection_enabled context.idor_protection_enabled = false yield ensure context.idor_protection_enabled = original_idor_protection_enabled end else yield end end |
.worker_process_server ⇒ Object
424 425 426 |
# File 'lib/aikido/zen.rb', line 424 def self.worker_process_server @worker_process_server end |