Class: Utopia::Session::Middleware

Inherits:
Protocol::HTTP::Middleware
  • Object
show all
Defined in:
lib/utopia/session/middleware.rb

Overview

A middleware which provides a secure client-side session storage using a private symmetric encrpytion key.

Defined Under Namespace

Classes: PayloadError

Constant Summary collapse

MAXIMUM_SIZE =
1024*32
SECRET_KEY =
"UTOPIA_SESSION_SECRET".freeze
SESSION_KEY =
"utopia.session".freeze
CIPHER_ALGORITHM =
"aes-256-cbc"
DEFAULT_EXPIRES_AFTER =

The session will expire if no requests were made within 24 hours:

3600*24
DEFAULT_UPDATE_TIMEOUT =

At least, the session will be updated every 1 hour:

3600

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, session_name: SESSION_KEY, secret: nil, expires_after: DEFAULT_EXPIRES_AFTER, update_timeout: DEFAULT_UPDATE_TIMEOUT, domain: nil, path: "/", max_age: nil, secure: false, http_only: true, same_site: :lax, partitioned: false, maximum_size: MAXIMUM_SIZE) ⇒ Middleware

Returns a new instance of Middleware.

Parameters:

  • session_name (String) (defaults to: SESSION_KEY)

    The name of the session cookie.

  • secret (String) (defaults to: nil)

    The secret text used to generate a symmetric encryption key for the cookie data.

  • expires_after (Numeric | Nil) (defaults to: DEFAULT_EXPIRES_AFTER)

    The maximum session inactivity in seconds.

  • update_timeout (Numeric | Nil) (defaults to: DEFAULT_UPDATE_TIMEOUT)

    The maximum interval between session cookie updates.

  • domain (String | Nil) (defaults to: nil)

    The domain for which the cookie is valid.

  • path (String | Nil) (defaults to: "/")

    The path for which the cookie is valid.

  • max_age (Integer | Nil) (defaults to: nil)

    The browser cookie lifetime in seconds.

  • secure (Boolean) (defaults to: false)

    Whether the cookie requires a secure connection.

  • http_only (Boolean) (defaults to: true)

    Whether client-side scripts may access the cookie.

  • same_site (Symbol | String | Boolean | Nil) (defaults to: :lax)

    Controls whether the cookie is sent with cross-site requests.

  • partitioned (Boolean) (defaults to: false)

    Whether the cookie uses partitioned storage.

  • maximum_size (Integer | Nil) (defaults to: MAXIMUM_SIZE)

    The maximum encoded session payload size.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/utopia/session/middleware.rb', line 52

def initialize(app, session_name: SESSION_KEY, secret: nil, expires_after: DEFAULT_EXPIRES_AFTER, update_timeout: DEFAULT_UPDATE_TIMEOUT, domain: nil, path: "/", max_age: nil, secure: false, http_only: true, same_site: :lax, partitioned: false, maximum_size: MAXIMUM_SIZE)
	super(app)
	
	@session_name = session_name
	@cookie_name = @session_name + ".encrypted"
	
	if secret.nil? or secret.empty?
		raise ArgumentError, "invalid session secret: #{secret.inspect}"
	end
	
	# This generates a 32-byte key suitable for aes.
	@key = Digest::SHA2.digest(secret)
	
	@expires_after = expires_after
	@update_timeout = update_timeout
	
	@cookie_defaults = {
		domain: domain,
		path: path,
		max_age: max_age,
		
		# The SameSite attribute controls when the cookie is sent to the server, from 3rd parties (None), from requests with external referrers (Lax) or from within the site itself (Strict).
		same_site: normalize_same_site(same_site),
		
		# The Secure attribute is meant to keep cookie communication limited to encrypted transmission, directing browsers to use cookies only via secure/encrypted connections. However, if a web server sets a cookie with a secure attribute from a non-secure connection, the cookie can still be intercepted when it is sent to the user by man-in-the-middle attacks. Therefore, for maximum security, cookies with the Secure attribute should only be set over a secure connection.
		secure: secure,
		
		# The HttpOnly attribute directs browsers not to expose cookies through channels other than HTTP (and HTTPS) requests. This means that the cookie cannot be accessed via client-side scripting languages (notably JavaScript), and therefore cannot be stolen easily via cross-site scripting (a pervasive attack technique).
		http_only: http_only,
		partitioned: partitioned,
	}
	
	@serialization = Serialization.new
	@maximum_size = maximum_size
end

Instance Attribute Details

Returns the value of attribute cookie_defaults.



94
95
96
# File 'lib/utopia/session/middleware.rb', line 94

def cookie_defaults
  @cookie_defaults
end

Returns the value of attribute cookie_name.



88
89
90
# File 'lib/utopia/session/middleware.rb', line 88

def cookie_name
  @cookie_name
end

#expires_afterObject (readonly)

Returns the value of attribute expires_after.



91
92
93
# File 'lib/utopia/session/middleware.rb', line 91

def expires_after
  @expires_after
end

#keyObject (readonly)

Returns the value of attribute key.



89
90
91
# File 'lib/utopia/session/middleware.rb', line 89

def key
  @key
end

#update_timeoutObject (readonly)

Returns the value of attribute update_timeout.



92
93
94
# File 'lib/utopia/session/middleware.rb', line 92

def update_timeout
  @update_timeout
end

Instance Method Details

#call(request) ⇒ Object

Attach a lazily loaded session to the request, then persist it.



113
114
115
116
117
118
119
120
121
# File 'lib/utopia/session/middleware.rb', line 113

def call(request)
	request.session = prepare_session(request)
	
	response = Response.wrap(@delegate.call(request))
	
	update_session(request.session, response.headers)
	
	return response
end

#freezeObject

Freeze this object and its internal state.



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/utopia/session/middleware.rb', line 98

def freeze
	return self if frozen?
	
	@cookie_name.freeze
	@key.freeze
	@expires_after.freeze
	@update_timeout.freeze
	@cookie_defaults.freeze
	
	super
end