Problem/Motivation
We have several sites that are scanned by Invicti / Netsparker to identify potential security concerns.
They repeatedly have the following finding:
Invicti Enterprise identified a version disclosure (Jquery) in the target web server's HTTP response.This information can help an attacker gain a greater understanding of the systems in use and potentially develop further attacks targeted at the specific version of Jquery (or other javascript libraries).
After reaching out to find what they are referring to, it is this
<script src="/core/assets/vendor/jquery/jquery.min.js?v=3.7.1"></script>
<script src="/core/misc/touchevents-test.js?v=10.4.5"></script>
<script src="/core/assets/vendor/backbone/backbone-min.js?v=1.6.0"></script>
It is a "Low" severity described here.
Steps to reproduce
- Load any Drupal page with JS Aggregation disabled, or install.php or update.php regardless of JS aggregation enabled
- View the source of the page
- Observe script tags for javascript files that reveal the version of the file as "?v=nn.nn"
Proposed resolution
This version identification is baked into core's JsCollectionRenderer::render(). The current approach makes it easy to debug as well as making sure that versions get locally cached appropriately with minimal extra bandwidth needed for unnecessary downloads. We could hash the version to make it undisclosed but still prevent unnecessary downloads.
The end result would have them looking something like this
<script src="/core/assets/vendor/jquery/jquery.min.js?hv=Myta2hC6"></script>
<script src="/core/assets/vendor/underscore/underscore-min.js?hv=OoQsK9Dv"></script>
<script src="/core/assets/vendor/once/once.min.js?hv=1R5uyUBY"></script>
This change may not ever be something that gets merged to core, but could live as a patch here for those of us that need to prevent version disclosure.
Remaining tasks
User interface changes
None
Introduced terminology
API changes
None
Data model changes
The query parameter v for 'version' would be come hv for 'hashed version'.
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #30 | Screenshot 2025-05-30 at 11.14.42 AM.png | 572.31 KB | dmundra |
| #29 | network_tab.png | 91.51 KB | jatingupta40 |
| #29 | after.png | 100.19 KB | jatingupta40 |
| #29 | before.png | 90.19 KB | jatingupta40 |
Issue fork drupal-3518344
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
swirtComment #5
swirtIf there is interest in this actually being merged, let me know and I will rework the failing asset tests to account for this change.
Comment #6
quietone commentedComment #7
cilefen commentedI don't see version numbers on assets with
or withoutaggregation enabled.Comment #8
cilefen commentedYes, they are there on the JavaScript files if aggregation is off. Why is aggregation off on the site?
Comment #9
swirt@cliefen Good question. Two different answers:
It is nice that aggregations solves it for most requests, and the Drupal recommended approach is to use aggregation, I don't think it should be a security requirement. There is nothing on the performance UI that says "use JS aggregation or your site will be more at risk."
Comment #10
swirtI updated the steps to reproduce to be more specific about when and where they are exposed.
Comment #11
cilefen commentedHeads up - a move in the opposite direction is being asked for in #3056065: Use library version as query strings for external css (like js does).
Comment #12
swirtLOL. Interesting. I don't think that would be a security risk though as CSS rarely has exploitable issues. At least not that I am aware of. :shrug:
Comment #13
swirtComment #14
catchI think the scanner here is unreasonable, especially when talking about a dev site in a development mode, but also it would be easy and still correct to use a hash here. Should probably be the very shortest xxhash available.
Comment #15
cilefen commented@swirt if you had a situation with Bootstrap, for example, seeing its CSS version would reveal its JS version. That’s all on the assumption that this is a security improvement which I’m doubtful about. In fact, don’t these libraries declare their versions within their code, defeating this proposed protection?
Comment #16
swirt@cliefen, These are all good questions, and I do not pretend to be a security expert but my current thinking on it is this:
Version disclosure is labeled as a low risk but is serious enough that Drupal does not reveal its minor version, Only the major.
If it is serious enough that Drupal does not reveal itself, then Drupal should probably show the same level of care to libraries and not reveal their specific version.
Many do. Example: jQuery.fn.jQuery will reveal its own version. If the library wants to reveal its own version, that is the library's choice. However, I think that should remain the choice of the library's maintainers. Drupal should not be the library's loose lipped friend that blabs the secret. I think this falls into the same reason why we routinely make the composer.lock and composer.json of our sites return an access denied.
Comment #17
swirt@catch I took your suggestion on this MR and made it use a shorter hash https://git.drupalcode.org/project/drupal/-/merge_requests/11787
Comment #18
cilefen commentedDAST tools and bad actors could additionally hash file contents and compare that against widely published lists to know an asset version. So, no, it actually wouldn't matter whether or not a given library includes its version in code. Anyone can still determine the version. I just want to be clear about how much this change actually would accomplish.
But, just to be clear, if a fast hash algo gets the job done, I don't see any reason not to do this.
Comment #19
swirtYes, makes sense. This alone does very little. But does a little. I will work on fixing the failing tests to get this completed.
Comment #20
swirtComment #26
swirtI think the Merge request !12122 for 11.x is ready for review. See QA steps in previous comment.
Comment #27
swirtComment #28
swirtComment #29
jatingupta40 commentedHello,
I have tested the MR #25 and it is working absolutely fine.



Before -
After -
Network Tab -
Comment #30
dmundraReviewing MR https://git.drupalcode.org/project/drupal/-/merge_requests/12122 and seeing that on install.php https://mr12122-4loygljzig4n8abvsdn1mltxwszyebvg.tugboatqa.com/core/inst... of the tugboat site the versions are hashed.
Comment #31
nod_This changes the query parameter from
v=tohv=, it shouldn't impact much things but I'd like an other review to make sure we don't have too much things depending on the query string name.Comment #32
swirt@nod_ That is a good concern. I grepped the Drupal core codebase for
I also examined the 22 instances of 'parse_str(' to see if anything was looking for a query param of 'v' . Found nothing
Same for the two instances of 'PHP_URL_QUERY'
I believe that nothing other than our tests (already altered for this merge request) are looking for a 'v' query parameter. My belief is based on the fact that ?v= does not exists with aggregation turned on other than a few special pages like update.php and install.php and they are not using the ?v param.
Comment #33
longwaveTo me this is won't fix, this is security by obscurity. It is trivial to find out the version of jQuery because it's embedded in the file anyway, and for any given library an attacker can just read the file themselves and fingerprint it to find out the version. If you use CDN hosted versions of JavaScript libraries the version number is usually explicitly embedded in the URL and you cannot change it. It seems more useful to actually keep the version number because it is occasionally helpful to developers to know which version of a JS library is in use.
Comment #34
nod_Thanks for all the work on the issue, after a discussion with @catch and @longwave we're not going to make this change.
We don't consider the security scanner report as relevant here. Additionally it seems it's only concerned about jQuery version specifically. We have a history of backporting some security fixes to an older jQuery version to prevent core from being exposed (so a site would use a "vulnerable" version, but with our backport it wouldn't be possible to exploit that particular vulnerability). We're usually on top of it regarding jQuery.
Closing and crediting since you've been a great contributor on this issue :)
Comment #35
swirtI appreciate that nod_ thank you. One last way to think about this, my final pitch I promise ;)
As longwave points out, Security through obscurity is not the best... by itself. It is the equivalent of me hiding my house key under the flower pot, but it is still better than leaving it in the door.
An old timey phrase of "Loose lips sink ships", or the more modern "Snitches get stiches" seem to apply.
What if the shoe was on the other foot?
Drupal intentionally obscures its own version
<meta name="Generator" content="Drupal 10 (https://www.drupal.org)" />What if some third party library decided to output "Drupal Version 10.36" because running internally it can detect the version. Would we want some other library disclosing that for us. Some might even find it helpful for development. I am pretty sure that leak would get closed in a hurry.
Why is our own obscurity fine, but we don't show the same care with third party code.
Drupal is popular in government, but so is this particular scanner. The amount of time people like me spend responding to the monthly scans is more than I enjoy. The "higher ups" don't always understand enough to say "this finding is legit", or "this finding is silly" They pay for the service and they want to trust its findings. Then I have to say, "it is not really anything to worry about"... then they search for "version disclosure" and they find pages like this and pretty soon they are starting to second guess my professionalism, because I told them, it was no big deal. I'd prefer if the product I love (Drupal) did not put me in that situation.
This is not specific to Jquery. The scanner triggers on any third party library where a version is displayed. For any given month there would be ~5 libraries it would include in its findings. I use the patch from this issue and they all go away.
Comment #36
cilefen commentedFWIW you can delete install.php and update.php in production deploys and enable aggregation. Those actions will eliminate the scanner detection.
Comment #37
nod_This could be fixed from contrib, you can change the version from a hook_js_alter:
would achieve the same and would keep people off your back :)
Comment #38
swirtThese are all good solutions,
any of which are no problem for me, a semi-experienced developer to implement. I am not sure if that holds true for the thousands of Drupal CMS adopters coming down the river.
Comment #39
longwave@swirt as @nod_ suggests this could be built as an opinionated contrib module which could be installed with no configuration, to hide the JS versions and the generator tag and anything else that you feel needs to be obscured; this just isn't something we are interested in supporting in core.
Comment #40
longwaveIn fact there already is contrib to hide the generator tag: https://www.drupal.org/project/remove_generator
So a companion module to hide JS versions makes sense to me.
Comment #41
nod_I was thinking a contrib module would be a good place to do that, yes.
Is there a security track for Drupal CMS? if there is and there is already a module being used for it might be a good idea to submit a patch to implement those few lines. Or make a module and ask for inclusion in Drupal CMS. Core and CMS have different audiences so it might be more appropriate there
Comment #42
cilefen commented@nod_ What do you mean by a "security track"?
Comment #43
catchThis is advertising the version, not obscuring it.
If a site is on Drupal 8 or 9, then you can find that out. I believe the meta tag is primarily so that crawlers can aggregate Drupal versions by site, and we want them to do this by major rather than patch or minor version. For example if there was version information on https://lookerstudio.google.com/u/0/reporting/55bc8fad-44c2-4280-aa0b-5f... (which there isn't, but if there was...)
Comment #44
catch@cilefen Drupal CMS 1.0 was organised by tracks for different areas of functionality with different track leads. I don't think there are functional tracks any more but the list is at #3454529: [META] Drupal CMS 1.0 work tracks.
Comment #45
swirtSorry for the confusion. I meant that it only shows "Drupal 10" and not "10.3.7" it only shows major version, not specific version.
Comment #46
swirtI will create a new module that is no problem.
I can speak to the rationale for avoiding "Version disclosure" in the project page. That is documented a bunch of places. But what should I say for the choice made here to preserve version disclosure as a feature, not a bug. Is it really just "It is occasionally helpful to developers to know which version of a JS library is in use." or is there a better reason?
Comment #47
longwaveNo, the primary reason is that it's security through obscurity: hiding version numbers achieves nothing. Almost all attacks are automated, most attackers won't even check the version number and will blindly try anyway, and those that do check will fingerprint the file, rather than interpreting the query string.
Comment #48
swirtClosing this out with the new module Prevent Version Disclosure
Thanks nod_ for the push in that direction. That way I don't have to keep re-rolling this patch for our use.
Comment #49
nod_Happy to help ! Thanks for being understanding