Ticket #287 (new enhancement)

Opened 11 months ago

Last modified 8 days ago

Redirect Command and Function Support for http/https Redirection

Reported by: bklaas@… Owned by: peterfarrell
Priority: minor Milestone: Mach-II 1.9.0 Milestone 2
Component: framework - core Version: 1.9.0 - Framework
Keywords: redirect HTTPS secure SSL security Cc: kurtwiersma, mattwoodward, brianfitzgerald
Blocking: Blocked By:

Description

It would be most useful if the redirect command or function (as of Mach-II 1.8) would support an optional argument allowing redirecting between http and https.

Switching between http and https calls is common, especially in login and shopping cart scenarios. A user will need to login securely (to prevent clear-text transmission of a password), but then switch back to non-SSL activity for less sensitive transactions. Similarly, shopping cart checkout needs to occur over https, but navigating to other parts of the site does not.

This can currently be accomplished via filter or plug-in which looks for non-https events and performs a <cflocation> to a SSL-enabled page. However, if event arguments need to be persisted across the http to https redirect, event arguments are lost with the <cflocation>. Workarounds to this are possible (storing within the session scope, etc.), but are cumbersome and would be the responsibility of each developer who uses the framework.

Given that the framework can currently perform redirects which persist even complex event arguments, it would be very useful to be able to utilize this existing functionality to enable http to https redirects (and vice versa).

Attachments

Change History

Changed 11 months ago by peterfarrell

  • cc kurtwiersma, mattwoodward, brianfitzgerald added
  • keywords redirect HTTPS secure SSL security added
  • version set to 1.9.0 - Framework
  • milestone set to Mach-II 1.9.0 alpha

Brian, thanks for your thoughts on this subject. It's something that Kurt and I have discussed in the past. Another route to think about is to "mark" an event-handler as "https" only and have the framework auto-negotiate the http/https switching for you (this would apply for first run events only -- meaning if you announced a new event in the middle of a request and it is was marked "secured" -- it wouldn't stop and change the protocol midstream -- this would only apply on incoming requests).

I'm going to triage this ticket and put it into the milestone for 1.9 at the moment.

Changed 11 months ago by bklaas@…

Hi Peter -

An attribute which marks an event as secure would be great. You're going to want the entire event to be secure, not just a portion of it, so this makes a lot of sense.

I appreciate you considering it for 1.9. I'm wrestling with this issue right now (hence the ticket), but haven't come up with a simple solution that doesn't involve plug-ins + a filter+ the session scope, or hacking the framework, which would be plain foolish.

Changed 11 months ago by peterfarrell

The simplest solution is a plugin that checks if the event is supposed to be secured, saves the requested event object in the session and redirects to the secured url. You then use the original event object that you saved in the session scope and "replay" that (clearing the event queue and adding in the original event args). If the plugin comes across an event that is not in the "secured" events, it switches back to standard HTTP requests using the same process. I use the simple pattern matching utility cfc that ships with 1.8 to do pattern based matching on event names (so I don't have to explicitly list all of them out -- stuff like cart.* or login.* as patterns).

Changed 11 months ago by .jonah <jonah.mach-ii@…>

Cross-protocol and cross-domain redirects are something that I've been thinking about too in my ecommerce app.

The solution I came up with is to set two properties:

<property name="urlBase" value="hxxp://www.store2.net" />
<property name="urlBaseSecure" value="hxxps://secure.store2.net" />

Then, when I want to ensure a link generated with BuildURL() goes to the appropriate domain with the correct protocol, I pass the respective property.

How far would it get things if we could do:

<redirect event="user.login" statusType="prg" url="${urlBaseSecure}" persist="true" persistArgs="errorCollection"/>

Changed 11 months ago by bklaas@…

Hi Jonah -

Before I created this ticket, I had thought that the framework might already support such a BuildSecureURL() function or something similar. The support for https in redirects is a separate, but very related issue.

I'd personally prefer adding a "secure=true" attribute to the redirect or BuildURL commands (because it would be very useful to have BuildURL be able to generate https requests as well). I think that comes from most/all of my apps not needing to do a secure redirect to another server. If you actually need to make a secure redirect to another server (maybe for single-sign on authorization), then being able to plug in a urlBaseSecure value would make sense.

Changed 11 months ago by .jonah <jonah.mach-ii@…>

Brian,

I agree the <redirect> question is different from buildURL().

For the redirect stuff, I'm just thinking that simply having secure="true" build urls using https://* instead of https://* is somewhat less flexible than being able to specify full host names. for the redirect. (Which we can do with the URL parameter.) So, my point was if we could use ${} variable replacement in the XML file, it might just do the job.

Changed 11 months ago by peterfarrell

There are two ways of accomplishing this explicit (which is what Brian originally suggested) or implicit (which I suggest by marking events as secured and have the framework say during a redirect "oh, you want THAT event - that's secured and we're not using HTTPS - let me take care of that"). Jonah's method works in Mach-II 1.8 right now and Brian you could use that like this:

<property name="securedUrlBase" value="https://www.example.com/index.cfm"/>

<redirect event="login" url="${securedUrlBase}" />

As for BuildUrl(), it's a similar story as you can use do this right now (as of Mach-II 1.5):

#buildUrl("login", "", getProperty("securedUrlBase"))#

Also, the <redirect> command behind the scene uses BuildUrl anyways and then uses location tag after that with the url.

The real question is how to make this simpler and more seamless. In a manner of speaking, I personally would like to say that my "login" event is always secured (something like <event-handler event="login" secure="true">) and not have to remember if I do a redirect to it or a buildUrl to my login event. The framework would take care of negotiating the correct protocol for me (HTTP/HTTPS). Thoughts?

Also, don't forget we now have a <view:a> tag in the view tag library that makes things even simpler.

Changed 11 months ago by .jonah <jonah.mach-ii@…>

Excited to see you can do url="${securedUrlBase}" in 1.8!

I do like the idea of specifying a secure parameter for events, but wouldn't want to rely on it exclusively for a couple reasons: 1. There's an extra redirect each time someone insecurely hits an event specified as secure and 2. If I'm POSTing to that event, my info is transmitted insecurely.

I guess what I'm saying is the idea of a secure=true flag is great and these other approaches are great too and each has their place and it's good to have them all. ;)

Changed 11 months ago by peterfarrell

@Jonah,

A few implementation points I've already thought of (but didn't mention right away):

  1. An extra redirect is well worth it for security. Remember there is a tendency to pre-optimize and unless you're building a low-bandwith site -- this isn't something I would worry about right away. This is really about enforcement of the fact that something needs to be secured which is an important application requirement.
  2. Regarding form posts, they should POST to the secure URL. I always use buildUrl in the action attribute of the form tag. What I was planning to do was make buildUrl smarter by having it create a secured version of the URL if the event-handler is marked as secure (which means it does a lookup to check if secure). That way you are assured that the POST will be to a secure URL. So this is really a moot point for me and in a non-Mach-II application you would have to ensure that the form POST is secure (as well as the form page).

Changed 11 months ago by bklaas@…

@Peter -

One concern about making BuildURL default to https when an entire event is marked secure: there are, of course, valid reasons to BuildURLs to non-secure requests even in a secure event. Again with the shopping cart checkout example: checkout events are secure, but a link back to "Continue Shopping" probably wouldn't be (because you don't want the overhead). In that case, being able to specify

#buildURL("login", "", getProperty("securedUrlBase"))#

resolves the need for building non-secure URLs within an event flagged as secure.

By the way, I totally didn't find the third attribute to buildURL() in the wiki when I first started working on this issue a couple weeks back. That, I'm sure, was an oversight on my part, because it's actually in the API docs. Perhaps I need to commit to writing up a wiki entry for all of this for 1.9.

In any case, having the ability to:

1. Mark an entire event as secure 2. Redirect the event to an event marked secure or with a secure URL supplied in the url attribute of the redirect tag (or command) 3. Specify a secure baseUrl value in BuildURL()

provides a great deal of flexibility while letting the framework handle the complexity of persisting complex event arguments across (secure) redirects.

Changed 11 months ago by peterfarrell

@Brian, hmm - I think I must have bungled my original thought about marking an event at secure. Consider this example. The following is a pseudo list of event-handlers

cart = secure
cart.checkout_01 = secure
home = not secure
product.abc = not secure

As you can see, only the cart event handler is marked as secured. Let's say we are on the home page (using the home event handler) and we call BuildUrl() for URLs to both the cart and product.abc. BuildUrl() would produce a secured URL for cart and an unsecured URL for product.abc.

Continuing with the example, we are on the cart page (using the cart event handler). This page is HTTPS (secure) and we call BuildUrl for URLs to both the home page and cart.checkout_01. We would get a non-https URL for the home page (because it's not required to be secure) and a secure URL (with HTTP) for cart.checkout_01.

So you don't have to worry about having all BuildUrl() calls producing HTTPS URLs when used inside a secure event-handler. The secure marking just says that a HTTPS URL should be produced if you call buildUrl('cart') or if you make a redirect to it or if somebody types in the URL to the cart but doesn't use HTTPS (this is where an auto-negotiated redirect would happen as @jonah pointed and was worried would happen often). Does that make sense?

Changed 11 months ago by bklaas@…

Ah, so you'd set up some declaration of the event handlers which are to be "made secure" by the framework. Any time an event is called or a URL is built via BuildURL, this list of event names (or events matched via wildcards) is checked an event that is in the list is set with a  https:// prefix. Additionally, the framework checks each event call and if the event name is in the list of handlers which are to be made secure, ensures that the request is coming in over https and, if not, redirects the request appropriately.

Am I understanding this correctly?

Changed 11 months ago by peterfarrell

@Brian Yes, you are understanding it correctly! I mentioned the secure="true" attribute idea in second to last paragraph of my comment (although it was buried inline probably got lost in the shuffle):

 http://greatbiztoolsllc.trac.cvsdude.com/mach-ii/ticket/287#comment:7

I think there is one exception to ensuring it is HTTPS. Let's say an event is announce (via clicking on an URL) and this event is not secured. Further down the request, more events are announced and added to the queue. I think it would be rather silly to change to HTTPS in mid-request (after processing many events) because the "initial" event was not secure. So if you want to entire request to be secure, the form POST or click on hyperlink should be to an event handler that is marked as secure so the URL generated by BuildURL() or a redirect has HTTPS in it. Otherwise if it happens mid-stream in a request, it more likely that it merely code-reuse therefore you cannot go back change that a form POST was insecure, etc. Does that make sense?

Changed 11 months ago by bklaas@…

So here's my thought about that: what happens when a user copies + pastes a URL or is given a URL without the http/https prefix in an email message and their request begins in a non-secure way that should really be secure? If you don't redirect to https somewhere down the chain, the event isn't going to be getting the benefits of SSL. I realize that this is an edge condition, but the malformed/partially incomplete URL distributed in an email message happens _all_ the damn time on some of our training apps.

Now, in all likelihood, you're going to require a user to log in to access events that are also secure, so the user will most likely be redirected to the login, which is (hopefully) secure and will then, after successful login, redirect the user back to the _secure_ version of the originally requested event. So, again, this concern should be handled by a properly designed app.

Changed 11 months ago by peterfarrell

Well, I think having a concrete example will help us here. Let's say their is an email a champaign and it has a URL like this:

http://www.example.com/index.cfm?event=doABC&userId=123987

The event-handler for doABC would look like this:

<event-handler event="doABC" access="public" secure="true">
   ... commands here ...
</event-handler>

So the user clicks the link (which is just normal HTTP) and Mach-II receives the request. Mach-II goes "oh, this needs to be secure - lemme handle that for you" and redirect to this URL:

https://www.example.com/index.cfm?event=doABC&userId=123987

So the framework will negotiate the request to the correct protocol as needed based on the meta-data it has in event-handler. The vice-versa would be true if a HTTPS request comes in for an event that is not required to be secure. BuildUrl would generate the appropriate HTTP/HTTPS url based on if the event-handler is marked as secure (by default the secure="false" would be the default).

So I think that handles your edge case of an user having a non-secure URL that really needs to be HTTPS as the framework handles the enforcement. As for form POSTs that need to be sure, the form action needs to POST to a secure URL (or have the form page be HTTPS in the first place so the protocol doesn't need switching). This is no different than in a non-Mach-II application -- this needs the developer's attention to detail because anybody can take an HTTPS url and switch it to HTTP -- we can't stop people from messing with that stuff. It comes down to the framework enforcement -- something we can off load to the framework easily.

Changed 11 months ago by .jonah <jonah.mach-ii@…>

This is turning into quite a thread!

I'm excited by what @peterfarrell said in #comment:11 about the secure=true attribute affecting buildURL() as well.

I have two thoughts based on my experience:

1. I don't have SSL in my dev environment, so both securedUrlBase and urlBase are HTTP.

2. Like I mentioned in #comment:4, sometimes secure URLs have a separate third level domain  hxxps://secure. vs  hxxp://www.. so simply prepending HTTP or HTTPS wouldn't work.

I propose that both of these situations could be resolved by buildURL() using two properties in production:

<property name="urlBase" value="hxxp://www.store2.net" />
<property name="securedUrlBase" value="hxxps://secure.store2.net" />

And these two in development:

<property name="urlBase" value="hxxp://www.store2.net" />
<property name="securedUrlBase" value="hxxp://www.store2.net" />

Changed 11 months ago by peterfarrell

@Jonah,

Agreed on the part that secured URLS may have a different domain (sub-domain -- they do some of my applications -- being cheap and not wanting to pay for wildcard certificate) and that in development / staging / testing environments these URL bases may be different. Easily solved by an additional property as you suggested and the use of the EnvironmentProperty?:

<property name="Environment" type="MachII.properties.EnvironmentProperty">
	<parameters>
		<parameter name="defaultEnvironmentName" value="production"/>
		<parameter name="development">
			<struct>
				<key name="environmentGroup" value="development"/>
				<key name="servers" value="m2website"/>
				<key name="properties">
					<struct>
					   <key name="urlBase"value="http://www.example.net" />

					   <key name="urlBase"value="http://www.example.net" />
					</struct>
				</key>
			</struct>
		</parameter>
		<parameter name="production">
			<struct>
				<key name="environmentGroup" value="production"/>
				<key name="servers" value="www.mach-ii.com,mach-ii.com"/>
				<key name="properties">
					<struct>
					   <key name="urlBase"value="http://www.example.net" />

					   <key name="urlBase"value="https://secure.example.net" />
					</struct>
				</key>
			</struct>
		</parameter>
	</parameters>
</property>

That builds on top of are already great environment stuff for 1.8 and gives you the most flexibility.

Changed 11 months ago by bklaas@…

You beat me to the punch on suggesting the use of environment variables, Peter! =D

The lack of https on most local development machines is a great reason to use the environment configuration options in 1.8. Yet another item to go in the wiki entry I need to commit to writing.

Changed 11 months ago by peterfarrell

As a side note, the environment property and supported stuff incurred a rather large discussion on the team. It was things like this ultimately convinced people and now I wouldn't want to go back (the big thing was one the resolveValueByEnvironment method available in the BaseComponent -- makes life so easy for my custom plugins / filters / listeners).

Yes, yes, Brian, do the wiki entry!

Changed 3 months ago by kurtwiersma

  • owner changed from peterfarrell to kurtwiersma
  • status changed from new to assigned

For buildURL: It should be pretty easy to look up the event-handler meta data to determine if the event specified in buildURL requires SSL. Then we could add support for a property called urlSecuredBase which could be determined based on the environment.

If an event that is marked as secured is announced as part of another event then we will not redirect to SSL.

I don't think we need to implement auto negotiation between events that require SSL or not. You could do this in a plugin if you need it for your application.

Changed 3 months ago by bklaas@…

Hey Kurt -

I think that if people see the urlSecuredBase property and utilize it in an event definition, I think that their expectation will be that the framework will auto-negotiate between unsecured and secured events. Maybe that's my bias as the person who opened the ticket, but my belief is that if I see an event attribute that says "make sure this event is called via HTTPS" then my expectation would be that I wouldn't have to write anything extra to make sure the event is called via HTTPS. The comment thread for the ticket really points to the framework taking care of the auto negotiation.

brian

Changed 3 months ago by peterfarrell

  • milestone changed from Mach-II 1.9.0 alpha to Mach-II 1.9.0 Milestone 2

Re-targeted for the Integrity Milestone 2

Changed 8 days ago by peterfarrell

(In [2142]) Plumbed in "secure" attribute for <event-handler> nodes and "secureDefault" for <event-handlers> master nodes. This is just metadata right now and no functionality to auto-negotiates protocol exists yet. refs #287

Changed 8 days ago by peterfarrell

  • owner changed from kurtwiersma to peterfarrell
  • status changed from assigned to new

Kurt, I got a start on this ticket. I did not complete it, but feel free to take ownership of the ticket if you want.

Anyways, it's not functional yet, but the syntax is this:

<event-handlers secureDefault="secure">
  <event-handler event="home" secure="unsecure">
    <!-- commands -->
  </event-handler>
  <event-handler event="aboutUs">
    <!-- commands -->
  </event-handler>
</event-handlers>

The secureDefault attribute applies to all event-handlers where there is no secure attribute defined. In the example above, I set all event-handlers in that block will be "secure" unless there is a "secure" attribute on concrete event-handler.

I also set the framework so you can define multiple event-handlers blocks in a single XML file, all with different "secureDefault" attributes. Like so:

<event-handlers secureDefault="secure">
  <!-- secure event-handler shere -->
</event-handlers>
<event-handlers secureDefault="secure">
  <!-- unsecure event-handler shere -->
</event-handlers>

Add/Change #287 (Redirect Command and Function Support for http/https Redirection)

Author


E-mail address and user name can be saved in the Preferences.


Action
as new
 
Note: See TracTickets for help on using tickets.