├── 20140318 └── index.html ├── .github └── workflows │ └── build-validate-publish.yml ├── .gitignore ├── .pr-preview.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ECHIDNA ├── LICENSE.md ├── Makefile ├── README.markdown ├── default.css ├── index.bs ├── index.kramdown.html ├── signature-based-restrictions-explainer.markdown ├── spec_v1.markdown ├── template.erb └── w3c.json /.github/workflows/build-validate-publish.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: {} 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | main: 9 | name: Build, Validate, and Publish 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: w3c/spec-prod@v2 14 | with: 15 | GH_PAGES_BRANCH: gh-pages 16 | BUILD_FAIL_ON: nothing 17 | VALIDATE_LINKS: false 18 | VALIDATE_MARKUP: true 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | index.html 2 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Application Security Working Group 2 | 3 | Contributions to this repository are intended to become part of Recommendation-track documents 4 | governed by the [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy/) and 5 | [Document License](https://www.w3.org/Consortium/Legal/copyright-documents). To contribute, you must 6 | either participate in the relevant W3C Working Group or make a non-member patent licensing 7 | commitment. 8 | 9 | If you are not the sole contributor to a contribution (pull request), please identify all 10 | contributors in the pull request's body or in subsequent comments. 11 | 12 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 13 | 14 | ``` 15 | +@github_username 16 | ``` 17 | 18 | If you added a contributor by mistake, you can remove them in a comment with: 19 | 20 | ``` 21 | -@github_username 22 | ``` 23 | 24 | If you are making a pull request on behalf of someone else but you had no part in designing the 25 | feature, you can remove yourself with the above syntax. 26 | 27 | # Tests 28 | 29 | See [CONTRIBUTING.md](https://github.com/w3c/webappsec/blob/master/CONTRIBUTING.md). 30 | -------------------------------------------------------------------------------- /ECHIDNA: -------------------------------------------------------------------------------- 1 | # ECHIDNA configuration 2 | # Note: we have to list additional resource, when they come up (e.g. images) 3 | index.html?specStatus=WD;shortName=SRI respec 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors under the [W3C Document 2 | License](http://www.w3.org/Consortium/Legal/copyright-documents). 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: clean index.html index.kramdown.html 2 | 3 | clean: 4 | rm -rf index.html index.kramdown.html 5 | 6 | index.kramdown.html: spec_v1.markdown template.erb 7 | sed -e 's/\[\[/\\[\\[/g' -e 's/\]\]/\\]\\]/g' ./spec_v1.markdown | kramdown --parse-block-html --template='template.erb' > index.kramdown.html 8 | 9 | index.html: index.bs 10 | curl https://api.csswg.org/bikeshed/ -F file=@index.bs -F force=1 --fail -o index.html 11 | 12 | local: 13 | bikeshed -f spec index.bs 14 | 15 | publish: all 16 | git push origin master 17 | git push origin master:gh-pages 18 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Subresource Integrity 2 | ===================== 3 | 4 | Editor's draft: https://w3c.github.io/webappsec-subresource-integrity/ 5 | 6 | The spec text is written in [Bikeshed]. After editing `index.bs`, you can 7 | generate a new HTML draft by typing `make` on the command line. Note that this 8 | requires an active internet connection to download up-to-date config files. 9 | 10 | You can publish a new draft by typing `make publish` (which simply pushes 11 | the local `master` branch to GitHub's `gh-pages` branch). 12 | 13 | Please do not modify `spec_v1.markdown` or its compiled file, 14 | `index.kramdown.html`. These are the spec files from version 1 of the spec, 15 | which was originally written in Kramdown, but work is now done exclusively in 16 | `index.bs`. 17 | 18 | Pull requests happily reviewed. 19 | 20 | [bikeshed]: https://github.com/tabatkins/bikeshed 21 | 22 | -------------------------------------------------------------------------------- /default.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Style sheet for WebAppSec specifications (stolen wholesale from the CSSWG), 3 | * to be used in addition to http://www.w3.org/StyleSheets/TR/W3C-{WD,PR,REC} 4 | */ 5 | 6 | @media print { 7 | html { margin: 0 !important } 8 | body { font-family: serif } 9 | th, td { font-family: inherit } 10 | a { color: inherit !important } 11 | .example:before { font-family: serif !important } 12 | a:link, a:visited { text-decoration: none !important } 13 | a:link:after, a:visited:after { /* create a cross-ref "see..." */ } 14 | } 15 | @page { 16 | margin: 1.5cm 1.1cm; 17 | } 18 | 19 | body { 20 | counter-reset: exampleno figure issue; 21 | max-width: 50em; 22 | margin: 0 auto !important; 23 | } 24 | 25 | /* Pagination */ 26 | h1, h2, h3, h4, h5, h6 { page-break-after: avoid } 27 | figure, div.figure, div.sidefigure, pre, table.propdef, table.propdef-extra, 28 | .example { page-break-inside: avoid } 29 | dt { page-break-after: avoid } 30 | 31 | span.id {float: right; font-weight: bold} 32 | 33 | /* General Structural Markup */ 34 | h2, h3, h5, h6 { margin-top: 3em; } 35 | 36 | /* #subtitle is a subtitle in an H2 under the H1 */ 37 | h1 + h2, #subtitle + h2 { margin-top: 0; } 38 | 39 | h4 { margin-top: 4em; } 40 | 41 | h2 + h3, h3 + h4, h4 + h5, h5 + h6 { margin-top: 1.2em } 42 | 43 | hr:not([title]) { 44 | font-size: 1.5em; 45 | text-align: center; 46 | margin: 1em auto; 47 | border: transparent solid; 48 | background: transparent; 49 | } 50 | hr:not([title])::before { 51 | content: "\1F411\2003\2003\1F411\2003\2003\1F411"; 52 | } 53 | 54 | p, div.note, div.issue, details.why { 55 | margin-top: 1em; 56 | margin-bottom: 1em; 57 | } 58 | 59 | dd > p:first-child, li > p:first-child, .note > p:first-child, .issue > p:first-child { 60 | margin-top: 0 61 | } 62 | 63 | pre { 64 | margin-top: 1em; 65 | margin-bottom: 1em; 66 | } 67 | 68 | pre, code { 69 | font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace; 70 | font-size: .9em; 71 | } 72 | 73 | img { 74 | border-style: none; 75 | color: white; 76 | } 77 | .toc { 78 | } 79 | 80 | body { 81 | line-height: 1.5; 82 | } 83 | 84 | a.logo:link, a.logo:visited { 85 | padding: 0; 86 | border-style: none; 87 | } 88 | 89 | dl dd { margin: 0 0 1em 2em } 90 | .head dd { margin-bottom: 0; } 91 | ul, ol { margin-left: 0; padding-left: 2em; } 92 | li { margin: 0.25em 2em 0.5em 0; padding-left: 0 } 93 | 94 | ul.indexlist { margin-left: 0; columns: 13em; } 95 | ul.indexlist li { margin-left: 0; list-style: none } 96 | ul.indexlist li li { margin-left: 1em } 97 | ul.indexlist a { font-weight: bold; } 98 | ul.indexlist ul, ul.indexlist dl { font-size: smaller; } 99 | ul.indexlist dl { margin-top: 0; } 100 | ul.indexlist dt { margin: .2em 0 .2em 20px;} 101 | ul.indexlist dd { margin: .2em 0 .2em 40px;} 102 | 103 | /* .self-link is a link to the element */ 104 | .heading, .issue, .note, .example, li, dt { position: relative; } 105 | a.self-link { 106 | position: absolute; 107 | top: 0; 108 | left: -2.5em; 109 | width: 2em; 110 | height: 2em; 111 | text-align: center; 112 | border: none; 113 | transition: opacity .2s; 114 | opacity: .5; 115 | } 116 | a.self-link:hover { 117 | opacity: 1; 118 | } 119 | .heading > a.self-link { 120 | font-size: 83%; 121 | } 122 | li > a.self-link { 123 | left: -3.5em; 124 | } 125 | dfn > a.self-link { 126 | top: auto; 127 | left: auto; 128 | opacity: 0; 129 | width: 1.5em; 130 | height: 1.5em; 131 | background: gray; 132 | color: white; 133 | font-style: normal; 134 | transition: opacity .2s, background-color .2s, color .2s; 135 | } 136 | dfn:hover > a.self-link { 137 | opacity: 1; 138 | } 139 | dfn > a.self-link:hover { 140 | color: black; 141 | } 142 | 143 | a.self-link::before { content: "¶"; } 144 | .heading > a.self-link::before { content: "§"; } 145 | dfn > a.self-link::before { content: "#"; } 146 | 147 | /* Examples */ 148 | 149 | .example { 150 | counter-increment: exampleno; 151 | } 152 | .example:before { 153 | content: "Example"; 154 | content: "Example " counter(exampleno); 155 | min-width: 7.5em; 156 | text-transform: uppercase; 157 | display: block; 158 | } 159 | div.illegal-example:before, pre.illegal-example:before { 160 | content: "Invalid Example"; 161 | content: "Invalid Example" counter(exampleno); 162 | } 163 | .example, .illegal-example, div.html, div.illegal-html, div.xml, 164 | div.illegal-xml, pre.html, 165 | pre.illegal-html, pre.xml, pre.illegal-xml { 166 | padding: 0.5em; 167 | margin: 1em 0; 168 | position: relative; 169 | clear: both; 170 | } 171 | pre.example, pre.illegal-example, pre.html, 172 | pre.illegal-html, pre.xml, pre.illegal-xml { 173 | padding-top: 1.5em; 174 | } 175 | pre.illegal-example { color: red } 176 | div.illegal-example { color: red } 177 | div.illegal-example p { color: black } 178 | 179 | div.html { color: #600 } 180 | pre.html { color: #600 } 181 | pre.illegal-html { color: red } 182 | div.illegal-html { color: red } 183 | div.illegal-html p { color: black } 184 | pre.deprecated-html { color: red } 185 | div.deprecated-html { color: red } 186 | div.deprecated-html p { color: black } 187 | 188 | div.xml { color: #600 } 189 | pre.xml { color: #600 } 190 | pre.illegal-xml { color: red } 191 | div.illegal-xml { color: red } 192 | div.illegal-xml p { color: black } 193 | 194 | .css, .property { color: #005a9c } /* inline CSS code (SPAN/CODE) */ 195 | code.css { font-family: inherit; font-size: 100% } 196 | code.html { color: #600 } /* inline HTML */ 197 | code.xml { color: #600 } /* inline XML */ 198 | .property { font: inherit; white-space: nowrap; } /* name of a CSS property (SPAN) */ 199 | .descriptor { } /* name of a CSS descriptor (SPAN) */ 200 | .type { font-style: italic } /* A value for a property */ 201 | 202 | /* Autolinks produced using Bikeshed. */ 203 | [data-link-type="property"]::before, 204 | [data-link-type="propdesc"]::before, 205 | [data-link-type="descriptor"]::before, 206 | [data-link-type="value"]::before, 207 | [data-link-type="function"]::before, 208 | [data-link-type="at-rule"]::before, 209 | [data-link-type="selector"]::before, 210 | [data-link-type="maybe"]::before {content: "\2018";} 211 | [data-link-type="property"]::after, 212 | [data-link-type="propdesc"]::after, 213 | [data-link-type="descriptor"]::after, 214 | [data-link-type="value"]::after, 215 | [data-link-type="function"]::after, 216 | [data-link-type="at-rule"]::after, 217 | [data-link-type="selector"]::after, 218 | [data-link-type="maybe"]::after {content: "\2019";} 219 | [data-link-type].production::before, 220 | [data-link-type].production::after, 221 | .prod [data-link-type]::before, 222 | .prod [data-link-type]::after { content: ""; } 223 | 224 | 225 | /* Element-type link styling */ 226 | [data-link-type=element] { font-family: monospace; } 227 | [data-link-type=element]::before { content: "<" } 228 | [data-link-type=element]::after { content: ">" } 229 | 230 | dfn { font-weight: bolder; } 231 | 232 | .issue, .note, .example, .why { 233 | padding: .5em; 234 | /* padding: .5rem; /* proposed unit in css3-values */ 235 | border-left-width: .5em; 236 | /* border-left-width: .5rem; /* proposed unit in css3-values */ 237 | border-left-style: solid; 238 | } 239 | span.note, span.issue { 240 | padding: .1em .5em .15em; 241 | border-right-width: .5em; 242 | border-right-style: solid; 243 | } 244 | 245 | /* Open issue / editorial remark; not intended for a final publication */ 246 | .issue { 247 | border-color: #E05252; 248 | background: #FBE9E9; 249 | counter-increment: issue; 250 | overflow: auto; 251 | } 252 | 253 | .issue:before { 254 | content: "Issue " counter(issue); 255 | padding-right: 1em; 256 | text-transform: uppercase; 257 | color: #E05252; 258 | } 259 | 260 | /* Class note is a non-normative note. May be inline or a P or DIV */ 261 | .note, .why { 262 | border-color: #52E052; 263 | background: #E9FBE9; 264 | overflow: auto; 265 | } 266 | 267 | .normref { color: red } 268 | .informref { color: green } 269 | 270 | /* Example box */ 271 | .example { 272 | border-color: #E0CB52; 273 | background: #FCFAEE; 274 | overflow: auto; 275 | } 276 | 277 | .example:before { 278 | color: #B9AB2D; 279 | font-family: sans-serif; 280 | } 281 | 282 | details.why { 283 | border-color: #52E052; 284 | background: #E9FBE9; 285 | display: block; 286 | } 287 | 288 | details.why > summary { 289 | font-style: italic; 290 | display: block; 291 | } 292 | 293 | details.why[open] > summary { 294 | border-bottom: 1px silver solid; 295 | } 296 | 297 | /* ToC not indented, but font style shows hierarchy */ 298 | ul.toc {margin: 1em 0; padding: 0; line-height: 1.3; font-weight: bold; /*text-transform: uppercase;*/ } 299 | ul.toc ul {margin: 0; padding: 0; font-weight: normal; text-transform: none; } 300 | ul.toc ul ul {margin: 0 0 0 2em; font-style: italic; } 301 | ul.toc ul ul ul {margin: 0} 302 | ul.toc > li {margin: 1.5em 0; padding: 0; } 303 | ul.toc ul.toc li { margin: 0.3em 0 0 0; } 304 | ul.toc a { text-decoration: none; border-bottom-style: none; } 305 | ul.toc a:hover, ul.toc a:focus { border-bottom-style: solid; } 306 | /* 307 | ul.toc li li li, ul.toc li li li ul {margin-left: 0; display: inline} 308 | ul.toc li li li ul, ul.toc li li li ul li {margin-left: 0; display: inline} 309 | */ 310 | 311 | /* Section numbers in a column of their own */ 312 | ul.toc span.secno {float: left; width: 4em; margin-left: -5em} 313 | ul.toc ul ul span.secno { margin-left: -7em; } 314 | /*ul.toc span.secno {text-align: right}*/ 315 | ul.toc li {clear: both} 316 | ul.toc {margin-left: 5em} 317 | /* If we had 'tab', floats would not be needed here: 318 | ul.toc span.secno {tab: 5em right; margin-right: 1em} 319 | ul.toc li {text-indent: 5em hanging} 320 | The second line in case items wrap 321 | */ 322 | 323 | ul.index { 324 | list-style: none; 325 | } 326 | 327 | s, del {text-decoration: line-through; color: red} 328 | u, ins {text-decoration: underline; color: #080} 329 | 330 | div.figure, p.figure, div.sidefigure, figure { 331 | text-align: center; 332 | margin: 2.5em 0; 333 | } 334 | div.figure pre, div.sidefigure pre, figure pre { 335 | text-align: left; 336 | display: table; 337 | margin: 1em auto; 338 | } 339 | .figure table, figure table { 340 | margin: auto; 341 | } 342 | div.sidefigure, figure.sidefigure { 343 | float: right; 344 | width: 50%; 345 | margin: 0 0 0.5em 0.5em 346 | } 347 | div.figure img, div.sidefigure img, figure img, 348 | div.figure object, div.sidefigure object, figure object { 349 | display: block; 350 | margin: auto; 351 | max-width: 100% 352 | } 353 | p.caption, figcaption, caption { 354 | text-align: center; 355 | font-style: italic; 356 | font-size: 90%; 357 | } 358 | p.caption:before, figcaption:before { 359 | content: "Figure " counter(figure) ". "; 360 | font-weight: bold; 361 | } 362 | p.caption, figcaption { 363 | counter-increment: figure; 364 | } 365 | 366 | /* DL list is indented, but figure inside it is not */ 367 | dd { margin-left: 2em } 368 | dd div.figure, dd figure { margin-left: -2em } 369 | 370 | sup { 371 | vertical-align: super; 372 | font-size: 80% 373 | } 374 | 375 | /* "Equations" (not real MathML, but simple HTML) are put in a 376 | blockquote and may have an equation number. We make sure the 377 | blockquote has enough margin on the right and then put the equation 378 | number there. */ 379 | 380 | blockquote { 381 | margin: 0.5em 4em 0.5em 2em; 382 | text-indent: 0; 383 | } 384 | .eqno { 385 | text-align: right; 386 | float: right; 387 | width: 3em; 388 | margin: 0 -4em 0 0; 389 | font-weight: bold; 390 | /* background: silver; color: black; padding: 0.1em */ 391 | } 392 | 393 | table.equiv-table { border-spacing: 0; margin: 0.5em 0 } 394 | table.equiv-table th, table.equiv-table td { padding: 0.3em } 395 | table.equiv-table th { text-align: left } 396 | /* table.equiv-table th:first-child { text-align: right } */ 397 | table.equiv-table td, table.equiv-table th { border-bottom: thin solid #666 } 398 | table.equiv-table { border-bottom: hidden } 399 | table.equiv-table { empty-cells: show } 400 | table.equiv-table caption { margin: 0.5em 0 0 0 } 401 | 402 | /* Style for table of properties */ 403 | table.proptable { 404 | font-size: small; 405 | border-collapse: collapse; 406 | border-spacing: 0; 407 | text-align: left; 408 | margin: 1em 0; 409 | } 410 | 411 | table.proptable td, table.proptable th { 412 | padding: 0.4em; 413 | text-align: center; 414 | } 415 | 416 | table.proptable tr:hover td { 417 | background: #DEF; 418 | } 419 | 420 | 421 | /* Style for table that defines a property or a descriptor */ 422 | table.propdef, table.propdef-extra, table.descdef, table.definition-table { 423 | border-spacing: 0; 424 | padding: 0 1em 0.5em; 425 | width: 100%; 426 | table-layout: fixed; 427 | background: #DEF; 428 | margin: 1.2em 0; 429 | border-left: 0.5em solid #8CCBF2; 430 | } 431 | 432 | table.propdef td, table.propdef-extra td, table.descdef td, table.definition-table td, 433 | table.propdef th, table.propdef-extra th, table.descdef th, table.definition-table th { 434 | padding: 0.5em; 435 | vertical-align: baseline; 436 | border-bottom: 1px solid #bbd7e9; 437 | } 438 | /* 439 | table.propdef dfn, table.propdef-extra dfn, table.descdef dfn { 440 | font-weight: bold; 441 | font-style: normal 442 | } 443 | */ 444 | 445 | table.propdef td:first-child, 446 | table.propdef-extra td:first-child, 447 | table.descdef td:first-child, 448 | table.definition-table td:first-child, 449 | table.propdef th:first-child, 450 | table.propdef-extra th:first-child, 451 | table.descdef th:first-child, 452 | table.definition-table th:first-child { 453 | font-style: italic; 454 | font-weight: normal; 455 | width: 8.3em; 456 | padding-left: 1em; 457 | } 458 | table.propdef td[colspan]:first-child, 459 | table.propdef-extra td[colspan]:first-child, 460 | table.descdef td[colspan]:first-child, 461 | table.definition-table td[colspan]:first-child, 462 | table.propdef th[colspan]:first-child, 463 | table.propdef-extra th[colspan]:first-child, 464 | table.descdef th[colspan]:first-child, 465 | table.definition-table th[colspan]:first-child { 466 | font-style: inherit 467 | } 468 | table.propdef tr:first-child, 469 | table.propdef-extra tr:first-child, 470 | table.descdef tr:first-child, 471 | table.definition-table tr:first-child { 472 | 473 | } 474 | 475 | table.propdef > tbody > tr:last-child th, 476 | table.propdef-extra > tbody > tr:last-child th, 477 | table.descdef > tbody > tr:last-child th, 478 | table.definition-table > tbody > tr:last-child th, 479 | table.propdef > tbody > tr:last-child td, 480 | table.propdef-extra > tbody > tr:last-child td, 481 | table.descdef > tbody > tr:last-child td, 482 | table.definition-table > tbody > tr:last-child td { 483 | border-bottom: 0; 484 | } 485 | 486 | table.propdef tr:first-child th, 487 | table.propdef-extra tr:first-child th, 488 | table.descdef tr:first-child th, 489 | table.definition-table tr:first-child th, 490 | table.propdef tr:first-child td, 491 | table.propdef-extra tr:first-child td, 492 | table.descdef tr:first-child td, 493 | table.definition-table tr:first-child td { 494 | padding-top: 1em; 495 | } 496 | 497 | /* For when values are extra-complex and need formatting for readability */ 498 | table td.pre { 499 | white-space: pre-wrap; 500 | } 501 | 502 | /* A footnote at the bottom of a propdef */ 503 | table.propdef td.footnote, 504 | table.propdef-extra td.footnote, 505 | table.descdef td.footnote, 506 | table.definition-table td.footnote { 507 | padding-top: 0.6em; 508 | width: auto 509 | } 510 | table.propdef td.footnote:before, 511 | table.propdef-extra td.footnote:before, 512 | table.descdef td.footnote:before, 513 | table.definition-table td.footnote:before { 514 | content: " "; 515 | display: block; 516 | height: 0.6em; 517 | width: 4em; 518 | border-top: thin solid; 519 | } 520 | 521 | /* The link in the first column in the property table (formerly a TD) */ 522 | table.proptable td .property, 523 | table.proptable th .property { 524 | display: block; 525 | text-align: left; 526 | font-weight: bold; 527 | } 528 | 529 | 530 | /* Styling for IDL fragments */ 531 | 532 | pre.idl { 533 | padding: .5em 1em; 534 | background: #DEF; 535 | margin: 1.2em 0; 536 | border-left: 0.5em solid #8CCBF2; 537 | } 538 | pre.idl :link, pre.idl :visited { 539 | color:inherit; 540 | background:transparent; 541 | } 542 | 543 | 544 | /* CSS modules typically don't use MUST, SHOULD etc. from RFC 2119, 545 | or, if they do, they don't put them in uppercase. But the following 546 | class is provided in case a spec wants to use RFC 2119 terms in 547 | uppercase in the source. */ 548 | 549 | em.rfc2119 { 550 | text-transform: lowercase; 551 | font-variant: small-caps; 552 | font-style: normal 553 | } 554 | 555 | /* In Profile specs, a table of required features: */ 556 | 557 | table.features th { 558 | background: #00589f; 559 | color: #fff; 560 | text-align: left; 561 | padding: 0.2em 0.2em 0.2em 0.5em; 562 | } 563 | table.features td { 564 | vertical-align: top; 565 | border-bottom: 1px solid #ccc; 566 | padding: 0.3em 0.3em 0.3em 0.7em; 567 | } 568 | 569 | 570 | /* Style for data tables (and properly marked-up proptables) */ 571 | 572 | .data, .proptable { 573 | margin: 1em auto; 574 | border-collapse: collapse; 575 | width: 100%; 576 | border: hidden; 577 | } 578 | .data { 579 | text-align: center; 580 | width: auto; 581 | } 582 | .data caption { 583 | width: 100%; 584 | } 585 | 586 | .data td, .data th, 587 | .proptable td, .proptable th { 588 | padding: 0.5em; 589 | border-width: 1px; 590 | border-color: silver; 591 | border-top-style: solid; 592 | } 593 | 594 | .data thead td:empty { 595 | padding: 0; 596 | border: 0; 597 | } 598 | 599 | .data thead th[scope="row"], 600 | .proptable thead th[scope="row"] { 601 | text-align: right; 602 | color: inherit; 603 | } 604 | 605 | .data thead, 606 | .proptable thead, 607 | .data tbody, 608 | .proptable tbody { 609 | color: inherit; 610 | border-bottom: 2px solid; 611 | } 612 | 613 | .data colgroup { 614 | border-left: 2px solid; 615 | } 616 | 617 | .data tbody th:first-child, 618 | .proptable tbody th:first-child , 619 | .data tbody td[scope="row"]:first-child, 620 | .proptable tbody td[scope="row"]:first-child { 621 | text-align: right; 622 | color: inherit; 623 | border-right: 2px solid; 624 | border-top: 1px solid silver; 625 | padding-right: 1em; 626 | } 627 | .data.define td:last-child { 628 | text-align: left; 629 | } 630 | 631 | .data tbody th[rowspan], 632 | .proptable tbody th[rowspan], 633 | .data tbody td[rowspan], 634 | .proptable tbody td[rowspan]{ 635 | border-left: 1px solid silver; 636 | } 637 | 638 | .data tbody th[rowspan]:first-child, 639 | .proptable tbody th[rowspan]:first-child, 640 | .data tbody td[rowspan]:first-child, 641 | .proptable tbody td[rowspan]:first-child{ 642 | border-left: 0; 643 | border-right: 1px solid silver; 644 | } 645 | 646 | .complex.data th, 647 | .complex.data td { 648 | border: 1px solid silver; 649 | } 650 | 651 | .data td.long { 652 | vertical-align: baseline; 653 | text-align: left; 654 | } 655 | 656 | .data img { 657 | vertical-align: middle; 658 | } 659 | 660 | table.propdef { 661 | table-layout: auto; 662 | } 663 | .propdef th { 664 | font-style: italic; 665 | font-weight: normal; 666 | text-align: left; 667 | width: 3em; 668 | } 669 | dt dfn code { 670 | font-size: inherit; 671 | } 672 | 673 | /* Style for switch/case
s */ 674 | dl.switch { 675 | padding-left: 2em; 676 | } 677 | dl.switch > dt { 678 | text-indent: -1.5em; 679 | } 680 | dl.switch > dt:before { 681 | content: '\21AA'; 682 | padding: 0 0.5em 0 0; 683 | display: inline-block; 684 | width: 1em; 685 | text-align: right; 686 | line-height: 0.5em; 687 | } 688 | 689 | 690 | /* Style for At Risk features (intended as editorial aid, not intended for publishing) */ 691 | .atrisk::before { 692 | position: absolute; 693 | margin-left: -5em; 694 | margin-top: -2px; 695 | padding: 4px; 696 | border: 1px solid; 697 | content: 'At risk'; 698 | font-size: small; 699 | background-color: white; 700 | color: gray; 701 | border-radius: 1em; 702 | text-align: center; 703 | } 704 | 705 | .toc .atrisk::before { content:none } 706 | 707 | 708 | /* This is mostly to make the list inside the CR exit criteria more compact. */ 709 | ol.inline, ol.inline li {display: inline; padding: 0; margin: 0} 710 | ol.inline {counter-reset: list-item} 711 | ol.inline li {counter-increment: list-item} 712 | ol.inline li:before {content: "(" counter(list-item) ") "; font-weight: bold} 713 | 714 | /* This styles the obsoletion notice on some of our older/abandoned specs. */ 715 | details.annoying-warning[open] { 716 | background: #fdd; 717 | color: red; 718 | font-weight: bold; 719 | text-align: center; 720 | padding: .5em; 721 | border: thick solid red; 722 | border-radius: 1em; 723 | position: fixed; 724 | left: 1em; 725 | right: 1em; 726 | bottom: 1em; 727 | z-index: 1000; 728 | } 729 | 730 | details.annoying-warning:not([open]) > summary { 731 | background: #fdd; 732 | color: red; 733 | font-weight: bold; 734 | text-align: center; 735 | padding: .5em; 736 | } 737 | -------------------------------------------------------------------------------- /index.bs: -------------------------------------------------------------------------------- 1 | 25 | 26 |
 27 | spec: ABNF; urlPrefix: https://tools.ietf.org/html/rfc5234
 28 |   type: dfn
 29 |     text: VCHAR; url: appendix-B.1
 30 |     text: WSP; url: appendix-B.1
 31 |   type: grammar
 32 |     text: VCHAR; url: appendix-B.1
 33 |     text: WSP; url: appendix-B.1
 34 | 
 35 | spec: FETCH; urlPrefix: https://fetch.spec.whatwg.org/
 36 |   type: dfn
 37 |     text: main fetch; url: main-fetch
 38 | 
 39 | spec: RFC7234; urlPrefix: https://tools.ietf.org/html/rfc7234
 40 |   type: dfn
 41 |     text: Cache-Control; url: section-5.2
 42 |     text: no-transform; url: section-5.2.1.6
 43 | 
 44 | spec: SHA2; urlPrefix: https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
 45 |   type: dfn
 46 |     text: SHA-1; url: #
 47 |     text: SHA-2; url: #
 48 |     text: SHA-256; url: #
 49 |     text: SHA-384; url: #
 50 |     text: SHA-512; url: #
 51 | 
 52 | spec: RFC8288; urlPrefix: https://tools.ietf.org/html/rfc8288
 53 |   type: http-header
 54 |     text: link
 55 | 
 56 | spec: RFC9651; urlPrefix: https://tools.ietf.org/html/rfc9651
 57 |   type: dfn
 58 |     text: Dictionary; url: name-dictionaries
 59 |     text: inner list; url: name-inner-lists
 60 |     text: token; url: name-tokens
 61 | 
 62 | 
63 | 66 | 67 |
 68 | {
 69 |   "SHA2": {
 70 |     "href": "http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf",
 71 |     "title": "FIPS PUB 180-4, Secure Hash Standard"
 72 |   }
 73 | }
 74 | 
75 | 76 | 77 | 78 | # Introduction # {#intro} 79 | 80 | Sites and applications on the web are rarely composed of resources from 81 | only a single origin. For example, authors pull scripts and styles from a 82 | wide variety of services and content delivery networks, and must trust 83 | that the delivered representation is, in fact, what they expected to 84 | load. If an attacker can trick a user into downloading content from 85 | a hostile server (via DNS [[RFC1035]] poisoning, or other such means), the author has 86 | no recourse. Likewise, an attacker who can replace the file on the Content 87 | Delivery Network (CDN) server has the ability to inject arbitrary content. 88 | 89 | Delivering resources over a secure channel mitigates some of this risk: with 90 | TLS [[TLS]], HSTS [[RFC6797]], and pinned public keys 91 | [[RFC7469]], a user agent can be fairly certain 92 | that it is indeed speaking with the server it believes it's talking to. These 93 | mechanisms, however, authenticate only the server, not the content. An 94 | attacker (or administrator) with access to the server can manipulate content with 95 | impunity. Ideally, authors would not only be able to pin the keys of a 96 | server, but also pin the content, ensuring that an exact representation of 97 | a resource, and only that representation, loads and executes. 98 | 99 | This document specifies such a validation scheme, extending two HTML elements 100 | with an `integrity` attribute that contains a cryptographic hash 101 | of the representation of the resource the author expects to load. For instance, 102 | an author may wish to load some framework from a shared server rather than hosting it 103 | on their own origin. Specifying that the expected SHA-384 hash of 104 | `https://example.com/example-framework.js` 105 | is `Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7` means 106 | that the user agent can verify that the data it loads from that URL matches 107 | that expected hash before executing the JavaScript it contains. This 108 | integrity verification significantly reduces the risk that an attacker can 109 | substitute malicious content. 110 | 111 | This example can be communicated to a user agent by adding the hash to a 112 | `script` element, like so: 113 | 114 |
115 | 116 | <script src="https://example.com/example-framework.js" 117 | integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7" 118 | crossorigin="anonymous"></script> 119 | 120 |
121 | 122 | Scripts, of course, are not the only response type which would benefit 123 | from integrity validation. The scheme specified here also applies to `link` 124 | and future versions of this specification are likely to expand this coverage. 125 | 126 | 127 | subresource-integrity.html 128 | 129 | 130 | ## Goals ## {#goals} 131 | 132 | 1. Compromise of a third-party service should not automatically mean 133 | compromise of every site which includes its scripts. Content authors 134 | will have a mechanism by which they can specify expectations for 135 | content they load, meaning for example that they could load a 136 | specific script, and not any script that happens to have a 137 | particular URL. 138 | 139 | 2. The verification mechanism should have error-reporting functionality which 140 | would inform the author that an invalid response was received. 141 | 142 | ## Use Cases/Examples ## {#examples} 143 | 144 | ### Resource Integrity ### {#resource-integrity} 145 | 146 | * An author wishes to use a content delivery network to improve performance 147 | for globally-distributed users. It is important, however, to ensure that 148 | the CDN's servers deliver only the code the author expects them to 149 | deliver. To mitigate the risk that a CDN compromise (or unexpectedly malicious 150 | behavior) would change that site in unfortunate ways, the following 151 | integrity metadata is added to the `link` element included on the page: 152 | 153 |
154 | 155 | <link rel="stylesheet" href="https://site53.example.net/style.css" 156 | integrity="sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB" 157 | crossorigin="anonymous"> 158 | 159 |
160 | 161 | * An author wants to include JavaScript provided by a third-party 162 | analytics service. To ensure that only the code that has been carefully 163 | reviewed is executed, the author generates integrity metadata for 164 | the script, and adds it to the `script` element: 165 | 166 |
167 | 168 | <script src="https://analytics-r-us.example.com/v1.0/include.js" 169 | integrity="sha384-MBO5IDfYaE6c6Aao94oZrIOiC6CGiSN2n4QUbHNPhzk5Xhm0djZLQqTpL0HzTUxk" 170 | crossorigin="anonymous"></script> 171 | 172 |
173 | 174 | * A user agent wishes to ensure that JavaScript code running in high-privilege HTML 175 | contexts (for example, a browser's New Tab page) aren't manipulated before display. 176 | Integrity metadata mitigates the risk that altered JavaScript will run 177 | in these pages' high-privilege contexts. 178 | 179 | 180 | 181 | # Key Concepts and Terminology # {#terms} 182 | 183 | This section defines several terms used throughout the document. 184 | 185 | The term digest refers to the base64 encoded result of 186 | executing a cryptographic hash function on an arbitrary block of data. 187 | 188 | The terms [=/origin=] and [=same origin=] are defined in HTML. [[!HTML]] 189 | 190 | A base64 encoding is defined in Section 4 of RFC 4648. 192 | [[!RFC4648]] 193 | 194 | The SHA-256, SHA-384, and SHA-512 are part 195 | of the SHA-2 set of cryptographic hash functions defined by the 196 | NIST. [[!SHA2]] 197 | 198 | The valid SRI hash algorithm token set is the [=ordered set=] 199 | « "`sha256`", "`sha384`", "`sha512`" » (corresponding to [=SHA-256=], 200 | [=SHA-384=], and [=SHA-512=] respectively). The ordering of this set is 201 | meaningful, with stronger algorithms appearing later in the set. See 202 | [[#priority]] and [[#get-the-strongest-metadata]] for 203 | additional information. 204 | 205 | A string is a valid SRI hash algorithm token if its 206 | [=ASCII lowercase=] is [=set/contained=] in the 207 | [=valid SRI hash algorithm token set=]. 208 | 209 | ## Grammatical Concepts ## {#grammar-concepts} 210 | 211 | The Augmented Backus-Naur Form (ABNF) notation used in this document is 212 | specified in RFC5234. [[!ABNF]] 213 | 214 | Appendix B.1 of 215 | [[!ABNF]] defines the VCHAR (printing characters) and WSP 216 | (whitespace) rules. 217 | 218 | Content Security Policy defines the `base64-value` and 219 | `hash-algorithm` rules. [[!CSP]] 220 | 221 | 222 | 223 | # Framework # {#framework} 224 | 225 | The integrity verification mechanism specified here boils down to the 226 | process of generating a sufficiently strong cryptographic digest for a 227 | resource, and transmitting that digest to a user agent so that it may be 228 | used to verify the response. 229 | 230 | ## Integrity metadata ## {#integrity-metadata-description} 231 | 232 | To verify the integrity of a response, a user agent requires integrity 233 | metadata as part of the request. This metadata consists of 234 | the following pieces of information: 235 | 236 | * cryptographic hash function ("alg") 237 | * digest ("val") 238 | * options ("opt") 239 | 240 | The hash function and digest MUST be provided in order to validate a 241 | response's integrity. 242 | 243 | Note: At the moment, no options are defined. However, future versions of 244 | the spec may define options, such as MIME types [[!MIME-TYPES]]. 245 | 246 | This metadata MUST be encoded in the same format as the `hash-source` (without 247 | the single quotes) in section 4.2 of the Content 249 | Security Policy Level 2 specification. 250 | 251 | For example, given a script resource containing only the string `alert('Hello, 252 | world.');`, an author might choose SHA-384 as a hash function. 253 | `H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO` is the base64 encoded digest that results. This can be encoded 255 | as follows: 256 | 257 |
258 | 259 | sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO 260 | 261 |
262 | 263 |
264 | Digests may be generated using any number of utilities. OpenSSL, for example, is quite commonly 266 | available. The example in this section is the result of the following command 267 | line: 268 | 269 | 270 | echo -n "alert('Hello, world.');" | openssl dgst -sha384 -binary | openssl base64 -A 271 | 272 |
273 | 274 | ## Cryptographic hash functions ## {#hash-functions} 275 | 276 | Conformant user agents MUST support the SHA-256, SHA-384, 277 | and SHA-512 cryptographic hash functions for use as part of a 278 | request's integrity metadata and MAY support additional hash functions 279 | defined in future iterations of this document. 280 | 281 | NOTE: The algorithms supported in this document are (currently!) believed to be 282 | resistent to second-preimage and collision attacks. Future additions/removals 283 | from the set of supported algorithms would be well-advised to apply similar 284 | standard. See [[#hash-collision-attacks]]. 285 | 286 | ### Agility ### {#agility} 287 | 288 | Multiple sets of integrity metadata may be associated with a single 289 | resource in order to provide agility in the face of future cryptographic discoveries. 290 | For example, the resource described in the previous section may be described 291 | by either of the following hash expressions: 292 | 293 |
294 | 295 | sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO 296 | sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw== 297 | 298 |
299 | 300 | Authors may choose to specify both, for example: 301 | 302 |
303 | 304 | <script src="hello_world.js" 305 | integrity="sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO 306 | sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw==" 307 | crossorigin="anonymous"></script> 308 | 309 |
310 | 311 | In this case, the user agent will choose the strongest hash function in the 312 | list, and use that metadata to validate the response (as described below in 313 | the [[#parse-metadata-section]] and [[#get-the-strongest-metadata]] algorithms). 314 | 315 | When a hash function is determined to be insecure, user agents SHOULD deprecate 316 | and eventually remove support for integrity validation using the insecure hash 317 | function. User agents MAY check the validity of responses using a digest based on 318 | a deprecated function. 319 | 320 | To allow authors to switch to stronger hash functions without being held back by older 321 | user agents, validation using unsupported hash functions acts like no integrity value 322 | was provided (see the [[#does-response-match-metadatalist]] algorithm below). 323 | Authors are encouraged to use strong hash functions, and to begin migrating to 324 | stronger hash functions as they become available. 325 | 326 | ### Priority ### {#priority} 327 | 328 | The prioritization of hash algorithms is specified via the ordering of their 329 | respective tokens in the [=valid SRI hash algorithm token set=]. Algorithms 330 | appearing earlier in that set are weaker than algorithms appearing later in 331 | that set. 332 | 333 | As currently specified, [=SHA-256=] is weaker than [=SHA-384=], which is in 334 | turn weaker than [=SHA-512=]. No other hashing algorithms are currently 335 | supported by this specification. 336 | 337 | 338 | ## Response verification algorithms ## {#verification-algorithms} 339 | 340 | ### Apply |algorithm| to |bytes| ### {#apply-algorithm-to-response} 341 | 342 | 1. Let |result| be the result of applying |algorithm| to |bytes|. 343 | 2. Return the result of base64 encoding |result|. 344 | 345 | ### Parse metadata ### {#parse-metadata-section} 346 | 347 | When asked to parse metadata given a string |metadata|, run the following steps: 348 | 349 | Note: the algorithm returns a set of hash expressions whose hash functions are understood 350 | by the user agent. 351 | 352 | 1. Let |result| be the empty set. 353 | 2. For each |item| returned by splitting 354 | |metadata| on spaces: 355 | 1. Let |expression-and-options| be the result of 356 | splitting |item| on U+003F (?). 357 | 2. Let |algorithm-expression| be |expression-and-options|[0]. 358 | 3. Let |base64-value| be the empty string. 359 | 4. Let |algorithm-and-value| be the result of 360 | splitting |algorithm-expression| on U+002D (-). 361 | 5. Let |algorithm| be |algorithm-and-value|[0]. 362 | 6. If |algorithm-and-value|[1] exists, set 363 | |base64-value| to |algorithm-and-value|[1]. 364 | 7. If |algorithm| is not a [=valid SRI hash algorithm token=], then 365 | [=iteration/continue=]. 366 | 8. Let |metadata| be the ordered map «["alg" → |algorithm|, 367 | "val" → |base64-value|]». 368 | 369 | Note: Since no `options` are defined (see the 370 | [[#integrity-metadata-description]]), a corresponding entry is not set 371 | in |metadata|. If `options` are defined in a future version, 372 | |expression-and-options|[1] can be utilized as `options`. 373 | 374 | 9. Append |metadata| to |result|. 375 | 3. Return |result|. 376 | 377 | ### Get the strongest metadata from |set| ### {#get-the-strongest-metadata} 378 | 379 | 1. Let |result| be the empty set and |strongest| be the empty 380 | string. 381 | 2. For each |item| in |set|: 382 | 1. Assert: |item|["`alg`"] is a [=valid SRI hash algorithm token=]. 383 | 2. If |result| is the empty set, then: 384 | 1. [=set/Append=] |item| to |result|. 385 | 2. Set |strongest| to |item|. 386 | 3. [=iteration/Continue=]. 387 | 3. Let |currentAlgorithm| be |strongest|["`alg`"], and |currentAlgorithmIndex| 388 | be the index of |currentAlgorithm| in the [=valid SRI hash algorithm token set=]. 389 | 4. Let |newAlgorithm| be the |item|["`alg`"], and |newAlgorithmIndex| 390 | be the index of |newAlgorithm| in the [=valid SRI hash algorithm token set=]. 391 | 5. If |newAlgorithmIndex| is less than |currentAlgorithmIndex|, [=iteration/continue=]. 392 | 6. Otherwise, if |newAlgorithmIndex| is greater than |currentAlgorithmIndex|: 393 | 1. Set |strongest| to |item|. 394 | 2. Set |result| to « |item| ». 395 | 7. Otherwise, |newAlgorithmIndex| and |currentAlgorithmIndex| are the 396 | same value. [=set/Append=] |item| to |result|. 397 | 3. Return |result|. 398 | 399 |

Do |bytes| match |metadataList|?

400 | 401 | 1. Let |parsedMetadata| be the result of 402 | parsing |metadataList|. 403 | 2. If |parsedMetadata| [=set/is empty=] set, return `true`. 404 | 3. Let |metadata| be the result of 405 | getting the strongest metadata from |parsedMetadata|. 406 | 4. For each |item| in |metadata|: 407 | 1. Let |algorithm| be the |item|["alg"]. 408 | 2. Let |expectedValue| be the |item|["val"]. 409 | 3. Let |actualValue| be the result of applying |algorithm| to |bytes| 411 | . 412 | 4. If |actualValue| is a case-sensitive match for 413 | |expectedValue|, return `true`. 414 | 5. Return `false`. 415 | 416 | This algorithm allows the user agent to accept multiple, valid strong hash 417 | functions. For example, a developer might write a `script` element such as: 418 | 419 |
420 | 421 | <script src="https://example.com/example-framework.js" 422 | integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7 423 | sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB" 424 | crossorigin="anonymous"></script> 425 | 426 |
427 | 428 | which would allow the user agent to accept two different content payloads, one 429 | of which matches the first SHA-384 hash value and the other matches the second 430 | SHA-384 hash value. 431 | 432 | Note: User agents may allow users to modify the result of this algorithm via 433 | user preferences, bookmarklets, third-party additions to the user agent, and 434 | other such mechanisms. For example, redirects generated by an extension like HTTPS Everywhere could load and 436 | execute correctly, even if the HTTPS version of a resource differs from the HTTP 437 | version. 438 | 439 | Note: Subresource Integrity requires CORS and it is a logical error 440 | to attempt to use it without CORS. User agents are encouraged to report a 441 | warning message to the developer console to explain this failure. [[!Fetch]] 442 | 443 | ## Verification of HTML document subresources ## {#verification-of-html-document-subresources} 444 | 445 | A variety of HTML elements result in requests for resources that are to be 446 | embedded into the document, or executed in its context. To support integrity 447 | metadata for some of these elements, a new `integrity` attribute is added to 448 | the list of content attributes for the `link` and `script` elements. [[!HTML]] 449 | 450 | Note: A future revision of this specification is likely to include integrity support 451 | for all possible subresources, i.e., `a`, `audio`, `embed`, `iframe`, `img`, 452 | `link`, `object`, `script`, `source`, `track`, and `video` elements. 453 | 454 | ## The `integrity` attribute ## {#the-integrity-attribute} 455 | 456 | The `integrity` attribute represents integrity metadata for an element. 457 | The value of the attribute MUST be either the empty string, or at least one 458 | valid metadata as described by the following ABNF grammar: 459 | 460 |
461 |       integrity-metadata = *WSP hash-with-options *(1*WSP hash-with-options ) *WSP / *WSP
462 |       hash-with-options  = hash-expression *("?" option-expression)
463 |       option-expression  = *VCHAR
464 |       hash-expression    = hash-algorithm "-" base64-value
465 |   
466 | 467 | `option-expression`s are associated on a per `hash-expression` basis and are 468 | applied only to the `hash-expression` that immediately precedes it. 469 | 470 | In order for user agents to remain fully forwards compatible with future 471 | options, the user agent MUST ignore all unrecognized `option-expression`s. 472 | 473 | Note: Note that while the `option-expression` has been reserved in the syntax, 474 | no options have been defined. It is likely that a future version of the spec 475 | will define a more specific syntax for options, so it is defined here as broadly 476 | as possible. 477 | 478 | ## The `integrity` link processing option ## {#the-integrity-link-processing-option} 479 | 480 | Integrity metadata can also be specified for [:link:] HTTP response headers 481 | as an `integrity` link parameter which MUST be specified using the same 482 | `integrity-metadata` grammar that applies to `integrity` attributes 483 | on elements. For example: 484 | 485 | ```http 486 | Link: ; rel=preload; as=style; crossorigin="anonymous"; integrity="sha256-[digest goes here]" 487 | ``` 488 | 489 | 490 | ## Handling integrity violations ## {#handling-integrity-violations} 491 | 492 | The user agent will refuse to render or execute responses that fail an integrity 493 | check, instead returning a network error as defined in Fetch [[!Fetch]]. 494 | 495 | Note: On a failed integrity check, an `error` event is fired. Developers 496 | wishing to provide a canonical fallback resource (e.g., a resource not served 497 | from a CDN, perhaps from a secondary, trusted, but slower source) can catch this 498 | `error` event and provide an appropriate handler to replace the 499 | failed resource with a different one. 500 | 501 | 502 | 503 | ## Integrity-Policy ## {#integrity-policy-section} 504 | The `Integrity-Policy` and `Integrity-Policy-Report-Only` HTTP headers enable a document to 505 | enforce a policy regarding the integrity metadata requirements on all the subresources it 506 | loads of certain destinations. 507 | 508 | The headers' value is a Dictionary [[RFC9651]], with every member-value being an 509 | inner list of tokens. 510 | 511 | A source is a string. The only possible value for it is "`inline`". 512 | 513 | A destination is a string. The only possible value for it is "`script`". 514 | 515 | An integrity policy, is a struct that contains the following: 516 | 517 | * sources, a list of sources, initially empty. 518 | * blocked destinations, a list of destinations, initially empty. 519 | * endpoints, a list of strings, initially empty. 520 | 521 | When processing an integrity policy, with a header list |headers| 522 | and a header name |headerName|, do the following: 523 | 524 | 1. Let |integrityPolicy| be a new integrity policy. 525 | 1. Let |dictionary| be the result of getting a structured field value from |headers| 526 | given |headerName| and "`dictionary`". 527 | 1. If |dictionary|["`sources`"] does not exist or if its value 528 | contains "`inline`", append "`inline`" to 529 | |integrityPolicy|'s sources. 530 | 1. If |dictionary|["`blocked-destinations`"] exists: 531 | 1. If its value contains "`script`", 532 | append "`script`" to |integrityPolicy|'s blocked destinations. 533 | 1. If |dictionary|["`endpoints`"] exists: 534 | 1. Set |integrityPolicy|'s endpoints to |dictionary|['endpoints']. 535 | 1. Return |integrityPolicy|. 536 | 537 | ### Parse Integrity-Policy headers ### {#parse-integrity-policy-headers-section} 538 | To parse Integrity-Policy headers, given a Response |response| 539 | and a policy container |container|, do the following: 540 | 541 | 1. Let |headers| be |response|'s header list. 542 | 1. If |headers| contains ``integrity-policy``, 543 | set |container|'s integrity policy be the result of running 544 | processing an integrity policy with the corresponding header value. 545 | 1. If |headers| contains ``integrity-policy-report-only``, 546 | set |container|'s report only integrity policy be the result of running 547 | processing an integrity policy with the corresponding header value. 548 | 549 | ### Should request be blocked by Integrity Policy ### {#should-request-be-blocked-by-integrity-policy-section} 550 | To determine should request be blocked by integrity policy, given a request |request|, 551 | do the following: 552 | 553 | 1. Let |policyContainer| be |request|'s policy container. 554 | 1. Let |parsedMetadata| be the result of calling parse metadata with 555 | |request|'s integrity metadata. 556 | 1. If |parsedMetadata| is not the empty set and 557 | |request|'s mode is either "`cors`" or "`same-origin`", 558 | return "Allowed". 559 | 1. If |request|'s url is local, 560 | return "Allowed". 561 | 1. Let |policy| be |policyContainer|'s integrity policy. 562 | 1. Let |reportPolicy| be |policyContainer|'s report only integrity policy. 563 | 1. If both |policy| and |reportPolicy| are empty integrity policys, return "Allowed". 564 | 1. Let |global| be |request|'s client's global object. 565 | 1. If |global| is not a {{Window}} nor a {{WorkerGlobalScope}}, return "`Allowed`". 566 | 1. Let |block| be a boolean, initially false. 567 | 1. Let |reportBlock| be a boolean, initially false. 568 | 1. If |policy|'s sources contains "`inline`" 569 | and |policy|'s blocked destinations contains 570 | |request|'s destination, 571 | set |block| to true. 572 | 1. If |reportPolicy|'s sources contains "`inline`" 573 | and |reportPolicy|'s blocked destinations contains 574 | |request|'s destination, 575 | set |reportBlock| to true. 576 | 1. If |block| is true or |reportBlock| is true, then report violation 577 | with |request|, |block|, |reportBlock|, |policy| and |reportPolicy|. 578 | 1. If |block| is true, then return "`Blocked`"; otherwise "`Allowed`". 579 | 580 | ### Report violations ### {#report-violations} 581 | 582 |
583 |     [Exposed=Window]
584 |     interface IntegrityPolicyViolationReportBody : ReportBody {
585 |       [Default] object toJSON();
586 |       readonly attribute USVString documentURL;
587 |       readonly attribute USVString blockedURL;
588 |       readonly attribute USVString destination;
589 |       readonly attribute boolean   reportOnly;
590 |     };
591 |   
592 | 593 | To report violation given a Request |request|, a boolean |block|, 594 | a boolean |reportBlock|, an integrity policy |policy|, 595 | and an integrity policy |reportPolicy|, do the following: 596 | 597 | 1. Assert: |request|'s client is not null. 598 | 1. Let |settingsObject| be |request|'s client. 599 | 1. Let |global| be |settingsObject|'s global object. 600 | 1. Assert: |global| is a {{Window}} or a {{WorkerGlobalScope}}. 601 | 1. Let |url| be null. 602 | 1. If |global| is a {{Window}}, set |url| to |global|'s associated Document's {{Document/URL}}. 603 | 1. If |global| is a {{WorkerGlobalScope}}, set |url| to |global|'s URL. 604 | 1. Assert: |url| is a URL. 605 | 1. Let |documentURL| be the result of strip URL for use in reports on |url|. 606 | 1. Let |blockedURL| be the result of strip URL for use in reports on |request|'s URL. 607 | 1. If |block| is true, for each |endpoint| in |policy|'s endpoints: 608 | 1. Let |body| be a new {{IntegrityPolicyViolationReportBody}}, initialized as follows: 609 | : {{IntegrityPolicyViolationReportBody/documentURL}} 610 | :: |documentURL| 611 | : {{IntegrityPolicyViolationReportBody/blockedURL}} 612 | :: |blockedURL| 613 | : {{IntegrityPolicyViolationReportBody/destination}} 614 | :: |request|'s destination 615 | : {{IntegrityPolicyViolationReportBody/reportOnly}} 616 | :: false 617 | 2. Generate and queue a report with the following arguments: 618 | : context 619 | :: |settingsObject| 620 | : type 621 | :: "`integrity-policy-violation`" 622 | : destination 623 | :: |endpoint| 624 | : data 625 | :: |body| 626 | 1. If |reportBlock| is true, for each |endpoint| in |reportPolicy|'s endpoints: 627 | 1. Let |reportBody| be a new {{IntegrityPolicyViolationReportBody}}, initialized as follows: 628 | : {{IntegrityPolicyViolationReportBody/documentURL}} 629 | :: |documentURL| 630 | : {{IntegrityPolicyViolationReportBody/blockedURL}} 631 | :: |blockedURL| 632 | : {{IntegrityPolicyViolationReportBody/destination}} 633 | :: |request|'s destination 634 | : {{IntegrityPolicyViolationReportBody/reportOnly}} 635 | :: true 636 | 2. Generate and queue a report with the following arguments: 637 | : context 638 | :: |settingsObject| 639 | : type 640 | :: "`integrity-policy-violation`" 641 | : destination 642 | :: |endpoint| 643 | : data 644 | :: |reportBody| 645 | 646 | ### Integration ### {#integration} 647 | 648 | Expand step 7 of main fetch to call should request be blocked by integrity policy 649 | and return a network error if it returns "`Blocked`". 650 | 651 | # Proxies # {#proxies} 652 | 653 | Optimizing proxies and other intermediate servers which modify the 654 | responses MUST ensure that the digest associated 655 | with those responses stays in sync with the new content. One option 656 | is to ensure that the integrity metadata associated with 657 | resources is updated. Another 658 | would be simply to deliver only the canonical version of resources 659 | for which a page author has requested integrity verification. 660 | 661 | To help inform intermediate servers, those serving the resources SHOULD 662 | send along with the resource a `Cache-Control` header 663 | with a value of `no-transform`. 664 | 665 | 666 | 667 | # Security and Privacy Considerations # {#security-considerations} 668 | 669 | This section is not normative. 670 | 671 | ## Non-secure contexts remain non-secure ## {#non-secure-contexts} 672 | 673 | Integrity metadata delivered by a context that is not a Secure 674 | Context such as an HTTP page, only protects an origin against a compromise 675 | of the server where an external resources is hosted. Network attackers can alter 676 | the digest in-flight (or remove it entirely, or do absolutely anything else to 677 | the document), just as they could alter the response the hash is meant to 678 | validate. Thus, it is recommended that authors deliver integrity metadata only 679 | to a Secure Context. See also Securing the Web. 681 | 682 | ## Hash collision attacks ## {#hash-collision-attacks} 683 | 684 | Digests are only as strong as the hash function used to generate them. It is 685 | recommended that user agents refuse to support known-weak hashing functions and 686 | limit supported algorithms to those known to be collision resistant. Examples of 687 | hashing functions that are not recommended include MD5 and SHA-1. At the time of 688 | writing, SHA-384 is a good baseline. 689 | 690 | Moreover, it is recommended that user agents re-evaluate their supported hash 691 | functions on a regular basis and deprecate support for those functions shown to 692 | be insecure. Over time, hash functions may be shown to be much weaker than 693 | expected and, in some cases, broken, so it is important that user agents stay 694 | aware of these developments. 695 | 696 | ## Cross-origin data leakage ## {#cross-origin-data-leakage} 697 | 698 | This specification requires integrity-protected cross-origin requests to use the 699 | CORS protocol to ensure that the resource's content is explicitly shared 700 | with the requestor. If that requirement were omitted, 701 | attackers could violate the same-origin policy 703 | and determine whether a cross-origin resource has certain content. 704 | 705 | Attackers would attempt to load the resource with a known digest, and 706 | watch for load failures. If the load fails, the attacker could surmise 707 | that the response didn't match the hash and thereby gain some insight into 708 | its contents. This might reveal, for example, whether or not a user is 709 | logged into a particular service. 710 | 711 | Moreover, attackers could brute-force specific values in an otherwise 712 | static resource. Consider a JSON response that looks like this: 713 | 714 |
715 | 716 | {"status": "authenticated", "username": "admin"} 717 | 718 |
719 | 720 | An attacker could precompute hashes for the response with a variety of 721 | common usernames, and specify those hashes while repeatedly attempting 722 | to load the document. A successful load would confirm that the attacker 723 | has correctly guessed the username. 724 | 725 | 726 | 727 | # Acknowledgements # {#acknowledgements} 728 | 729 | Much of the content here is inspired heavily by Gervase Markham's Link Fingerprints 731 | concept as well as WHATWG's Link Hashes. 733 | 734 | A special thanks to Mike West for his invaluable contributions to the initial 735 | version of this spec. Thanks to Brad Hill, Anne van Kesteren, Jonathan 736 | Kingston, Fatih Kilic, Mark Nottingham, Sergey Shekyan, Dan Veditz, Eduardo Vela, 737 | Tanvi Vyas, Yoav Weiss, and Michal Zalewski for providing invaluable feedback. 738 | -------------------------------------------------------------------------------- /index.kramdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Subresource Integrity 6 | 7 | 129 | 130 | 131 |
132 |

This specification defines a mechanism by which user agents may verify that 133 | a fetched resource has been delivered without unexpected manipulation.

134 |
135 | 136 |
137 |

A list of changes to this document may be found at 138 | https://github.com/w3c/webappsec-subresource-integrity.

139 |
140 | 141 |
142 |

Introduction

143 | 144 |

Sites and applications on the web are rarely composed of resources from 145 | only a single origin. For example, authors pull scripts and styles from a 146 | wide variety of services and content delivery networks, and must trust 147 | that the delivered representation is, in fact, what they expected to 148 | load. If an attacker can trick a user into downloading content from 149 | a hostile server (via DNS poisoning, or other such means), the author has 150 | no recourse. Likewise, an attacker who can replace the file on the Content 151 | Delivery Network (CDN) server has the ability to inject arbitrary content.

152 | 153 |

Delivering resources over a secure channel mitigates some of this risk: with 154 | TLS, HSTS, and pinned public keys, a user agent can be fairly certain 155 | that it is indeed speaking with the server it believes it’s talking to. These 156 | mechanisms, however, authenticate only the server, not the content. An 157 | attacker (or administrator) with access to the server can manipulate content with 158 | impunity. Ideally, authors would not only be able to pin the keys of a 159 | server, but also pin the content, ensuring that an exact representation of 160 | a resource, and only that representation, loads and executes.

161 | 162 |

This document specifies such a validation scheme, extending two HTML elements 163 | with an integrity attribute that contains a cryptographic hash 164 | of the representation of the resource the author expects to load. For instance, 165 | an author may wish to load some framework from a shared server rather than hosting it 166 | on their own origin. Specifying that the expected SHA-384 hash of 167 | https://example.com/example-framework.js 168 | is Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7 means 169 | that the user agent can verify that the data it loads from that URL matches 170 | that expected hash before executing the JavaScript it contains. This 171 | integrity verification significantly reduces the risk that an attacker can 172 | substitute malicious content.

173 | 174 |

This example can be communicated to a user agent by adding the hash to a 175 | script element, like so:

176 | 177 |
<script src="https://example.com/example-framework.js"
178 |         integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7"
179 |         crossorigin="anonymous"></script>
180 | 
181 | 182 |

Scripts, of course, are not the only response type which would benefit 183 | from integrity validation. The scheme specified here also applies to link 184 | and future versions of this specification are likely to expand this coverage.

185 | 186 |
187 |

Goals

188 | 189 |
    190 |
  1. 191 |

    Compromise of a third-party service should not automatically mean 192 | compromise of every site which includes its scripts. Content authors 193 | will have a mechanism by which they can specify expectations for 194 | content they load, meaning for example that they could load a 195 | specific script, and not any script that happens to have a 196 | particular URL.

    197 |
  2. 198 |
  3. 199 |

    The verification mechanism should have error-reporting functionality which 200 | would inform the author that an invalid response was received.

    201 |
  4. 202 |
203 | 204 |
205 | 206 | 207 |
208 |

Use Cases/Examples

209 | 210 |
211 |

Resource Integrity

212 | 213 |
    214 |
  • 215 |

    An author wishes to use a content delivery network to improve performance 216 | for globally-distributed users. It is important, however, to ensure that 217 | the CDN’s servers deliver only the code the author expects them to 218 | deliver. To mitigate the risk that a CDN compromise (or unexpectedly malicious 219 | behavior) would change that site in unfortunate ways, the following 220 | integrity metadata is added to the link element included on the page:

    221 | 222 |
    <link rel="stylesheet" href="https://site53.example.net/style.css"
    223 |       integrity="sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB"
    224 |       crossorigin="anonymous">
    225 | 
    226 |
  • 227 |
  • 228 |

    An author wants to include JavaScript provided by a third-party 229 | analytics service. To ensure that only the code that has been carefully 230 | reviewed is executed, the author generates integrity metadata for 231 | the script, and adds it to the script element:

    232 | 233 |
    <script src="https://analytics-r-us.example.com/v1.0/include.js"
    234 |         integrity="sha384-MBO5IDfYaE6c6Aao94oZrIOiC6CGiSN2n4QUbHNPhzk5Xhm0djZLQqTpL0HzTUxk"
    235 |         crossorigin="anonymous"></script>
    236 | 
    237 |
  • 238 |
  • 239 |

    A user agent wishes to ensure that JavaScript code running in high-privilege HTML 240 | contexts (for example, a browser’s New Tab page) aren’t manipulated before display. 241 | Integrity metadata mitigates the risk that altered JavaScript will run 242 | in these pages’ high-privilege contexts.

    243 |
  • 244 |
245 |
246 | 247 |
248 | 249 |
250 | 251 | 252 |
253 |

Conformance requirements phrased as algorithms or specific steps can be 254 | implemented in any manner, so long as the end result is equivalent. In 255 | particular, the algorithms defined in this specification are intended to 256 | be easy to understand and are not intended to be performant. Implementers 257 | are encouraged to optimize.

258 | 259 |
260 |

Key Concepts and Terminology

261 | 262 |

This section defines several terms used throughout the document.

263 | 264 |

The term digest refers to the base64-encoded result of 265 | executing a cryptographic hash function on an arbitrary block of data.

266 | 267 |

The term origin is defined in the Origin specification. 268 | [[!RFC6454]]

269 | 270 |

The representation data and content encoding of a resource 271 | are defined by RFC7231, section 3. [[!RFC7231]]

272 | 273 |

A base64 encoding is defined in RFC 4648, section 4. 274 | [[!RFC4648]]

275 | 276 |

The SHA-256, SHA-384, and SHA-512 are part 277 | of the SHA-2 set of cryptographic hash functions defined by the 278 | NIST in “FIPS PUB 180-4: Secure Hash Standard (SHS)”.

279 | 280 |
281 | 282 |
283 |

Grammatical Concepts

284 | 285 |

The Augmented Backus-Naur Form (ABNF) notation used in this document is 286 | specified in RFC5234. [[!ABNF]]

287 | 288 |

Appendix B.1 of [[!ABNF]] defines VCHAR 289 | (printing characters).

290 | 291 |

WSP (white space) characters are defined in Section 292 | 2.4.1 Common parser idioms of the HTML 5 specification as 293 | White_Space characters.

294 | 295 |
296 | 297 |
298 | 299 |
300 |

Framework

301 | 302 |

The integrity verification mechanism specified here boils down to the 303 | process of generating a sufficiently strong cryptographic digest for a 304 | resource, and transmitting that digest to a user agent so that it may be 305 | used to verify the response.

306 | 307 |
308 |

Integrity metadata

309 | 310 |

To verify the integrity of a response, a user agent requires integrity 311 | metadata as part of the request. This metadata consists of the following 312 | pieces of information:

313 | 314 |
    315 |
  • cryptographic hash function (“alg”)
  • 316 |
  • digest (“val”)
  • 317 |
  • options (“opt”)
  • 318 |
319 | 320 |

The hash function and digest MUST be provided in order to validate a 321 | response’s integrity.

322 | 323 |

At the moment, no options are defined. However, future versions of 324 | the spec may define options, such as MIME types [[!MIMETYPE]].

325 | 326 |

This metadata MUST be encoded in the same format as the hash-source (without the single quotes) 327 | in section 4.2 of the Content Security Policy Level 2 specification.

328 | 329 |

For example, given a script resource containing only the string alert('Hello, world.');, 330 | an author might choose SHA-384 as a hash function. 331 | H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO is the base64-encoded 332 | digest that results. This can be encoded as follows:

333 | 334 |
sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO
335 | 
336 | 337 |
338 |

Digests may be generated using any number of utilities. OpenSSL, for 339 | example, is quite commonly available. The example in this section is the 340 | result of the following command line:

341 | 342 |
echo -n "alert('Hello, world.');" | openssl dgst -sha384 -binary | openssl base64 -A
343 | 
344 |
345 | 346 |
347 | 348 | 349 |
350 |

Cryptographic hash functions

351 | 352 |

Conformant user agents MUST support the SHA-256, SHA-384 353 | and SHA-512 cryptographic hash functions for use as part of a 354 | request’s integrity metadata and MAY support additional hash functions.

355 | 356 |

User agents SHOULD refuse to support known-weak hashing functions like MD5 or 357 | SHA-1 and SHOULD restrict supported hashing functions to those known to be 358 | collision-resistant. Additionally, user agents SHOULD re-evaluate their 359 | supported hash functions on a regular basis and deprecate support for those 360 | functions that have become insecure. See Hash collision attacks.

361 | 362 |
363 |

Agility

364 | 365 |

Multiple sets of integrity metadata may be associated with a single 366 | resource in order to provide agility in the face of future cryptographic discoveries. 367 | For example, the resource described in the previous section may be described 368 | by either of the following hash expressions:

369 | 370 |
sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd
371 | sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw==
372 | 
373 | 374 |

Authors may choose to specify both, for example:

375 | 376 |
<script src="hello_world.js"
377 |    integrity="sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd
378 |               sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw=="
379 |    crossorigin="anonymous"></script>
380 | 
381 | 382 |

In this case, the user agent will choose the strongest hash function in the 383 | list, and use that metadata to validate the response (as described below in 384 | the “parse metadata” and “get the strongest metadata from 385 | set” algorithms).

386 | 387 |

When a hash function is determined to be insecure, user agents SHOULD deprecate 388 | and eventually remove support for integrity validation using the insecure hash 389 | function. User agents MAY check the validity of responses using a digest based on 390 | a deprecated function.

391 | 392 |

To allow authors to switch to stronger hash functions without being held back by older 393 | user agents, validation using unsupported hash functions acts like no integrity value 394 | was provided (see the “Does response match metadataList” algorithm below). 395 | Authors are encouraged to use strong hash functions, and to begin migrating to 396 | stronger hash functions as they become available.

397 |
398 | 399 | 400 |
401 |

Priority

402 | 403 |

User agents must provide a mechanism for determining the relative priority of two 404 | hash functions and return the empty string if the priority is equal. That is, if 405 | a user agent implemented a function like getPrioritizedHashFunction(a, 406 | b) it would return the hash function the user agent considers the most 407 | collision-resistant. For example, getPrioritizedHashFunction('sha256', 408 | 'sha512') would return 'sha512' and getPrioritizedHashFunction('sha256', 409 | 'sha256') would return the empty string.

410 | 411 |

The getPrioritizedHashFunction is an internal 412 | implementation detail. It is not an API that implementors 413 | provide to web applications. It is used in this document 414 | only to simplify the algorithm description.

415 | 416 |
417 | 418 | 419 |
420 | 421 | 422 |
423 |

Response verification algorithms

424 | 425 |
426 |

Apply algorithm to response

427 | 428 |
    429 |
  1. Let result be the result of applying algorithm 430 | to the representation data without any content-codings 431 | applied, except when the user agent intends to consume the content with 432 | content-encodings applied. In the latter case, let result be 433 | the result of applying algorithm to the representation data.
  2. 434 |
  3. Let encodedResult be result of base64-encoding 435 | result.
  4. 436 |
  5. Return encodedResult.
  6. 437 |
438 |
439 | 440 |
441 |

Is response eligible for integrity validation

442 | 443 |

In order to mitigate an attacker’s ability to read data cross-origin by 444 | brute-forcing values via integrity checks, responses are only eligible for such 445 | checks if they are same-origin or are the result of explicit access granted to 446 | the loading origin via Cross Origin Resource Sharing [[!CORS]].

447 | 448 |

As noted in RFC6454, section 4, some user agents use 449 | globally unique identifiers for each file URI. This means that 450 | resources accessed over a file scheme URL are unlikely to be 451 | eligible for integrity checks.

452 | 453 |

Being in a Secure Context (e.g., a document delivered over HTTPS) is not 454 | necessary for the use of integrity validation. Because resource integrity is 455 | only an application level security tool, and it does not change the security 456 | state of the user agent, a Secure Context is unnecessary. However, if integrity 457 | is used in something other than a Secure Context (e.g., a document delivered 458 | over HTTP), authors should be aware that the integrity provides no security 459 | guarantees at all. For this reason, authors should only deliver integrity 460 | metadata in a Secure Context. See Non-secure contexts remain non-secure for 461 | more discussion.

462 | 463 |

The following algorithm details these restrictions:

464 | 465 |
    466 |
  1. Let response be the response that results from 467 | fetching the resource.
  2. 468 |
  3. If the response type is basic, 469 | cors or default, return true.
  4. 470 |
  5. Return false.
  6. 471 |
472 | 473 |
474 |

The response types are defined by the Fetch 475 | specification [[!FETCH]] and refer to the following:

476 | 477 |
    478 |
  • basic is a same-origin response, and thus the requestor has full access 479 | to read the body.
  • 480 |
  • cors is a valid response to a cross-origin, CORS-enabled request, and thus 481 | again the requestor has full access to read the body.
  • 482 |
  • default is a valid response that is generated by a Service Worker as a 483 | response to the request, so its body, too, is fully readable by the requestor.
  • 484 |
485 |
486 | 487 |
488 | 489 |
490 |

Parse metadata.

491 | 492 |

This algorithm accepts a string, and returns either no metadata, or a set of 493 | valid hash expressions whose hash functions are understood by 494 | the user agent.

495 | 496 |
    497 |
  1. Let result be the empty set.
  2. 498 |
  3. Let empty be equal to true.
  4. 499 |
  5. For each token returned by splitting metadata on 500 | spaces: 501 |
      502 |
    1. Set empty to false.
    2. 503 |
    3. If token is not a valid metadata, skip the remaining 504 | steps, and proceed to the next token.
    4. 505 |
    5. Parse token per the grammar in integrity metadata.
    6. 506 |
    7. Let algorithm be the alg component of 507 | token.
    8. 508 |
    9. If algorithm is a hash function recognized by the user 509 | agent, add the parsed token to result.
    10. 510 |
    511 |
  6. 512 |
  7. Return no metadata if empty is true, otherwise return 513 | result.
  8. 514 |
515 | 516 |
517 | 518 |
519 |

Get the strongest metadata from set.

520 | 521 |
    522 |
  1. Let result be the empty set and strongest be the empty 523 | string.
  2. 524 |
  3. For each item in set: 525 |
      526 |
    1. If result is the empty set, add item to 527 | result and set strongest to item, skip 528 | to the next item.
    2. 529 |
    3. Let currentAlgorithm be the alg component of 530 | strongest.
    4. 531 |
    5. Let newAlgorithm be the alg component of 532 | item.
    6. 533 |
    7. If the result of getPrioritizedHashFunction(currentAlgorithm, newAlgorithm) 534 | is the empty string, add item to result. If the 535 | result is newAlgorithm, set strongest to 536 | item, set result to the empty set, and add 537 | item to result.
    8. 538 |
    539 |
  4. 540 |
  5. Return result.
  6. 541 |
542 | 543 |
544 | 545 |
546 |

Does response match metadataList?

547 | 548 |
    549 |
  1. Let parsedMetadata be the result of 550 | parsing metadataList.
  2. 551 |
  3. If parsedMetadata is no metadata, return true.
  4. 552 |
  5. If response is not eligible for integrity 553 | validation, return false.
  6. 554 |
  7. If parsedMetadata is the empty set, return true.
  8. 555 |
  9. Let metadata be the result of getting the strongest 556 | metadata from parsedMetadata.
  10. 557 |
  11. For each item in metadata: 558 |
      559 |
    1. Let algorithm be the alg component of 560 | item.
    2. 561 |
    3. Let expectedValue be the val component of 562 | item.
    4. 563 |
    5. Let actualValue be the result of applying 564 | algorithm to response.
    6. 565 |
    7. If actualValue is a case-sensitive match for 566 | expectedValue, return true.
    8. 567 |
    568 |
  12. 569 |
  13. Return false.
  14. 570 |
571 | 572 |

This algorithm allows the user agent to accept multiple, valid strong hash 573 | functions. For example, a developer might write a script element such as:

574 | 575 |
<script src="https://example.com/example-framework.js"
576 |         integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7
577 |                    sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB"
578 |         crossorigin="anonymous"></script>
579 | 
580 | 581 |

which would allow the user agent to accept two different content payloads, one 582 | of which matches the first SHA384 hash value and the other matches the second 583 | SHA384 hash value.

584 | 585 |

User agents may allow users to modify the result of this algorithm via user 586 | preferences, bookmarklets, third-party additions to the user agent, and other 587 | such mechanisms. For example, redirects generated by an extension like 588 | HTTPS Everywhere could load and execute 589 | correctly, even if the HTTPS version of a resource differs from the HTTP 590 | version.

591 | 592 |

This algorithm returns false if the response is not eligible for integrity 593 | validation since Subresource Integrity requires CORS, and it is a logical error 594 | to attempt to use it without CORS. Additionally, user agents SHOULD report a 595 | warning message to the developer console to explain this failure.

596 |
597 | 598 |
599 | 600 | 601 |
602 |

Verification of HTML document subresources

603 | 604 |

A variety of HTML elements result in requests for resources that are to be 605 | embedded into the document, or executed in its context. To support integrity 606 | metadata for some of these elements, a new integrity attribute is added to 607 | the list of content attributes for the link and script elements.

608 | 609 |

A corresponding integrity IDL attribute which reflects the 610 | value each element’s integrity content attribute is added to the 611 | HTMLLinkElement and HTMLScriptElement interfaces.

612 | 613 |

A future revision of this specification is likely to include integrity support 614 | for all possible subresources, i.e., a, audio, embed, iframe, img, 615 | link, object, script, source, track, and video elements.

616 | 617 |
618 | 619 |
620 |

The integrity attribute

621 | 622 |

The integrity attribute represents integrity metadata for an element. 623 | The value of the attribute MUST be either the empty string, or at least one 624 | valid metadata as described by the following ABNF grammar:

625 | 626 |
integrity-metadata = *WSP hash-with-options *( 1*WSP hash-with-options ) *WSP / *WSP
627 | hash-with-options  = hash-expression *("?" option-expression)
628 | option-expression  = *VCHAR
629 | hash-algo          = <hash-algo production from [Content Security Policy Level 2, section 4.2]>
630 | base64-value       = <base64-value production from [Content Security Policy Level 2, section 4.2]>
631 | hash-expression    = hash-algo "-" base64-value
632 | 
633 | 634 |

The integrity IDL attribute must reflect the integrity content attribute.

635 | 636 |

option-expressions are associated on a per hash-expression basis and are 637 | applied only to the hash-expression that immediately precedes it.

638 | 639 |

In order for user agents to remain fully forwards compatible with future 640 | options, the user agent MUST ignore all unrecognized option-expressions.

641 | 642 |

Note that while the option-expression has been reserved in the syntax, no 643 | options have been defined. It is likely that a future version of the spec will 644 | define a more specific syntax for options, so it is defined here as broadly 645 | as possible.

646 | 647 |
648 | 649 | 650 |
651 |

Element interface extensions

652 | 653 |
654 |
HTMLLinkElement
655 | 656 |
657 |
attribute DOMString integrity
658 |
The value of this element’s integrity attribute
659 |
660 |
661 | 662 |
663 |
HTMLScriptElement
664 | 665 |
666 |
attribute DOMString integrity
667 |
The value of this element’s integrity attribute
668 |
669 |
670 | 671 |
672 | 673 |
674 |

Handling integrity violations

675 | 676 |

The user agent will refuse to render or execute responses that fail an integrity 677 | check, instead returning a network error as defined in Fetch [[!FETCH]].

678 | 679 |

On a failed integrity check, an error event is fired. Developers 680 | wishing to provide a canonical fallback resource (e.g., a resource not served 681 | from a CDN, perhaps from a secondary, trusted, but slower source) can catch this 682 | error event and provide an appropriate handler to replace the 683 | failed resource with a different one.

684 | 685 |
686 | 687 |
688 |
Elements
689 | 690 |
691 | 692 | 693 |

Whenever a user agent attempts to obtain a resource pointed to by a 694 | link element that has a rel attribute with the keyword of stylesheet, 695 | modify step 4 to read:

696 | 697 |

Do a potentially CORS-enabled fetch of the resulting absolute URL, with the 698 | mode being the current state of the element’s crossorigin content attribute, 699 | the origin being the origin of the link element’s Document, the default origin 700 | behavior set to taint, and the integrity metadata of the request set to the 701 | value of the element’s integrity attribute.

702 | 703 |
704 | 705 | 706 |
707 |
The script element
708 | 709 |

Replace step 14.1 of HTML5’s “prepare a script” algorithm with:

710 | 711 |
    712 |
  1. Let src be the value of the element’s src attribute and 713 | the request’s associated integrity metadata be the value of the element’s 714 | integrity attribute.
  2. 715 |
716 | 717 |
718 | 719 | 720 |
721 | 722 | 723 |
724 | 725 | 726 |
727 |

Proxies

728 | 729 |

Optimizing proxies and other intermediate servers which modify the 730 | responses MUST ensure that the digest associated 731 | with those responses stays in sync with the new content. One option 732 | is to ensure that the integrity metadata associated with 733 | resources is updated. Another 734 | would be simply to deliver only the canonical version of resources 735 | for which a page author has requested integrity verification.

736 | 737 |

To help inform intermediate servers, those serving the resources SHOULD 738 | send along with the resource a Cache-Control header 739 | with a value of no-transform.

740 | 741 |
742 | 743 | 744 |
745 |

Security Considerations

746 | 747 |
748 |

Non-secure contexts remain non-secure

749 | 750 |

Integrity metadata delivered by a context that is not a Secure Context, 751 | such as an HTTP page, only protects an origin against a compromise of the 752 | server where an external resources is hosted. Network attackers can alter the 753 | digest in-flight (or remove it entirely, or do absolutely anything else to the 754 | document), just as they could alter the response the hash is meant to validate. 755 | Thus, it is recommended that authors deliver integrity metadata only to a 756 | Secure Context. See also securing the web.

757 | 758 |
759 | 760 | 761 |
762 |

Hash collision attacks

763 | 764 |

Digests are only as strong as the hash function used to generate them. It is 765 | recommended that user agents refuse to support known-weak hashing functions and 766 | limit supported algorithms to those known to be collision resistant. Examples of 767 | hashing functions that are not recommended include MD5 and SHA-1. At the time of 768 | writing, SHA-384 is a good baseline.

769 | 770 |

Moreover, it is recommended that user agents re-evaluate their supported hash 771 | functions on a regular basis and deprecate support for those functions shown to 772 | be insecure. Over time, hash functions may be shown to be much weaker than 773 | expected and, in some cases, broken, so it is important that user agents stay 774 | aware of these developments.

775 | 776 |
777 | 778 | 779 |
780 |

Cross-origin data leakage

781 | 782 |

This specification requires the CORS settings attribute to be present on 783 | integrity-protected cross-origin requests. If that requirement were omitted, 784 | attackers could violate the same-origin policy and determine whether 785 | a cross-origin resource has certain content.

786 | 787 |

Attackers would attempt to load the resource with a known digest, and 788 | watch for load failures. If the load fails, the attacker could surmise 789 | that the response didn’t match the hash and thereby gain some insight into 790 | its contents. This might reveal, for example, whether or not a user is 791 | logged into a particular service.

792 | 793 |

Moreover, attackers could brute-force specific values in an otherwise 794 | static resource. Consider a JSON response that looks like this:

795 | 796 |
{'status': 'authenticated', 'username': 'admin'}
797 | 
798 | 799 |

An attacker could precompute hashes for the response with a variety of 800 | common usernames, and specify those hashes while repeatedly attempting 801 | to load the document. A successful load would confirm that the attacker 802 | has correctly guessed the username.

803 | 804 |
805 | 806 | 807 |
808 | 809 | 810 |
811 |

Acknowledgements

812 | 813 |

Much of the content here is inspired heavily by Gervase 814 | Markham’s Link Fingerprints concept, as well as WHATWG’s Link Hashes.

815 | 816 |

A special thanks to Mike West of Google, Inc. for his invaluable contributions 817 | to the initial version of this spec. Additionally, Brad Hill, Anne van Kesteren, 818 | Jonathan Kingston, Mark Nottingham, Dan Veditz, Eduardo Vela, Tanvi Vyas, and 819 | Michal Zalewski provided invaluable feedback.

820 | 821 |
822 | 823 | 824 | 825 | -------------------------------------------------------------------------------- /signature-based-restrictions-explainer.markdown: -------------------------------------------------------------------------------- 1 | Signature-based SRI now lives at . A draft specification is available at . 2 | -------------------------------------------------------------------------------- /spec_v1.markdown: -------------------------------------------------------------------------------- 1 |
2 | This specification defines a mechanism by which user agents may verify that 3 | a fetched resource has been delivered without unexpected manipulation. 4 |
5 | 6 |
7 | A list of changes to this document may be found at 8 | . 9 |
10 | 11 |
12 | ## Introduction 13 | 14 | Sites and applications on the web are rarely composed of resources from 15 | only a single origin. For example, authors pull scripts and styles from a 16 | wide variety of services and content delivery networks, and must trust 17 | that the delivered representation is, in fact, what they expected to 18 | load. If an attacker can trick a user into downloading content from 19 | a hostile server (via [DNS][] poisoning, or other such means), the author has 20 | no recourse. Likewise, an attacker who can replace the file on the Content 21 | Delivery Network (CDN) server has the ability to inject arbitrary content. 22 | 23 | Delivering resources over a secure channel mitigates some of this risk: with 24 | [TLS][], [HSTS][], and [pinned public keys][], a user agent can be fairly certain 25 | that it is indeed speaking with the server it believes it's talking to. These 26 | mechanisms, however, authenticate _only_ the server, _not_ the content. An 27 | attacker (or administrator) with access to the server can manipulate content with 28 | impunity. Ideally, authors would not only be able to pin the keys of a 29 | server, but also pin the _content_, ensuring that an exact representation of 30 | a resource, and _only_ that representation, loads and executes. 31 | 32 | This document specifies such a validation scheme, extending two HTML elements 33 | with an `integrity` attribute that contains a cryptographic hash 34 | of the representation of the resource the author expects to load. For instance, 35 | an author may wish to load some framework from a shared server rather than hosting it 36 | on their own origin. Specifying that the _expected_ SHA-384 hash of 37 | `https://example.com/example-framework.js` 38 | is `Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7` means 39 | that the user agent can verify that the data it loads from that URL matches 40 | that expected hash before executing the JavaScript it contains. This 41 | integrity verification significantly reduces the risk that an attacker can 42 | substitute malicious content. 43 | 44 | This example can be communicated to a user agent by adding the hash to a 45 | `script` element, like so: 46 | 47 | 50 | {:.example} 51 | 52 | Scripts, of course, are not the only response type which would benefit 53 | from integrity validation. The scheme specified here also applies to `link` 54 | and future versions of this specification are likely to expand this coverage. 55 | 56 | [DNS]: https://www.ietf.org/rfc/rfc1035.txt 57 | [TLS]: https://tools.ietf.org/html/rfc5246 58 | [HSTS]: https://tools.ietf.org/html/rfc6797 59 | [pinned public keys]: https://tools.ietf.org/html/rfc7469 60 | 61 |
62 | ### Goals 63 | 64 | 1. Compromise of a third-party service should not automatically mean 65 | compromise of every site which includes its scripts. Content authors 66 | will have a mechanism by which they can specify expectations for 67 | content they load, meaning for example that they could load a 68 | _specific_ script, and not _any_ script that happens to have a 69 | particular URL. 70 | 71 | 2. The verification mechanism should have error-reporting functionality which 72 | would inform the author that an invalid response was received. 73 | 74 |
75 | 76 |
77 | ### Use Cases/Examples 78 | 79 |
80 | #### Resource Integrity 81 | 82 | * An author wishes to use a content delivery network to improve performance 83 | for globally-distributed users. It is important, however, to ensure that 84 | the CDN's servers deliver _only_ the code the author expects them to 85 | deliver. To mitigate the risk that a CDN compromise (or unexpectedly malicious 86 | behavior) would change that site in unfortunate ways, the following 87 | [integrity metadata][] is added to the `link` element included on the page: 88 | 89 | 92 | {:.example} 93 | 94 | * An author wants to include JavaScript provided by a third-party 95 | analytics service. To ensure that only the code that has been carefully 96 | reviewed is executed, the author generates [integrity metadata][] for 97 | the script, and adds it to the `script` element: 98 | 99 | 102 | {:.example} 103 | 104 | * A user agent wishes to ensure that JavaScript code running in high-privilege HTML 105 | contexts (for example, a browser's New Tab page) aren't manipulated before display. 106 | [Integrity metadata][] mitigates the risk that altered JavaScript will run 107 | in these pages' high-privilege contexts. 108 |
109 |
110 |
111 | 112 |
113 | Conformance requirements phrased as algorithms or specific steps can be 114 | implemented in any manner, so long as the end result is equivalent. In 115 | particular, the algorithms defined in this specification are intended to 116 | be easy to understand and are not intended to be performant. Implementers 117 | are encouraged to optimize. 118 | 119 |
120 | ### Key Concepts and Terminology 121 | 122 | This section defines several terms used throughout the document. 123 | 124 | The term digest refers to the base64-encoded result of 125 | executing a cryptographic hash function on an arbitrary block of data. 126 | 127 | The term origin is defined in the Origin specification. 128 | [[!RFC6454]] 129 | 130 | The representation data and content encoding of a resource 131 | are defined by [RFC7231, section 3][representationdata]. [[!RFC7231]] 132 | 133 | [representationdata]: https://tools.ietf.org/html/rfc7231#section-3 134 | 135 | A base64 encoding is defined in [RFC 4648, section 4][base64]. 136 | [[!RFC4648]] 137 | 138 | [base64]: https://tools.ietf.org/html/rfc4648#section-4 139 | 140 | The SHA-256, SHA-384, and SHA-512 are part 141 | of the SHA-2 set of cryptographic hash functions defined by the 142 | NIST in ["FIPS PUB 180-4: Secure Hash Standard (SHS)"][shs]. 143 | 144 | [shs]: http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf 145 |
146 | 147 |
148 | ### Grammatical Concepts 149 | 150 | The Augmented Backus-Naur Form (ABNF) notation used in this document is 151 | specified in RFC5234. [[!ABNF]] 152 | 153 | [Appendix B.1][abnf-b1] of [[!ABNF]] defines VCHAR 154 | (printing characters). 155 | 156 | WSP (white space) characters are defined in Section 157 | [2.4.1 Common parser idioms][space-chars] of the HTML 5 specification as 158 | White_Space characters. 159 | 160 | [abnf-b1]: https://tools.ietf.org/html/rfc5234#appendix-B.1 161 | [space-chars]: http://www.w3.org/TR/html5/infrastructure.html#space-character 162 |
163 | 164 |
165 | 166 |
167 | ## Framework 168 | 169 | The integrity verification mechanism specified here boils down to the 170 | process of generating a sufficiently strong cryptographic digest for a 171 | resource, and transmitting that digest to a user agent so that it may be 172 | used to verify the response. 173 | 174 |
175 | ### Integrity metadata 176 | 177 | To verify the integrity of a response, a user agent requires integrity 178 | metadata as part of the [request][]. This metadata consists of the following 179 | pieces of information: 180 | 181 | * cryptographic hash function ("alg") 182 | * [digest][] ("val") 183 | * options ("opt") 184 | 185 | The hash function and digest MUST be provided in order to validate a 186 | response's integrity. 187 | 188 | At the moment, no options are defined. However, future versions of 189 | the spec may define options, such as MIME types [[!MIMETYPE]]. 190 | {:.note} 191 | 192 | This metadata MUST be encoded in the same format as the `hash-source` (without the single quotes) 193 | in [section 4.2 of the Content Security Policy Level 2 specification][csp2-section42]. 194 | 195 | For example, given a script resource containing only the string `alert('Hello, world.');`, 196 | an author might choose [SHA-384][sha2] as a hash function. 197 | `H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO` is the base64-encoded 198 | digest that results. This can be encoded as follows: 199 | 200 | sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO 201 | {:.example} 202 | 203 |
204 | Digests may be generated using any number of utilities. [OpenSSL][], for 205 | example, is quite commonly available. The example in this section is the 206 | result of the following command line: 207 | 208 | echo -n "alert('Hello, world.');" | openssl dgst -sha384 -binary | openssl base64 -A 209 |
210 | {:.note} 211 | 212 | [request]: https://fetch.spec.whatwg.org/#concept-request-integrity-metadata 213 | [csp2-section42]: http://www.w3.org/TR/CSP2/#source-list-syntax 214 | [openssl]: https://www.openssl.org/ 215 | [sha2]: #dfn-sha-2 216 | [digest]: #dfn-digest 217 | [integrity metadata]: #dfn-integrity-metadata 218 |
219 | 220 |
221 | ### Cryptographic hash functions 222 | 223 | Conformant user agents MUST support the [SHA-256][sha2], [SHA-384][sha2] 224 | and [SHA-512][sha2] cryptographic hash functions for use as part of a 225 | request's [integrity metadata][] and MAY support additional hash functions. 226 | 227 | User agents SHOULD refuse to support known-weak hashing functions like MD5 or 228 | SHA-1 and SHOULD restrict supported hashing functions to those known to be 229 | collision-resistant. Additionally, user agents SHOULD re-evaluate their 230 | supported hash functions on a regular basis and deprecate support for those 231 | functions that have become insecure. See [Hash collision attacks]. 232 | 233 | [Hash collision attacks]: #hash-collision-attacks 234 | 235 |
236 | #### Agility 237 | 238 | Multiple sets of [integrity metadata][] may be associated with a single 239 | resource in order to provide agility in the face of future cryptographic discoveries. 240 | For example, the resource described in the previous section may be described 241 | by either of the following hash expressions: 242 | 243 | sha384-dOTZf16X8p34q2/kYyEFm0jh89uTjikhnzjeLeF0FHsEaYKb1A1cv+Lyv4Hk8vHd 244 | sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw== 245 | {:.example} 246 | 247 | Authors may choose to specify both, for example: 248 | 249 | 253 | {:.example} 254 | 255 | In this case, the user agent will choose the strongest hash function in the 256 | list, and use that metadata to validate the response (as described below in 257 | the "[parse metadata][parse]" and "[get the strongest metadata from 258 | set][get-the-strongest]" algorithms). 259 | 260 | When a hash function is determined to be insecure, user agents SHOULD deprecate 261 | and eventually remove support for integrity validation using the insecure hash 262 | function. User agents MAY check the validity of responses using a digest based on 263 | a deprecated function. 264 | 265 | To allow authors to switch to stronger hash functions without being held back by older 266 | user agents, validation using unsupported hash functions acts like no integrity value 267 | was provided (see the "[Does response match metadataList][match]" algorithm below). 268 | Authors are encouraged to use strong hash functions, and to begin migrating to 269 | stronger hash functions as they become available. 270 |
271 | 272 |
273 | #### Priority 274 | 275 | User agents must provide a mechanism for determining the relative priority of two 276 | hash functions and return the empty string if the priority is equal. That is, if 277 | a user agent implemented a function like getPrioritizedHashFunction(a, 278 | b) it would return the hash function the user agent considers the most 279 | collision-resistant. For example, `getPrioritizedHashFunction('sha256', 280 | 'sha512')` would return `'sha512'` and `getPrioritizedHashFunction('sha256', 281 | 'sha256')` would return the empty string. 282 | 283 | The getPrioritizedHashFunction is an internal 284 | implementation detail. It is not an API that implementors 285 | provide to web applications. It is used in this document 286 | only to simplify the algorithm description. 287 | {:.note} 288 | 289 |
290 | 291 |
292 | 293 |
294 | ### Response verification algorithms 295 | 296 |
297 | #### Apply algorithm to response 298 | {: #apply-algorithm-to-response} 299 | [apply-algorithm]: #apply-algorithm-to-response 300 | 301 | 1. Let result be the result of [applying algorithm][apply-algorithm] 302 | to the [representation data][representationdata] without any content-codings 303 | applied, except when the user agent intends to consume the content with 304 | content-encodings applied. In the latter case, let result be 305 | the result of applying algorithm to the [representation data][representationdata]. 306 | 2. Let encodedResult be result of base64-encoding 307 | result. 308 | 3. Return encodedResult. 309 |
310 |
311 | #### Is response eligible for integrity validation 312 | {: #is-response-eligible-for-integrity-validation} 313 | [eligible]: #is-response-eligible-for-integrity-validation 314 | 315 | In order to mitigate an attacker's ability to read data cross-origin by 316 | brute-forcing values via integrity checks, responses are only eligible for such 317 | checks if they are same-origin or are the result of explicit access granted to 318 | the loading origin via Cross Origin Resource Sharing [[!CORS]]. 319 | 320 | As noted in [RFC6454, section 4][uri-origin], some user agents use 321 | globally unique identifiers for each file URI. This means that 322 | resources accessed over a `file` scheme URL are unlikely to be 323 | eligible for integrity checks. 324 | {:.note} 325 | 326 | Being in a [Secure Context][] (e.g., a document delivered over HTTPS) is not 327 | necessary for the use of integrity validation. Because resource integrity is 328 | only an application level security tool, and it does not change the security 329 | state of the user agent, a Secure Context is unnecessary. However, if integrity 330 | is used in something other than a Secure Context (e.g., a document delivered 331 | over HTTP), authors should be aware that the integrity provides no security 332 | guarantees at all. For this reason, authors should only deliver integrity 333 | metadata in a Secure Context. See [Non-secure contexts remain non-secure][] for 334 | more discussion. 335 | {:.note} 336 | 337 | [uri-origin]: https://tools.ietf.org/html/rfc6454#section-4 338 | [Secure Context]: https://w3c.github.io/webappsec-secure-contexts/ 339 | [Non-secure contexts remain non-secure]: #non-secure-contexts-remain-non-secure 340 | 341 | The following algorithm details these restrictions: 342 | 343 | 1. Let response be the response that results from 344 | [fetching][] the resource. 345 | 2. If the response [type][response type] is `basic`, 346 | `cors` or `default`, return `true`. 347 | 3. Return `false`. 348 | 349 |
The [response types][response type] are defined by the Fetch 350 | specification [[!FETCH]] and refer to the following: 351 | 352 | * `basic` is a same-origin response, and thus the requestor has full access 353 | to read the body. 354 | * `cors` is a valid response to a cross-origin, CORS-enabled request, and thus 355 | again the requestor has full access to read the body. 356 | * `default` is a valid response that is generated by a Service Worker as a 357 | response to the request, so its body, too, is fully readable by the requestor. 358 |
359 | 360 | [fetching]: https://fetch.spec.whatwg.org/#concept-fetch 361 | [response type]: https://fetch.spec.whatwg.org/#concept-response-type 362 |
363 |
364 | #### Parse metadata. 365 | {: #parse-metadata} 366 | [parse]: #parse-metadata 367 | 368 | This algorithm accepts a string, and returns either `no metadata`, or a set of 369 | valid hash expressions whose hash functions are understood by 370 | the user agent. 371 | 372 | 1. Let result be the empty set. 373 | 2. Let empty be equal to `true`. 374 | 3. For each token returned by [splitting metadata on 375 | spaces][split-on-spaces]: 376 | 1. Set empty to `false`. 377 | 2. If token is not a valid metadata, skip the remaining 378 | steps, and proceed to the next token. 379 | 3. Parse token per the grammar in [integrity metadata][]. 380 | 4. Let algorithm be the alg component of 381 | token. 382 | 5. If algorithm is a hash function recognized by the user 383 | agent, add the parsed token to result. 384 | 4. Return `no metadata` if empty is `true`, otherwise return 385 | result. 386 | 387 | [split-on-spaces]: http://www.w3.org/TR/html5/infrastructure.html#split-a-string-on-spaces 388 |
389 |
390 | #### Get the strongest metadata from set. 391 | {: #get-the-strongest-metadata-from-set} 392 | [get-the-strongest]: #get-the-strongest-metadata-from-set 393 | 394 | 1. Let result be the empty set and strongest be the empty 395 | string. 396 | 2. For each item in set: 397 | 1. If result is the empty set, add item to 398 | result and set strongest to item, skip 399 | to the next item. 400 | 2. Let currentAlgorithm be the alg component of 401 | strongest. 402 | 3. Let newAlgorithm be the alg component of 403 | item. 404 | 4. If the result of [`getPrioritizedHashFunction(currentAlgorithm, newAlgorithm)`][getPrioritizedHashFunction] 405 | is the empty string, add item to result. If the 406 | result is newAlgorithm, set strongest to 407 | item, set result to the empty set, and add 408 | item to result. 409 | 3. Return result. 410 | 411 | [getPrioritizedHashFunction]: #dfn-getprioritizedhashfunction-a-b 412 |
413 |
414 | #### Does response match metadataList? 415 | {: #does-response-match-metadatalist} 416 | [match]: #does-response-match-metadatalist 417 | 418 | 1. Let parsedMetadata be the result of 419 | [parsing metadataList][parse]. 420 | 2. If parsedMetadata is `no metadata`, return `true`. 421 | 3. If [response is not eligible for integrity 422 | validation][eligible], return `false`. 423 | 4. If parsedMetadata is the empty set, return `true`. 424 | 5. Let metadata be the result of [getting the strongest 425 | metadata from parsedMetadata][get-the-strongest]. 426 | 6. For each item in metadata: 427 | 1. Let algorithm be the alg component of 428 | item. 429 | 2. Let expectedValue be the val component of 430 | item. 431 | 3. Let actualValue be the result of [applying 432 | algorithm to response][apply-algorithm]. 433 | 4. If actualValue is a case-sensitive match for 434 | expectedValue, return `true`. 435 | 7. Return `false`. 436 | 437 | This algorithm allows the user agent to accept multiple, valid strong hash 438 | functions. For example, a developer might write a `script` element such as: 439 | 440 | 444 | {:.example} 445 | 446 | which would allow the user agent to accept two different content payloads, one 447 | of which matches the first SHA384 hash value and the other matches the second 448 | SHA384 hash value. 449 | 450 | User agents may allow users to modify the result of this algorithm via user 451 | preferences, bookmarklets, third-party additions to the user agent, and other 452 | such mechanisms. For example, redirects generated by an extension like 453 | [HTTPS Everywhere](https://www.eff.org/https-everywhere) could load and execute 454 | correctly, even if the HTTPS version of a resource differs from the HTTP 455 | version. 456 | {:.note} 457 | 458 | This algorithm returns `false` if the response is not [eligible] for integrity 459 | validation since Subresource Integrity requires CORS, and it is a logical error 460 | to attempt to use it without CORS. Additionally, user agents SHOULD report a 461 | warning message to the developer console to explain this failure. 462 | {:.note} 463 |
464 |
465 | 466 |
467 | ### Verification of HTML document subresources 468 | 469 | A variety of HTML elements result in requests for resources that are to be 470 | embedded into the document, or executed in its context. To support integrity 471 | metadata for some of these elements, a new `integrity` attribute is added to 472 | the list of content attributes for the `link` and `script` elements. 473 | 474 | A corresponding `integrity` IDL attribute which [reflects][reflect] the 475 | value each element's `integrity` content attribute is added to the 476 | `HTMLLinkElement` and `HTMLScriptElement` interfaces. 477 | 478 | A future revision of this specification is likely to include integrity support 479 | for all possible subresources, i.e., `a`, `audio`, `embed`, `iframe`, `img`, 480 | `link`, `object`, `script`, `source`, `track`, and `video` elements. 481 | {:.note} 482 | 483 |
484 | 485 |
486 | #### The `integrity` attribute 487 | {: #the-integrity-attribute} 488 | 489 | The `integrity` attribute represents [integrity metadata][] for an element. 490 | The value of the attribute MUST be either the empty string, or at least one 491 | valid metadata as described by the following ABNF grammar: 492 | 493 | integrity-metadata = *WSP hash-with-options *( 1*WSP hash-with-options ) *WSP / *WSP 494 | hash-with-options = hash-expression *("?" option-expression) 495 | option-expression = *VCHAR 496 | hash-algo = 497 | base64-value = 498 | hash-expression = hash-algo "-" base64-value 499 | 500 | The `integrity` IDL attribute must [reflect][] the `integrity` content attribute. 501 | 502 | `option-expression`s are associated on a per `hash-expression` basis and are 503 | applied only to the `hash-expression` that immediately precedes it. 504 | 505 | In order for user agents to remain fully forwards compatible with future 506 | options, the user agent MUST ignore all unrecognized `option-expression`s. 507 | 508 | Note that while the `option-expression` has been reserved in the syntax, no 509 | options have been defined. It is likely that a future version of the spec will 510 | define a more specific syntax for options, so it is defined here as broadly 511 | as possible. 512 | {:.note} 513 | 514 | [reflect]: http://www.w3.org/TR/html5/infrastructure.html#reflect 515 |
516 | 517 |
518 | #### Element interface extensions 519 | 520 |
521 | ##### HTMLLinkElement 522 | 523 | attribute DOMString integrity 524 | : The value of this element's `integrity` attribute 525 | {:title="partial interface HTMLLinkElement"} 526 | {:.idl} 527 |
528 |
529 | ##### HTMLScriptElement 530 | 531 | attribute DOMString integrity 532 | : The value of this element's `integrity` attribute 533 | {:title="partial interface HTMLScriptElement"} 534 | {:.idl} 535 |
536 |
537 |
538 | #### Handling integrity violations 539 | 540 | The user agent will refuse to render or execute responses that fail an integrity 541 | check, instead returning a network error as defined in Fetch [[!FETCH]]. 542 | 543 | On a failed integrity check, an error event is fired. Developers 544 | wishing to provide a canonical fallback resource (e.g., a resource not served 545 | from a CDN, perhaps from a secondary, trusted, but slower source) can catch this 546 | error event and provide an appropriate handler to replace the 547 | failed resource with a different one. 548 | {:.note} 549 | 550 |
551 | 552 |
553 | ##### Elements 554 | 555 |
556 | ###### The `link` element for stylesheets 557 | {: #the-link-element-for-stylesheets} 558 | 559 | Whenever a user agent attempts to [obtain a resource][] pointed to by a 560 | `link` element that has a `rel` attribute with the keyword of `stylesheet`, 561 | modify step 4 to read: 562 | 563 | Do a potentially CORS-enabled fetch of the resulting absolute URL, with the 564 | mode being the current state of the element's crossorigin content attribute, 565 | the origin being the origin of the link element's Document, the default origin 566 | behavior set to taint, and the [integrity metadata][] of the request set to the 567 | value of the element's `integrity` attribute. 568 | 569 | {:start="4"} 570 | 571 | [obtain a resource]: http://www.w3.org/TR/html5/document-metadata.html#concept-link-obtain 572 |
573 | 574 |
575 | ###### The `script` element 576 | {: #the-script-element} 577 | 578 | Replace step 14.1 of HTML5's ["prepare a script" algorithm][prepare] with: 579 | 580 | 1. Let src be the value of the element's `src` attribute and 581 | the request's associated [integrity metadata][] be the value of the element's 582 | `integrity` attribute. 583 | 584 | {:start="6"} 585 | 586 | [prepare]: http://www.w3.org/TR/html5/scripting-1.html#prepare-a-script 587 | [fetching algorithm]: http://www.w3.org/TR/html5/infrastructure.html#potentially-cors-enabled-fetch 588 | [fire a simple event]: http://www.w3.org/TR/html5/webappapis.html#fire-a-simple-event 589 | [queue a task]: http://www.w3.org/TR/html5/webappapis.html#queue-a-task 590 |
591 | 592 |
593 | 594 |
595 | 596 |
597 | ## Proxies 598 | 599 | Optimizing proxies and other intermediate servers which modify the 600 | responses MUST ensure that the digest associated 601 | with those responses stays in sync with the new content. One option 602 | is to ensure that the [integrity metadata][] associated with 603 | resources is updated. Another 604 | would be simply to deliver only the canonical version of resources 605 | for which a page author has requested integrity verification. 606 | 607 | To help inform intermediate servers, those serving the resources SHOULD 608 | send along with the resource a [`Cache-Control`][cachecontrol] header 609 | with a value of [`no-transform`][notransform]. 610 | 611 | [cachecontrol]: https://tools.ietf.org/html/rfc7234#section-5.2 612 | [notransform]: https://tools.ietf.org/html/rfc7234#section-5.2.1.6 613 | 614 |
615 | 616 |
617 | ## Security Considerations 618 | 619 |
620 | ### Non-secure contexts remain non-secure 621 | 622 | [Integrity metadata][] delivered by a context that is not a [Secure Context], 623 | such as an HTTP page, only protects an origin against a compromise of the 624 | server where an external resources is hosted. Network attackers can alter the 625 | digest in-flight (or remove it entirely, or do absolutely anything else to the 626 | document), just as they could alter the response the hash is meant to validate. 627 | Thus, it is recommended that authors deliver integrity metadata only to a 628 | [Secure Context][]. See also [securing the web][]. 629 | 630 | [Securing the Web]: http://www.w3.org/2001/tag/doc/web-https 631 |
632 | 633 |
634 | ### Hash collision attacks 635 | 636 | Digests are only as strong as the hash function used to generate them. It is 637 | recommended that user agents refuse to support known-weak hashing functions and 638 | limit supported algorithms to those known to be collision resistant. Examples of 639 | hashing functions that are not recommended include MD5 and SHA-1. At the time of 640 | writing, SHA-384 is a good baseline. 641 | 642 | Moreover, it is recommended that user agents re-evaluate their supported hash 643 | functions on a regular basis and deprecate support for those functions shown to 644 | be insecure. Over time, hash functions may be shown to be much weaker than 645 | expected and, in some cases, broken, so it is important that user agents stay 646 | aware of these developments. 647 | 648 |
649 | 650 |
651 | ### Cross-origin data leakage 652 | 653 | This specification requires the [CORS settings attribute][] to be present on 654 | integrity-protected cross-origin requests. If that requirement were omitted, 655 | attackers could violate the [same-origin policy][] and determine whether 656 | a cross-origin resource has certain content. 657 | 658 | Attackers would attempt to load the resource with a known digest, and 659 | watch for load failures. If the load fails, the attacker could surmise 660 | that the response didn't match the hash and thereby gain some insight into 661 | its contents. This might reveal, for example, whether or not a user is 662 | logged into a particular service. 663 | 664 | Moreover, attackers could brute-force specific values in an otherwise 665 | static resource. Consider a JSON response that looks like this: 666 | 667 | {'status': 'authenticated', 'username': 'admin'} 668 | {:.example} 669 | 670 | An attacker could precompute hashes for the response with a variety of 671 | common usernames, and specify those hashes while repeatedly attempting 672 | to load the document. A successful load would confirm that the attacker 673 | has correctly guessed the username. 674 | 675 | [CORS settings attribute]: http://www.w3.org/TR/html5/infrastructure.html#cors-settings-attributes 676 | [same-origin policy]: http://www.w3.org/Security/wiki/Same_Origin_Policy 677 |
678 | 679 |
680 | 681 |
682 | ## Acknowledgements 683 | 684 | Much of the content here is inspired heavily by Gervase 685 | Markham's [Link Fingerprints][] concept, as well as WHATWG's [Link Hashes][]. 686 | 687 | A special thanks to Mike West of Google, Inc. for his invaluable contributions 688 | to the initial version of this spec. Additionally, Brad Hill, Anne van Kesteren, 689 | Jonathan Kingston, Mark Nottingham, Dan Veditz, Eduardo Vela, Tanvi Vyas, and 690 | Michal Zalewski provided invaluable feedback. 691 | 692 | 693 | [Link Fingerprints]: http://www.gerv.net/security/link-fingerprints/ 694 | [Link Hashes]: https://wiki.whatwg.org/wiki/Link_Hashes 695 |
696 | -------------------------------------------------------------------------------- /template.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Subresource Integrity 6 | 7 | 129 | 130 | 131 | <%= @body %> 132 | 133 | 134 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 49309 3 | , "contacts": ["wseltzer"] 4 | , "policy": "open" 5 | , "repo-type": "rec-track" 6 | } 7 | --------------------------------------------------------------------------------