-
Notifications
You must be signed in to change notification settings - Fork 11.1k
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
[9.x] Support CSP nonce, SRI, and arbitrary attributes with Vite #43442
Conversation
d50ee62
to
40b8697
Compare
FYI: Support for nonce / arbitrary attributes in HMR mode has not yet been pushed, but will be supported before the PR is merged. Just need to discuss it a little more with the team on how we wanna handle a few things. |
Using Singleton & a Facade 👍 |
Shouldn't the CSP stuff be independent of Vite? I know that's the main use case, but other setups could still benefit from Laravel having a default CSP implementation without using Vite and all. Either way, nice addition! |
I haven't dived into Vite yet, but this is awesome and I'm definitely going to upgrade to Vite now. 😁 @tonysm it needs to be tied into the Vite implementation as that automatically generates the script and style tags on the page. So getting a nonce and SRI onto those tags needs to be supported by Vite anyway, this just makes it easier. Implementing a CSP nonce without this is still fairly trivial with But I do agree that having CSP in Laravel's core would be a really nice addition. All the developer would need to do* is tweak a config file to define the rules, toggle report-only, set a reporting endpoint, etc. (I'd PR it myself if I actually had some free time...) * Famous last words... |
@valorin the Vite implementation could rely on the core CSP stuff under the hood (the same way other setups would have to) |
@tonysm Oh I completely agree, but until that becomes a reality, having it in Vite like this is better than nothing. |
Really appreciate the feedback here folks ❤️ I've tried build this with future enhancements / integrations in mind. Taking Spatie's CSP package as an example, you could do either of the following...
See the default generator that ships with the package. <?php
namespace Spatie\Csp\Nonce;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Vite;
class LaravelVite implements NonceGenerator
{
public function generate(): string
{
return Vite::useNonce();
}
} or coming at it the other way, you could still generate your own nonce, and just tell Vite about the nonce to use... <?php
namespace Spatie\Csp\Nonce;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Vite;
class RandomString implements NonceGenerator
{
public function generate(): string
{
return Vite::useNonce(
Str::random(32)
);
}
} If there is a usecase I have not considered, please let me know. I would love to dive into it. But keep in mind this PR isn't about making CSP headers a first party solution, but making it so that you can use your own CSP header, something from a 3rd party, or potentially something first party further down the line. |
@valorin Yep, only that part. Maybe making some helper functions and stuff, like @timacdonald Got it. 👍🏼 |
@timacdonald Yep, makes sense. As you said, it's trivial to pass in an externally generated nonce, or pass your nonce into anything that needs it, so I'm happy. Is CSP support something that would be considered at some point, or out of scope? |
9cc5108
to
27fdf70
Compare
@valorin all I can really say is that it isn't something we are actively working on. Doesn't mean it is out of scope or not something we will consider in the future however. |
…avel#43442) * Support CSP nonce, SRI, and arbitrary attributes with Vite * formatting Co-authored-by: Taylor Otwell <taylor@laravel.com>
This PR introduces support for three distinct, but related, Vite features. Content security policy nonce (security), sub-resource integrity (security), and arbitrary tag attributes (security / app specific features).
They are combined in a single PR to create a holistic solution, as they all share the same problem space with regards to implementation, which is putting attributes on a HTML tag.
Content Security Policy (CSP) Nonce
You can read about what a CSP nonce is over on the MDN documentation, but summary it is a security feature where all script and style tags must contain the same nonce specified by the
Content-Security-Policy
header or else the browser will not execute them.Our Vite implementation now has first party support for creating a nonce that will be used on all generated script and style tags.
Here is an example of its usage:
All script and style tags generated with Vite will now have the
nonce="{cryptographically_secure_nonce}"
attribute.You can additionally retrieve the nonce throughout your application, for example in you blade views:
If you do not want to use Laravel's default nonce generation, and would like to use your own, you may pass a nonce to the
Vite::useCspNonce()
function. When generation tags, Vite will then use the given value in thenonce
attribute.When specifying your own nonce to use, it is still possible to retrieve the value specified via the Vite class as seen above:
Subresource integrity (SRI)
You can read about what a SRI nonce is over on the MDN documentation, but summary it is a security feature where the hash of the resource contents must match the hash in the
integrity
attribute of the tag loading the resource, or else the browser will not execute them.By convention, Laravel's Vite integration works on the convention of having an "integrity" property specified in the manifest for the given chunk. Here is an example manifest:
When chunks within the Vite manifest has the given shape, generated tags will contain have the
"integrity"
attribute added.But generating these integrity tags manually in each application is a PITA. I recommend instead of recreating the wheel, to lean on the vite-plugin-manifest-sri. When using this plugin, everything will "Just Work™️" without any additional work by the developer.
So to setup SRI in your application, you can install the plugin:
Add it to your
vite.config.js
...and Bob's your uncle.
If you use a different plugin that does not use the
"integrity"
key in the manifest, you may configure the key used by Vite to find the integrity hash. If the plugin creates the following manifest structure...You can tell Vite to check the
"SRI"
key in the manifest instead:If you want to disable this auto-detection completely, you can opt out of it:
Arbitrary attributes
The above features have been about providing first party support for some key security features with loaded script / css resources, however it is also useful for developers to be able to add arbitrary attributes to the generated tags without creating first party solutions for everything under the sun.
With the following API, developers may specify attributes to add to their script and stylesheet tags.
If you need to conditionally add attributes to tags based on the chunk, you may use a callback that receives the src, url, chunk, and the entire manifest:
The attributes specified have some logic behind them to be aware of...
The notable things here are that attributes with values
=== false
will filtered out, whiletrue
values will return only the attribute, which is the HTML attribute conventions.Note that:
attribute-with-false-value
is not present in the output HTML. We filter them out asfalse
attributes are simply missing attributes.attribute-with-empty-string
is present and maintains it's empty string value. This is considered atrue
attribute in HTML land.attribute-with-null
is not present in the output HTML. I wasn't 100% sure about this, but I feel that in HTML an "empty" value is a missing attribute, so this feels like the right move to me.You can read more about how boolean attributes work in the HTML specification.
Finally, any attributes you return from the closure will be merged with the default attributes - but your returned attributes will have precedence - so you have the ability to override anything and everything if you have some wild use-case where you need to do that.
Introducing these in a non-breaking way
Some of the changes I've made feel a little bit funky in one spot, as I've needed to keep the current functionality in place to not break current functionality for those who have extended the
Vite
class, while introducing new functionality that requires different method signatures.As such, I've left the old
makeTag
function in place and that continues to be called unless any of these new features is utilised. I've deprecated 3 methods and we can clean them up in10.x
.APII've built this using a static API. I would have preferred to go down the path of an instance API, but I felt for this it did make sense to match the more static style API even thought it is not a Facade. If we want to pursue an instance API we would need to bind the Vite class as a singleton and then have the API used as follows:Documentation
I'll work on a follow up PR for the docs.