Skip to content

Commit

Permalink
Merge pull request #1148 from SeasideSt/protectionfilters
Browse files Browse the repository at this point in the history
Protectionfilters
  • Loading branch information
marschall authored Aug 25, 2019
2 parents a5121ab + 532d1df commit c674809
Show file tree
Hide file tree
Showing 36 changed files with 174 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A protection filter protects the wrapped request handler, mostly useful to protect against session hijacking. See subclasses for different strategies.
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
{
"commentStamp" : "lr 8/11/2009 10:56",
"commentStamp" : "JohanBrichau 8/4/2019 07:41",
"super" : "WARequestFilter",
"category" : "Seaside-Core-Filter",
"classinstvars" : [ ],
"pools" : [ ],
"classvars" : [ ],
"instvars" : [
"remoteAddress"
],
"name" : "WAProtectionFilter",
"instvars" : [ ],
"name" : "WAAbstractProtectionFilter",
"type" : "normal"
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ I track request handlers using a query field. This results in URLs looking like

This is a very simple and robust approach. It is also very convenient for development. Just remove the _s and you have a new session.

However there are some drawbacks. The smallest is aesthetical, the URL in the address bar of the browser is less "clean". Second because the session id is part of the request URL it shows up in all kinds of places. For example server logs. Not only the log of the server running the application but also very web site visited from there because it shows up in the Referer HTTP header. The danger of this is that when somebody knows the session id of somebody else he can take over his session. While most of these problems can be mitigated by adding a WAProtectionFilter to every session this creates new problems for users with changing IPs (eg. mobile devices).
However there are some drawbacks. The smallest is aesthetical, the URL in the address bar of the browser is less "clean". Second because the session id is part of the request URL it shows up in all kinds of places. For example server logs. Not only the log of the server running the application but also very web site visited from there because it shows up in the Referer HTTP header. The danger of this is that when somebody knows the session id of somebody else he can take over his session. Most of these problems can be mitigated by adding a WARemoteAddressProtectionFilter or WASessionCookieProtectionFilter to every session.

Mind that WARemoteAddressProtectionFilter creates new problems for users with changing IPs (eg. mobile devices) and WASessionCookieProtectionFilter requires cookies.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"commentStamp" : "pmm 7/17/2011 17:12",
"commentStamp" : "JohanBrichau 8/25/2019 16:20",
"super" : "WAHandlerTrackingStrategy",
"category" : "Seaside-Core-RequestHandling",
"classinstvars" : [ ],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The remote address protection filter ensures that the wrapped request handler only accepts requests from the same IP. Do add this filter to a WASession for example to avoid session hijacking, do not add it to static request handlers such as WAApplication or WADispatcher as this might restrict access to the handler if your IP changes.

Note that checking for IP addresses is not bullet proof and should never be used as the sole security measure for a web application as IP addresses can be easily spoofed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"commentStamp" : "JohanBrichau 8/4/2019 07:26",
"super" : "WAAbstractProtectionFilter",
"category" : "Seaside-Core-Filter",
"classinstvars" : [ ],
"pools" : [ ],
"classvars" : [ ],
"instvars" : [
"remoteAddress"
],
"name" : "WARemoteAddressProtectionFilter",
"type" : "normal"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The session cookie protection filter ensures that the wrapped request handler only accepts requests from the same browser session. This filter is specifically useful to protect session hijacking when using the (default) query field session tracking strategy.

Because WAQueryFieldHandlerTrackingStrategy puts the Seaside session key in the url, a session can be easily hijacked by copying the url. This request filter prevents this by requiring a browser session cookie associated to the Seaside session. As a result, a copied Seaside url can only be used in the same browser session.

The use of this filter, in combination with WAQueryFieldHandlerTrackingStrategy, keeps the ability for a user to open multiple sessions of the same Seaside application in a single browser, while removing easy session hijacking. A malicious user that wants to hijack the session now needs both the url and the cookie.

The appropriate use of this filter is to add it to the session in the `initializeFilters` method of your session class. Only in this way, the session is protected from the first rendered application page onwards. See WASessionCookieProtectedSession class as an example.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
accessing
browserSessionID
^ browserSessionID
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
constants
browserSessionIDCookieKey

^ 'seaside_browser_session'
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
constants
browserSessionIDSize

^ 20
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
private
createBrowserSessionIDCookieFor: aRequestContext
"Override this to customize cookie properties that fit your application needs"
^ aRequestContext newCookie
sameSite: 'Strict';
key: self browserSessionIDCookieKey;
value: self browserSessionID
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
processing
handleFiltered: aRequestContext
"If the browserSessionID instvar is nil, we are a newly created session and should set our browser session id.
Otherwise, verify the browser session id for the current request."
self browserSessionID
ifNil: [ self setBrowserSessionIDFor: aRequestContext ]
ifNotNil: [
(self verifyBrowserSessionIDFor: aRequestContext)
ifFalse: [
"If we carry a redirected url field, we have tried setting a cookie, which failed. Stop here to avoid infinite redirect.
In the other case, respond with the forbidden status code"
(self requestContext request queryFields includesKey: 'cookiecheck')
ifTrue:[ self respondCookiesRequired: aRequestContext ]
ifFalse: [ aRequestContext responseGenerator forbidden; respond ] ] ].
super handleFiltered: aRequestContext
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
processing
renderCookiesRequiredStatementOn: html
html text: 'Sorry, but this application requires cookies to be enabled'
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
processing
respondCookiesRequired: aRequestContext
| dialog |
dialog := WAHtmlCanvas builder
fullDocument: true;
rootBlock: [ :root |
root
beHtml5;
title: 'Cookies Required' ];
render: [ :html | self renderCookiesRequiredStatementOn: html ].
aRequestContext
respond: [ :response |
response
forbidden;
contentType: WAMimeType textHtml;
nextPutAll: dialog ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
private
setBrowserSessionIDFor: aRequestContext
"If there already is a browser session cookie, we reuse this one.
This means we can have multiple sessions open in a single browser, because they share the browser session id."
(aRequestContext request cookieAt: self browserSessionIDCookieKey)
ifNotNil: [ :cookie | browserSessionID := cookie value ]
ifNil: [
browserSessionID := (WAKeyGenerator current keyOfLength: self browserSessionIDSize).
aRequestContext response
addCookie: (self createBrowserSessionIDCookieFor: aRequestContext);
redirectTo: (aRequestContext request url copy
addField: self application trackingStrategy keyField value: self session key;
addField: 'cookiecheck' value: nil;
yourself) ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
private
verifyBrowserSessionIDFor: requestContext
| cookie |
cookie := requestContext request cookieAt: self browserSessionIDCookieKey.
^ cookie notNil and: [ cookie value = self browserSessionID ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"commentStamp" : "JohanBrichau 8/24/2019 13:30",
"super" : "WAAbstractProtectionFilter",
"category" : "Seaside-Core-Filter",
"classinstvars" : [ ],
"pools" : [ ],
"classvars" : [ ],
"instvars" : [
"browserSessionID"
],
"name" : "WASessionCookieProtectionFilter",
"type" : "normal"
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
initialization
initializeFilters
super initializeFilters.
self addFilter: WASessionCookieProtectionFilter new.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"commentStamp" : "",
"super" : "WASession",
"category" : "Seaside-Examples",
"classinstvars" : [ ],
"pools" : [ ],
"classvars" : [ ],
"instvars" : [ ],
"name" : "WASessionCookieProtectedSession",
"type" : "normal"
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
accessing
handlerName
^ 'examples/sessionprotected-counter'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
initialization
initialize
| app |
app := WAAdmin register: self asApplicationAt: self handlerName.
app sessionClass: WASessionCookieProtectedSession
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
initialization
initialize
super initialize.
counter := WACounter new
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
rendering
renderContentOn: html
counter renderOn: html.
html paragraph: 'I am the Counter example using WASessionCookieProtectionFilter so you cannot copy/paste the url in another browser to hijack my session. See the class comment of WASessionCookieProtectionFilter for more information.'
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"commentStamp" : "",
"super" : "WAExampleComponent",
"category" : "Seaside-Examples-Misc",
"classinstvars" : [ ],
"pools" : [ ],
"classvars" : [ ],
"instvars" : [
"counter"
],
"name" : "WASessionProtectedCounter",
"type" : "normal"
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
actions
protectorByBrowserSessionCookie
| filter |
"We could use #filterWith:during: but that requires Flow."
filter := WASessionCookieProtectionFilter new.
self session addFilter: filter.
child inform: 'Open this page in a different browser by copying the complete URL. Seaside should reply with a forbidden message. Closing this dialog should remove the filter.' onAnswer: [ self session removeFilter: filter ]
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
actions
protector
protectorByRemoteAddress
| filter |
"We could use #filterWith:during: but that requires Flow."
filter := WAProtectionFilter new.
filter := WARemoteAddressProtectionFilter new.
self session addFilter: filter.
child inform: 'Open this page on a different computer by copying the complete URL to a machine with a different IP address. Seaside should reply with a forbidden message. Closing this dialog should remove the filter.' onAnswer: [ self session removeFilter: filter ]
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ renderActionsOn: html
html text: ' (admin/seaside)'.
html break.
html anchor
callback: [ self protector ];
with: 'Session Protector' ].
callback: [ self protectorByRemoteAddress ];
with: 'Session Protector by remote address'.
html break.
html anchor
callback: [ self protectorByBrowserSessionCookie ];
with: 'Session Protector by session cookie' ].
html horizontalRule.
html paragraph: self children

This file was deleted.

0 comments on commit c674809

Please sign in to comment.