Here's the working iRule.
Amongst the things it's missing are precedence and event disables so it doesn't clash with other redirect iRules:
====================================================================
Name : Secure Credentials Handover Rule
Description :
This iRule is used to secure the handover of credentials between two apps when passed using HTTP redirects.
Special Notes :
Note this iRule applies to the following scenario, which can be secured by this iRule:
App1 and App2 sit on the same https://www.domain.com website behind the same BigIP on the same virtual server
User clicks on resource within App1, App1 sends back a 302 containing link to App2 along with the user's credentials in the URI
User responds to 302 submitting request to App2 with credentials in URI. App2 receives request and uses the URI auth details to authenticate the user.
Current Version : 1.1 - 18 Nov 2010
version control
===============
1.1 - Anonymised for DevCentral - tested on 9.4.8
Author (Version, Date, Name, Company)
=====================================
1.1, 18 Nov 2010, Carl Gottlieb
when RULE_INIT {
Specify the URL to redirect to when the the process fails
set ::error_redirect_url "https://www.domain.com/login"
Specify the unique string that the 302 Location Header contains that signifies this as an authentication redirect
set ::auth_identifier_string "authattempt"
Specify the temporary URI used to replace the original uri with
set ::uri_string_unique_auth_text "/secured_auth_request"
Specify the name of the authentication cookie
set ::auth_cookie_name "hasheduricookie"
Specify the session table entry timeout value in seconds
set ::cookie_session_timeout "5"
Specify the path of the cookie
set ::auth_cookie_path "/"
Specify the domain of the cookie
set ::auth_cookie_domain "www.domain.com"
Specify the domain of the website
set ::website_domain "https://www.domain.com"
}
when HTTP_RESPONSE {
Check if the HTTP response is a redirect to the authentication page
if {[HTTP::is_redirect] and ([HTTP::header Location] contains $::auth_identifier_string)}{
Use scan to check for https://$host/$cleartexturi, saving the matches
the leading / of the URI will be trimmed, so we need to append it in the Location rewrite
if {[scan [HTTP::header Location] {https://%[^/]/%s} host cleartexturi] == 2}{
Strip the uri off the Location header and replace with $::uri_string_unique_auth_text to identify this process
HTTP::header replace Location $::website_domain$::uri_string_unique_auth_text
Create a numeric md5 hashed version of the clear text uri
set hasheduri [b64encode [md5 $cleartexturi]]
Insert the new cookie containing the hashed uri
HTTP::cookie insert name $::auth_cookie_name value $hasheduri path $::auth_cookie_path domain $::auth_cookie_domain
Insert a session table entry for the hashed uri against the real clear text uri with a timeout of $::cookie_session_timeout seconds
session add uie $hasheduri $cleartexturi $::cookie_session_timeout
} else {
log local0. "[IP::client_addr]:[TCP::client_port]: Couldn't parse auth redirect: [HTTP::header Location]"
}
}
elseif { [info exists wipe_cookie_flag] } {
if { $wipe_cookie_flag == 1 } {
The HTTP Response is not a redirect but since the successful auth response variable is set this must be the response after successful auth
Set the auth session cookie to be expired in the response so the user's browser deletes the cookie
HTTP::header insert Set-Cookie "$::auth_cookie_name=null;path=$::auth_cookie_path;domain=$::auth_cookie_domain;Expires=Thurs, 01-Jan-1970 00:00:00 GMT"
log local0. "The hashed auth cookie has been set to expire due to successful authentication"
Now the flag has been observed and the cookie set to expire itself we don't need the flag anymore, thus unset the cookie flag.
unset wipe_cookie_flag
log local0. "Wipe_cookie_flag has been reset after this successful authentication."
}
}
}
when HTTP_REQUEST {
Only match traffic which is for this new app2 service that is a new auth attempt
if {[HTTP::uri] starts_with $::uri_string_unique_auth_text}{
Check that the request contains the auth cookie and it has a value
if {[HTTP::cookie exists $::auth_cookie_name] and [HTTP::cookie $::auth_cookie_name] ne ""}{
Check that session table isn't empty for this proposed cookie value
if {[session lookup uie [HTTP::cookie value $::auth_cookie_name]] ne ""}{
Set a variable to be the value of the incoming cookie
set session_table_cookie [HTTP::cookie value $::auth_cookie_name]
Replace the uri of the request with the original un-hashed uri from the session table with a slash
HTTP::uri "/[session lookup uie $session_table_cookie]"
Delete the uri from the session table now that it has been used
session delete uie $session_table_cookie
Delete the cookie containing the hashed uri from the HTTP request so that it is not submitted to the webserver
HTTP::cookie remove $::auth_cookie_name
set wipe_cookie_flag "1"
log local0. "Wipe_cookie_flag has been set in a request to 1 during this successful authentication."
} else {
Request was to $::uri_string_unique_auth_text and the cookie was present but the cookie didn't match a session table entry (most likely a cookie recreation attack)
HTTP::redirect $::error_redirect_url
log local0. "Request was to ::uri_string_unique_auth_text but the cookie didn't match a session table entry (most likely a cookie recreation attack)" }
} else {
Request was to ::uri_string_unique_auth_text but no cookie was present in the request
HTTP::redirect $::error_redirect_url
log local0. "User tried to access authentication site by going to uri of [HTTP::uri] , but authentication cookie wasn't present so redirected user to error page of $::error_redirect_url"
}
}
}