Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce hook in ResourceEntityResolver to override fallback to remote access for DTD/XSD resolution #29662

Closed
scantor opened this issue Dec 8, 2022 · 12 comments
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement

Comments

@scantor
Copy link

scantor commented Dec 8, 2022

This is definitely true on Spring 6.0.2, I suspect it's equally true for 5.3.x and likely everything going back many years...

I would not characterize this as a security issue, but it seems like a poor default decision to allow the org.springframework.beans.factory.xml.ResourceEntityResolver class to fall through to remote http(s) resolution of XML entities.

I would think at minimum that ought to be off by default, or certainly an optional behavior at least, but there's no obvious way I saw to override that behavior save for duplicating the class and injecting the replacement into our custom bean defintion reader.

There's also a nasty bug in Java's XML parser that causes it to handle null coming back from an EntityResolver by just doing its own resolution, but throwing an exception out of the resolveEntity call does prevent that. So I think it would be a good improvement to either default throw, or at least provide some kind of option to do so.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 8, 2022
@sbrannen sbrannen changed the title ResourceEntityResolver defaults to remote access ResourceEntityResolver falls back to remote access for DTD and XSD resolution Dec 8, 2022
@sbrannen
Copy link
Member

sbrannen commented Dec 8, 2022

In which scenarios are you using ResourceEntityResolver to resolve DTDs or XSDs in XML from untrusted sources?

@sbrannen sbrannen added status: waiting-for-feedback We need additional information before we can continue in: core Issues in core modules (aop, beans, core, context, expression) labels Dec 8, 2022
@scantor
Copy link
Author

scantor commented Dec 8, 2022

It happens automatically when you happen to, for example, reference a schema with an import in it. If you accomodate the import in the appropriate way via spring.schemas, it works, but an accidental mistake on the classpath can leave the import un-hijacked, so to speak, and it falls through to remotely attempt resolution.

That's how we noticed the behavior. We've frequently had to react to accidental remote fetches over the years, this time I just happened to have the time to bottom out the logic and locate the spot I needed to replace, but once I realized what it was doing, I felt it was worth mentioning.

I don't argue that it's unreasonable to allow remote access (well, I would, but I'm not going to argue it here), but as a default, it didn't seem ideal to me, and there also didn't appear to be any direct means of preventing it without actually copying the Spring class and injecting the "part" of it that isn't doing that.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 8, 2022
@scantor
Copy link
Author

scantor commented Dec 8, 2022

I guess to be fair, there's another consideration here, which is that the documentation and behavior of Java's XML parser, with regard to the EntityResolver interface, is that returning null results in the systemId being directly resolved anyway. So another way to frame this I guess is, if all you're doing is fetching the systemId remotely, that's effectively what Java's doing already. So it's plausible to say "hey, write less code".

So having said that, if you were to consider this behavior suboptimal and change it, you'd really have to change it to throw an exception, since removing the section of code outright won't change the behavior anyway.

@jhoeller
Copy link
Contributor

We could log a warning (or info statement) whenever we fall back to remote access there. After all, accidental remote access is unfortunate for cloud deployment, even more so for native images where one certainly intends to include all relevant resources.

@scantor
Copy link
Author

scantor commented Dec 12, 2022

Another possible suggestion: rewrite the ResourceEntityResolver slightly to expose a protected method for doing the remote fetch step, so subclassers could easily block that step without having ot duplicate the earlier section of code with the local resolution.

@sbrannen
Copy link
Member

Let's go with a combination of the two proposals.

  • Extract the External dtd/xsd lookup via https functionality to a new protected method that subclasses can override.
  • Log a warning (or info) statement in that newly extracted method.

@sbrannen sbrannen added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Dec 16, 2022
@sbrannen sbrannen changed the title ResourceEntityResolver falls back to remote access for DTD and XSD resolution Introduce hook in ResourceEntityResolver to override fallback to remote access for DTD/XSD resolution Dec 16, 2022
@sbrannen sbrannen added this to the 6.0.4 milestone Dec 16, 2022
simonbasle added a commit to simonbasle/spring-framework that referenced this issue Dec 16, 2022
This commit extracts the DTD/XSD remote lookup fallback from the
resolveEntity method into a protected method.

A warn logging statement is added to the extracted fallback in order to
make it clear that remote lookup happened.

Overriding the protected method would allow users to avoid this fallback
entirely if it isn't desirable, without the need to duplicate the local
resolution code.

Fixes spring-projectsgh-29662.
@sbrannen
Copy link
Member

@sbrannen sbrannen closed this as not planned Won't fix, can't repro, duplicate, stale Dec 16, 2022
@sbrannen sbrannen removed this from the 6.0.4 milestone Dec 16, 2022
@scantor
Copy link
Author

scantor commented Apr 18, 2023

It appears the hook/fix for this was done without allowing the protected method to raise an IOException, so there's no way to properly signal back "don't do this" since returning null will just fall through.

@simonbasle
Copy link
Contributor

@scantor since the protected method is expected to be overridden, isn't it possible cover that case by having the override throw a RuntimeException ?

@scantor
Copy link
Author

scantor commented Apr 18, 2023

I don't know what that will do exactly, since your base class doesn't catch that. It may be more fatal than it should otherwise be, but it will definitely cause non-localized logging and handling of the condition.

@simonbasle
Copy link
Contributor

the fact that the fallback is hit means the resourcePath could not be defined. if an override of that particular flavor of resolver can't be allowed to fall back to resolving the sytemId as a URL and can't produce a fallback InputSource, then I'd argue it has indeed encountered a fatal case that calling code needs to deal with anyway.

I'm not sure we can add the throws SAXException, IOException clause at this point, breaking implementations that could potentially have been made since this was released. although it's unlikely there's been any in these 4 months except for yours)...

@scantor
Copy link
Author

scantor commented Apr 18, 2023

Ok, I did run a quick trial and it lands the exception in more or less the same try/catch block as it did before, so I can live with that answer, I just wanted to note the inconsistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants