angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.81k stars 27.5k forks source link

Should CDN example on Download documentation page contain Subresource Integrity info? #13968

Open hakanson opened 8 years ago

hakanson commented 8 years ago

https://docs.angularjs.org/misc/downloading which is sourced from downloading.ngdoc contains a "Including angular scripts from the Google CDN" section with this example for downloading from the Google CDN server:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>

With Subresource Integrity available in some browsers, should this example be updated to include an integrity attribute?

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"
        integrity="sha384-hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0"
        crossorigin="anonymous"></script>

This value was determined by downloading the minified 1.4.5 file and computing the hash using:

wget https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js 
cat angular.min.js | openssl dgst -sha384 -binary | openssl enc -base64 -A

This is a good suggestion from a security perspective, but requires the documentation to be updated with a new integrity attribute along with every version change. It also would require the developer to understand the purpose of this attribute value, because if they copy that example and only change 1.4.5 to 1.4.6, the script will not load and the developer will get a potentially confusing error in the Console like:

Failed to find a valid digest in the 'integrity' attribute for resource 'https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js' with computed SHA-256 integrity 'RIkiUZXLM0fYBgxgKBSCPnFxlu37oguHYe96c9t+HAg='. The resource has been blocked.

However, I would like to see the hashed for Subresource Integrity published somewhere, even if the Download documentation isn't the right place.

petebacondarwin commented 8 years ago

This sounds like an excellent idea. Although I am wondering if the safest path is to encourage people to use a published version. Isn't it better that a developer downloads a copy of the library, checks that it is valid and then generates their own SRI? That way they know for sure what content they are receiving.

hakanson commented 8 years ago

I agree it is best to download a copy and serve it yourself. However, many tutorials reference a CDN and if you use the Google CDN it is likely the browser gets a cache hit.

I was brainstorming that on every "release" (e.g. 1.4.9, 1.5.0, 1.5.1, etc), the integrity hash values could be generated and put into a SRI.json file. Those values could be used to both feed the documentation and used as input into a grunt/gulp/whatever build task for projects using angular.js to add the integrity attribute to script tags.

I'm not sure of the best solution, nor know of other libraries to look at as examples, but wanted to raise as an issue for the discussion on what to do.

ScottHelme commented 8 years ago

I'm not sure that I agree it's better to host your own copy as that can result in a worse experience for the user in a few ways. Using an asset from a CDN increases the chance that the user will already have it in their cache, reducing bandwidth requirements and potentially cost. This also improves performance which results in a better UX. SRI resolves the trust issue of allowing a 3rd party to serve your assets.

Many projects already provide checksum/integrity values for their downloads and SRI is, in effect, a method of automating that process in the browser. If we can't trust the integrity values provided by the project then we have greater concerns. Also, is a developer realistically going to review the entire Angular code base before generating their own integrity value?

The only addition I'd make here is that the provided script tags for each release should include more than the sha384 integrity value. A browser will always chose the strongest hash that it supports so it could be a good idea to include the sha256 and sha512 values for backwards and forwards compatibility too. As Kevin mentioned, there are simple methods to produce these values and include them in documentation for wider circulation.

petebacondarwin commented 8 years ago

I wasn't suggesting that one hosts their own copy. I was just saying that if you can't rely upon the CDN being valid then why should you rely upon us providing a valid hash? I was suggesting that you use the CDN but then generate your own hash from a copy downloaded from the CDN that you have confirmed is valid.

petebacondarwin commented 8 years ago

We will discuss this in the team meeting today and come up with a plan.

ScottHelme commented 8 years ago

Ok, self hosting aside as that seems to have taken us off topic, we still need to depend on you guys to provide the hash.

If we download a copy from a CDN where and how do we validate that? We're going to turn to you guys and say 'is this valid?'. That's exactly what integrity hashes are for, both in the context of SRI and otherwise. You as the author define the hash that all others are checked against. Failing that I'm not sure what other method we have to validate the file.

petebacondarwin commented 8 years ago

OK, so we discussed this in our team meeting. Here is the plan:

Is this acceptable? PRs are most welcome :-)

hakanson commented 8 years ago

What were you thinking for file extension and format? I initially thought of the .md5 and .sha1 style extensions, but would you want to generate .sha256 , .sha384 , and .sha512 files? Also, a file with that style extensions would appear to be compatible with shasum but the shasum utility generates hex values and includes the filename.

shasum -a 384 angular.min.js 
857348f426fc3890b7faad74b440bb454cda0c458a8c8422135960fad148aedb5e7c81be49c5022aa3a541512a726334  angular.min.js

I am wondering if you should generate an .integrity file with all the valid prefixed and base64-encoded hashes? Here would be an example of angular.min.js.integrity

sha256-RPPsQcSPq5bHR3vDUWpr9XR/NHMe9QAJ8Uwffw3LBDM=
sha384-hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0
sha512-y2SOwfsp2w/M2K9LzWRyFd/V62w3z9frSvAB96p7AJlyNIF7O40A6ezctVEgyI2gap6ikSoFpSrtC8Gztmjp4A==

The contents from a file like this could be used directly by IDEs or build tasks to insert / verify the value of the integrity attribute into the script tag.

petebacondarwin commented 8 years ago

I like the idea of the integrity file, I would consider going further and just provide the full script tag in those files... My motivation is that really what we want is to make it super-simple for developers to use the integrity values, right?

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"
        integrity="sha256-RPPsQcSPq5bHR3vDUWpr9XR/NHMe9QAJ8Uwffw3LBDM="
        crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"
        integrity="sha384-hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0"
        crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"
        integrity="sha512-y2SOwfsp2w/M2K9LzWRyFd/V62w3z9frSvAB96p7AJlyNIF7O40A6ezctVEgyI2gap6ikSoFpSrtC8Gztmjp4A=="
        crossorigin="anonymous"></script>
ScottHelme commented 8 years ago

Yeah, I like the idea of the full tag being generated, it's less work to implement then. It's also worth noting that you can/should specify multiple integrity attributes in the same tag and the browser will automatically select the strongest it supports. This allows for wider support of SRI in various browsers and reduces the problems if support for a hash were to be dropped due to future events.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"
    integrity="sha256-RPPsQcSPq5bHR3vDUWpr9XR/NHMe9QAJ8Uwffw3LBDM="
    integrity="sha384-hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0"
    integrity="sha512-y2SOwfsp2w/M2K9LzWRyFd/V62w3z9frSvAB96p7AJlyNIF7O40A6ezctVEgyI2gap6ikSoFpSrtC8Gztmjp4A=="
    crossorigin="anonymous">
</script>

This is covered in the SRI spec: https://www.w3.org/TR/SRI/#agility

hakanson commented 8 years ago

I like the idea of making it as simple as possible, because I fear most developers won't take the time to figure out how to use the raw integrity hash data.

Is there a concern this "unfairly" promotes the Google CDN. If a user happens to use a different CDN host like cdnjs, they need to change the src. I don't think there is an issue, but wanted to raise the point.

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.5/angular.min.js"
    integrity="sha256-RPPsQcSPq5bHR3vDUWpr9XR/NHMe9QAJ8Uwffw3LBDM="
    integrity="sha384-hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0"
    integrity="sha512-y2SOwfsp2w/M2K9LzWRyFd/V62w3z9frSvAB96p7AJlyNIF7O40A6ezctVEgyI2gap6ikSoFpSrtC8Gztmjp4A=="
    crossorigin="anonymous">
</script>
petebacondarwin commented 8 years ago

@hakanson well it is a Google project :-)

petebacondarwin commented 8 years ago

https://hacks.mozilla.org/2016/04/how-to-implement-sri-into-your-build-process/?utm_source=html5weekly&utm_medium=email

hakanson commented 8 years ago

Were you thinking of using the grunt-sri payload.json format?

{
    "payload": {
        "@angular.min.js": {
            "path": "angular.min.js",
            "type": null,
            "integrity": "sha256-RPPsQcSPq5bHR3vDUWpr9XR/NHMe9QAJ8Uwffw3LBDM= sha384-hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0 sha512-y2SOwfsp2w/M2K9LzWRyFd/V62w3z9frSvAB96p7AJlyNIF7O40A6ezctVEgyI2gap6ikSoFpSrtC8Gztmjp4A==",
            "hashes": {
                "sha256": "RPPsQcSPq5bHR3vDUWpr9XR/NHMe9QAJ8Uwffw3LBDM=",
                "sha384": "hXNI9Cb8OJC3+q10tEC7RUzaDEWKjIQiE1lg+tFIrttefIG+ScUCKqOlQVEqcmM0",
                "sha512": "y2SOwfsp2w/M2K9LzWRyFd/V62w3z9frSvAB96p7AJlyNIF7O40A6ezctVEgyI2gap6ikSoFpSrtC8Gztmjp4A==",
            }
        }
    }
}

it seems more robust that the gulp-sri JSON

{
    "path/to/file/relative/to/project/root/filename.ext": "srihash",
    //other entries 
}
petebacondarwin commented 8 years ago

grunt-sri looks like a good option

hakanson commented 8 years ago

FYI: Subresource Integrity Becomes a W3C Recommendation

petebacondarwin commented 8 years ago

PR anyone??

petebacondarwin commented 8 years ago

I started a bit of PR for this....

https://github.com/angular/angular.js/pull/15220

petebacondarwin commented 6 years ago

I don't think there is a great deal of interest in doing this.