Closed Bug 283006 Opened 20 years ago Closed 20 years ago

XForms seems vulnerable to http-redirect attacks

Categories

(Core Graveyard :: XForms, defect)

defect
Not set
blocker

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: sicking, Assigned: smaug)

Details

Attachments

(1 file, 4 obsolete files)

From a brief look it looks like xforms is vulnerable to http-redirect attacks.
I.e. that you can point the instancedata url to your server and then send a
redirect response redirecting to some other server.

It's no huge deal since no 'real' xforms release has been done yet. But it
should still be fixed pretty quickly if the attac is in fact possible.
Btw, I don't know if xforms fetches other 'external' data then the instancedata.
If so that code would need to be fixed too.
Severity: normal → blocker
Is it enough to add a SameOriginCheck to the channel we are actually reading
data from?
Don't know. Check with darin. One thing to keep in mind is that you don't want
to make it possible to even check the existance of a resource. Though maybe that
is already possible using iframes...

Another thing that defenetly is not possible to stop that way is submitting data
to a server inside a firewall. Though this is probably possible anyway.
Taking. Going to use nsIHttpEventSink for this.
Assignee: aaronr → smaug
Status: NEW → ASSIGNED
Attached patch v.1 (obsolete) — Splinter Review
Something like this. I'm not too happy copying the same methods in too places,
but fortunately they are quite small ;)
Attached patch v.1.0.1 (obsolete) — Splinter Review
Added some null checks.
Attachment #175025 - Attachment is obsolete: true
Good catch jonas!  nsXFormsSubmissionElement::OnRedirect should implement the
same security logic as the sending code.  In other words, if the submission is
simple GET w/ replace=all, then there is no reason for same origin checks (in
fact, you'd be breaking things in that case).  The other thing to keep in mind
with this bug is that some HTTP redirects are OK.  Sometimes they just redirect
from POST to GET.  In those cases, there shouldn't be a same origin concern IMO
when replace != instance.  But, that is maybe a minor detail.

The rule I think should be this:

* If the page is sending a XML document, then restrict.
* If the page is fetching a XML document, then restrict.
* Otherwise, allow.
Comment on attachment 175029 [details] [diff] [review]
v.1.0.1

This doesn't work :(
Attachment #175029 - Attachment is obsolete: true
Attached patch v.1.1 (obsolete) — Splinter Review
darin, any comments on this one?
This uses same security check in ::OnRedirect as ::SendData.
could the page trick us out by modifying the replace attribute on the submission
element between the time it submits the document and the redirect is received? 
i think the answer is yes :(
Attached patch v.1.2 (obsolete) — Splinter Review
(In reply to comment #10)
> could the page trick us out by modifying the replace attribute on the
submission
> element between the time it submits the document and the redirect is
received? 
> i think the answer is yes :(
Blaah, of course.
Attachment #175051 - Attachment is obsolete: true
Comment on attachment 175063 [details] [diff] [review]
v.1.2

>+
>+  if (mFormat & (ENCODING_XML | ENCODING_MULTIPART_RELATED) ||
>+      mIsReplaceInstance) {
>+    if (!nsXFormsUtils::CheckSameOrigin(doc->GetDocumentURI(), newURI))
>+      return NS_ERROR_ABORT;
>+  }
>+  return NS_OK;
>+}
nit: you could make it all one if, no need for nested if statements really.

>@@ -482,16 +535,20 @@ nsXFormsSubmissionElement::Submit()
> 
>   // 1. ensure that we are not currently processing a xforms-submit (see E37)
>   NS_ENSURE_STATE(!mSubmissionActive);
>   mSubmissionActive = PR_TRUE;
>   
>   if (mActivator)
>     mActivator->SetDisabled(PR_TRUE);
  if mActivator is null, will things break?


>   nsAutoString replace;
>   mElement->GetAttribute(NS_LITERAL_STRING("replace"), replace);
>-  if (format & (ENCODING_XML | ENCODING_MULTIPART_RELATED) ||
>-      replace.EqualsLiteral("instance"))
>+  if (mFormat & (ENCODING_XML | ENCODING_MULTIPART_RELATED) ||
>+      mIsReplaceInstance)
>   {
>     if (!nsXFormsUtils::CheckSameOrigin(doc->GetDocumentURI(), uri))
>       return NS_ERROR_ABORT;
>   }

nit: You could probably save the 2nd if and just have one if, unless its too
unreadable?

DOM usage and changes to existing code look good, not too sure about the necko
usage, darin should be able to nitpick that.
(In reply to comment #12)
> >   
> >   if (mActivator)
> >     mActivator->SetDisabled(PR_TRUE);
>   if mActivator is null, will things break?

No. mActivator is used only to mark <xf:submit> element disabled.

Comment on attachment 175063 [details] [diff] [review]
v.1.2

Doron, modifiying those |if|s makes them quite unreadable,
but I can change them (if needed) before this is checked in.

Darin, could you sr(+|-)?
Attachment #175063 - Flags: superreview?(darin)
Attachment #175063 - Flags: review?(doronr)
Comment on attachment 175063 [details] [diff] [review]
v.1.2

>Index: nsXFormsInstanceElement.cpp

>+NS_IMETHODIMP
>+nsXFormsInstanceElement::GetInterface(const nsIID & aIID, void **aResult)
>+{
>+  *aResult = nsnull;
>+  nsresult rv = QueryInterface(aIID, aResult);
>+  if (NS_FAILED(rv)) {

how about doing:

    if (NS_SUCCEEDED(rv))
      return rv;

that way you can reduce indenting of the remainder of the function :)


>+    NS_ENSURE_STATE(mElement);
>+    nsCOMPtr<nsIDOMDocument> domDoc;
>+    mElement->GetOwnerDocument(getter_AddRefs(domDoc));
>+    nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
>+    NS_ENSURE_STATE(doc);
>+
>+    nsCOMPtr<nsISupports> container = doc->GetContainer();
>+    nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(container);
>+    if (callbacks)
>+      return callbacks->GetInterface(aIID, aResult);

So, I'm curious.  Why is this needed here?  We didn't do 
this before (the channel would query callbacks from the
loadgroup if necessary), so why do it now?


>+nsXFormsInstanceElement::OnRedirect(nsIHttpChannel *aHttpChannel,
>+                                    nsIChannel *aNewChannel)
>+{
>+  NS_ENSURE_ARG_POINTER(aNewChannel);
>+  nsCOMPtr<nsIURI> newURI;
>+  nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
>+  NS_ENSURE_SUCCESS(rv, rv);
>+  
>+  NS_ENSURE_STATE(mElement);
>+  nsCOMPtr<nsIDOMDocument> domDoc;
>+  mElement->GetOwnerDocument(getter_AddRefs(domDoc));
>+  nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
>+  NS_ENSURE_STATE(doc);
>+  return nsXFormsUtils::CheckSameOrigin(doc->GetDocumentURI(), newURI) ?
>+    NS_OK : NS_ERROR_ABORT;
>+}

incidentally, you could also just check that aHttpChannel
and aNewChannel have the same origin.  that might be less
code.  of course, what you have here is fine too.


>Index: nsXFormsSubmissionElement.h
...
>+  PRPackedBool   mSubmissionActive;
>+  PRPackedBool   mIsReplaceInstance;
>+  PRUint32       mFormat;

I think you should comment that these two new member variables
are valid over the course of a single submission.  So, I presume
they are valid only when mSubmissionActive is true, right?


>Index: nsXFormsSubmissionElement.cpp
...
>+nsXFormsSubmissionElement::GetInterface(const nsIID & aIID, void **aResult)

same comments apply to this guy.


>+nsXFormsSubmissionElement::OnRedirect(nsIHttpChannel *aHttpChannel,

and this guy.


>+  if (mFormat & (ENCODING_XML | ENCODING_MULTIPART_RELATED) ||
>+      mIsReplaceInstance) {

maybe this logic could be moved to a helper function that is
called in both places.	it'd be terrible if we ever got those
two places out of sync! :)


>@@ -482,16 +535,20 @@ nsXFormsSubmissionElement::Submit()
...
>+  nsAutoString replace;
>+  mElement->GetAttribute(NS_LITERAL_STRING("replace"), replace);
>+  mIsReplaceInstance = replace.EqualsLiteral("instance");

maybe mention why you are stashing this parameter.


>+  mFormat = GetSubmissionFormat(mElement);
>+  NS_ENSURE_STATE(mFormat != 0);

ditto
Attachment #175063 - Flags: superreview?(darin) → superreview+
Attachment #175063 - Flags: review?(doronr) → review+
(In reply to comment #15)
> So, I'm curious.  Why is this needed here?  We didn't do 
> this before (the channel would query callbacks from the
> loadgroup if necessary), so why do it now?
> 

Oh, no need for that code. Removing it...
Attached patch v.1.3Splinter Review
Attachment #175063 - Attachment is obsolete: true
checked in
Status: ASSIGNED → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: