Class: Otto::Security::Config
- Inherits:
-
Object
- Object
- Otto::Security::Config
- Includes:
- Core::Freezable
- Defined in:
- lib/otto/security/config.rb
Overview
Security configuration for Otto applications
This class manages all security-related settings including CSRF protection, input validation, trusted proxies, and security headers. Security features are disabled by default for backward compatibility.
Instance Attribute Summary collapse
-
#csp_nonce_enabled ⇒ Object
readonly
Returns the value of attribute csp_nonce_enabled.
-
#csrf_header_key ⇒ Object
readonly
Returns the value of attribute csrf_header_key.
-
#csrf_protection ⇒ Object
readonly
Returns the value of attribute csrf_protection.
-
#csrf_session_key ⇒ Object
Returns the value of attribute csrf_session_key.
-
#csrf_token_key ⇒ Object
Returns the value of attribute csrf_token_key.
-
#debug_csp ⇒ Object
readonly
Returns the value of attribute debug_csp.
-
#input_validation ⇒ Object
Returns the value of attribute input_validation.
-
#ip_privacy_config ⇒ Object
readonly
Returns the value of attribute ip_privacy_config.
-
#max_param_depth ⇒ Object
Returns the value of attribute max_param_depth.
-
#max_param_keys ⇒ Object
Returns the value of attribute max_param_keys.
-
#max_request_size ⇒ Object
Returns the value of attribute max_request_size.
-
#mcp_auth ⇒ Object
readonly
Returns the value of attribute mcp_auth.
-
#rate_limiting_config ⇒ Object
Returns the value of attribute rate_limiting_config.
-
#require_secure_cookies ⇒ Object
readonly
Returns the value of attribute require_secure_cookies.
-
#security_headers ⇒ Object
readonly
Returns the value of attribute security_headers.
-
#trusted_proxies ⇒ Object
readonly
Returns the value of attribute trusted_proxies.
Instance Method Summary collapse
-
#add_trusted_proxy(proxy) ⇒ void
Add a trusted proxy server for accurate client IP detection.
-
#csp_nonce_enabled? ⇒ Boolean
Check if CSP nonce support is enabled.
-
#csrf_enabled? ⇒ Boolean
Check if CSRF protection is currently enabled.
-
#debug_csp? ⇒ Boolean
Check if CSP debug logging is enabled.
-
#deep_freeze! ⇒ self
Override deep_freeze! to ensure rate_limiting_config has custom_rules initialized.
-
#disable_csp_nonce! ⇒ void
Disable CSP nonce support.
-
#disable_csrf_protection! ⇒ void
Disable CSRF protection.
-
#enable_csp!(policy = "default-src 'self'") ⇒ void
Enable Content Security Policy (CSP) header.
-
#enable_csp_with_nonce!(debug: false) ⇒ void
Enable Content Security Policy (CSP) with nonce support.
-
#enable_csrf_protection! ⇒ void
Enable CSRF (Cross-Site Request Forgery) protection.
-
#enable_frame_protection!(option = 'SAMEORIGIN') ⇒ void
Enable X-Frame-Options header to prevent clickjacking.
-
#enable_hsts!(max_age: 31_536_000, include_subdomains: true) ⇒ void
Enable HTTP Strict Transport Security (HSTS) header.
- #generate_csrf_token(session_id = nil) ⇒ Object
-
#generate_nonce_csp(nonce, development_mode: false) ⇒ String
Generate a CSP policy string with the provided nonce.
- #get_or_create_session_id(request) ⇒ Object
-
#initialize ⇒ Config
constructor
Initialize security configuration with safe defaults.
-
#set_custom_headers(headers) ⇒ void
Set custom security headers.
-
#trusted_proxy?(ip) ⇒ Boolean
Check if an IP address is from a trusted proxy.
-
#validate_request_size(content_length) ⇒ Boolean
Validate that a request size is within acceptable limits.
- #verify_csrf_token(token, session_id = nil) ⇒ Object
Constructor Details
#initialize ⇒ Config
Initialize security configuration with safe defaults
All security features are disabled by default to maintain backward compatibility with existing Otto applications.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/otto/security/config.rb', line 41 def initialize @csrf_protection = false @csrf_token_key = '_csrf_token' @csrf_header_key = 'HTTP_X_CSRF_TOKEN' @csrf_session_key = '_csrf_session_id' @max_request_size = 10 * 1024 * 1024 # 10MB @max_param_depth = 32 @max_param_keys = 64 @trusted_proxies = [] @require_secure_cookies = false @security_headers = default_security_headers @input_validation = true @csp_nonce_enabled = false @debug_csp = false @rate_limiting_config = { custom_rules: {} } @ip_privacy_config = Otto::Privacy::Config.new end |
Instance Attribute Details
#csp_nonce_enabled ⇒ Object (readonly)
Returns the value of attribute csp_nonce_enabled.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def csp_nonce_enabled @csp_nonce_enabled end |
#csrf_header_key ⇒ Object (readonly)
Returns the value of attribute csrf_header_key.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def csrf_header_key @csrf_header_key end |
#csrf_protection ⇒ Object (readonly)
Returns the value of attribute csrf_protection.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def csrf_protection @csrf_protection end |
#csrf_session_key ⇒ Object
Returns the value of attribute csrf_session_key.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def csrf_session_key @csrf_session_key end |
#csrf_token_key ⇒ Object
Returns the value of attribute csrf_token_key.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def csrf_token_key @csrf_token_key end |
#debug_csp ⇒ Object (readonly)
Returns the value of attribute debug_csp.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def debug_csp @debug_csp end |
#input_validation ⇒ Object
Returns the value of attribute input_validation.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def input_validation @input_validation end |
#ip_privacy_config ⇒ Object (readonly)
Returns the value of attribute ip_privacy_config.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def ip_privacy_config @ip_privacy_config end |
#max_param_depth ⇒ Object
Returns the value of attribute max_param_depth.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def max_param_depth @max_param_depth end |
#max_param_keys ⇒ Object
Returns the value of attribute max_param_keys.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def max_param_keys @max_param_keys end |
#max_request_size ⇒ Object
Returns the value of attribute max_request_size.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def max_request_size @max_request_size end |
#mcp_auth ⇒ Object (readonly)
Returns the value of attribute mcp_auth.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def mcp_auth @mcp_auth end |
#rate_limiting_config ⇒ Object
Returns the value of attribute rate_limiting_config.
29 30 31 |
# File 'lib/otto/security/config.rb', line 29 def rate_limiting_config @rate_limiting_config end |
#require_secure_cookies ⇒ Object (readonly)
Returns the value of attribute require_secure_cookies.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def @require_secure_cookies end |
#security_headers ⇒ Object (readonly)
Returns the value of attribute security_headers.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def security_headers @security_headers end |
#trusted_proxies ⇒ Object (readonly)
Returns the value of attribute trusted_proxies.
31 32 33 |
# File 'lib/otto/security/config.rb', line 31 def trusted_proxies @trusted_proxies end |
Instance Method Details
#add_trusted_proxy(proxy) ⇒ void
This method returns an undefined value.
Add a trusted proxy server for accurate client IP detection
Only requests from trusted proxies will have their X-Forwarded-For and similar headers honored for IP detection. This prevents IP spoofing from untrusted sources.
111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/otto/security/config.rb', line 111 def add_trusted_proxy(proxy) raise FrozenError, 'Cannot modify frozen configuration' if frozen? case proxy when String, Regexp @trusted_proxies << proxy when Array @trusted_proxies.concat(proxy) else raise ArgumentError, 'Proxy must be a String, Regexp, or Array' end end |
#csp_nonce_enabled? ⇒ Boolean
Check if CSP nonce support is enabled
250 251 252 |
# File 'lib/otto/security/config.rb', line 250 def csp_nonce_enabled? @csp_nonce_enabled end |
#csrf_enabled? ⇒ Boolean
Check if CSRF protection is currently enabled
88 89 90 |
# File 'lib/otto/security/config.rb', line 88 def csrf_enabled? @csrf_protection end |
#debug_csp? ⇒ Boolean
Check if CSP debug logging is enabled
257 258 259 |
# File 'lib/otto/security/config.rb', line 257 def debug_csp? @debug_csp end |
#deep_freeze! ⇒ self
Override deep_freeze! to ensure rate_limiting_config has custom_rules initialized
This pre-initializes any lazy values before freezing to prevent FrozenError when accessing configuration after it’s frozen.
305 306 307 308 309 |
# File 'lib/otto/security/config.rb', line 305 def deep_freeze! # Ensure custom_rules is initialized (should already be done in constructor) @rate_limiting_config[:custom_rules] ||= {} super end |
#disable_csp_nonce! ⇒ void
This method returns an undefined value.
Disable CSP nonce support
241 242 243 244 245 |
# File 'lib/otto/security/config.rb', line 241 def disable_csp_nonce! raise FrozenError, 'Cannot modify frozen configuration' if frozen? @csp_nonce_enabled = false end |
#disable_csrf_protection! ⇒ void
This method returns an undefined value.
Disable CSRF protection
79 80 81 82 83 |
# File 'lib/otto/security/config.rb', line 79 def disable_csrf_protection! raise FrozenError, 'Cannot modify frozen configuration' if frozen? @csrf_protection = false end |
#enable_csp!(policy = "default-src 'self'") ⇒ void
This method returns an undefined value.
Enable Content Security Policy (CSP) header
CSP helps prevent XSS attacks by controlling which resources can be loaded. The default policy only allows resources from the same origin.
212 213 214 215 216 |
# File 'lib/otto/security/config.rb', line 212 def enable_csp!(policy = "default-src 'self'") raise FrozenError, 'Cannot modify frozen configuration' if frozen? @security_headers['content-security-policy'] = policy end |
#enable_csp_with_nonce!(debug: false) ⇒ void
This method returns an undefined value.
Enable Content Security Policy (CSP) with nonce support
This enables dynamic CSP header generation with nonces for enhanced security. Unlike enable_csp!, this doesn’t set a static policy but enables the response helper to generate CSP headers with nonces on a per-request basis.
230 231 232 233 234 235 |
# File 'lib/otto/security/config.rb', line 230 def enable_csp_with_nonce!(debug: false) raise FrozenError, 'Cannot modify frozen configuration' if frozen? @csp_nonce_enabled = true @debug_csp = debug end |
#enable_csrf_protection! ⇒ void
This method returns an undefined value.
Enable CSRF (Cross-Site Request Forgery) protection
When enabled, Otto will:
-
Generate CSRF tokens for safe HTTP methods (GET, HEAD, OPTIONS, TRACE)
-
Validate CSRF tokens for unsafe methods (POST, PUT, DELETE, PATCH)
-
Automatically inject CSRF meta tags into HTML responses
-
Provide helper methods for forms and AJAX requests
69 70 71 72 73 |
# File 'lib/otto/security/config.rb', line 69 def enable_csrf_protection! raise FrozenError, 'Cannot modify frozen configuration' if frozen? @csrf_protection = true end |
#enable_frame_protection!(option = 'SAMEORIGIN') ⇒ void
This method returns an undefined value.
Enable X-Frame-Options header to prevent clickjacking
276 277 278 279 280 |
# File 'lib/otto/security/config.rb', line 276 def enable_frame_protection!(option = 'SAMEORIGIN') raise FrozenError, 'Cannot modify frozen configuration' if frozen? @security_headers['x-frame-options'] = option end |
#enable_hsts!(max_age: 31_536_000, include_subdomains: true) ⇒ void
This method returns an undefined value.
Enable HTTP Strict Transport Security (HSTS) header
HSTS forces browsers to use HTTPS for all future requests to this domain. WARNING: This can make your domain inaccessible if HTTPS is not properly configured. Only enable this when you’re certain HTTPS is working correctly.
193 194 195 196 197 198 199 |
# File 'lib/otto/security/config.rb', line 193 def enable_hsts!(max_age: 31_536_000, include_subdomains: true) raise FrozenError, 'Cannot modify frozen configuration' if frozen? hsts_value = "max-age=#{max_age}" hsts_value += '; includeSubDomains' if include_subdomains @security_headers['strict-transport-security'] = hsts_value end |
#generate_csrf_token(session_id = nil) ⇒ Object
159 160 161 162 163 164 165 166 167 |
# File 'lib/otto/security/config.rb', line 159 def generate_csrf_token(session_id = nil) base = session_id || 'no-session' token = SecureRandom.hex(32) hash_input = base + ':' + token signature = Digest::SHA256.hexdigest(hash_input) csrf_token = "#{token}:#{signature}" csrf_token end |
#generate_nonce_csp(nonce, development_mode: false) ⇒ String
Generate a CSP policy string with the provided nonce
266 267 268 269 |
# File 'lib/otto/security/config.rb', line 266 def generate_nonce_csp(nonce, development_mode: false) directives = development_mode ? development_csp_directives(nonce) : production_csp_directives(nonce) directives.join(' ') end |
#get_or_create_session_id(request) ⇒ Object
311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/otto/security/config.rb', line 311 def get_or_create_session_id(request) # Try existing sources first session_id = extract_existing_session_id(request) # Create and persist if none found if session_id.nil? || session_id.empty? session_id = SecureRandom.hex(16) store_session_id(request, session_id) end session_id end |
#set_custom_headers(headers) ⇒ void
This method returns an undefined value.
Set custom security headers
293 294 295 296 297 |
# File 'lib/otto/security/config.rb', line 293 def set_custom_headers(headers) raise FrozenError, 'Cannot modify frozen configuration' if frozen? @security_headers.merge!(headers) end |
#trusted_proxy?(ip) ⇒ Boolean
Check if an IP address is from a trusted proxy
128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/otto/security/config.rb', line 128 def trusted_proxy?(ip) return false if @trusted_proxies.empty? @trusted_proxies.any? do |proxy| case proxy when String ip == proxy || ip.start_with?(proxy) when Regexp proxy.match?(ip) else false end end end |
#validate_request_size(content_length) ⇒ Boolean
Validate that a request size is within acceptable limits
148 149 150 151 152 153 154 155 156 157 |
# File 'lib/otto/security/config.rb', line 148 def validate_request_size(content_length) return true if content_length.nil? size = content_length.to_i if size > @max_request_size raise Otto::Security::RequestTooLargeError, "Request size #{size} exceeds maximum #{@max_request_size}" end true end |
#verify_csrf_token(token, session_id = nil) ⇒ Object
169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/otto/security/config.rb', line 169 def verify_csrf_token(token, session_id = nil) return false if token.nil? || token.empty? token_part, signature = token.split(':') return false if token_part.nil? || signature.nil? base = session_id || 'no-session' hash_input = "#{base}:#{token_part}" expected_signature = Digest::SHA256.hexdigest(hash_input) comparison_result = secure_compare(signature, expected_signature) comparison_result end |