admin管理员组

文章数量:1287597

I am designing a JavaScript secure loader. The loader is inlined in the index.html. The goal of the secure loader is to only load JavaScript resources are trusted. The contents of index.html are mostly limited to the secure loader. For security purposes, I want index.html (as stored in cache) to never change, even if my website is hacked.

How can I cache index.html without the server being able to tamper with the cache? I am wondering if ServiceWorkers can help. Effectively, the index.html would register a service worker for fetching itself from an immutable cache (no network request is even made).

I am designing a JavaScript secure loader. The loader is inlined in the index.html. The goal of the secure loader is to only load JavaScript resources are trusted. The contents of index.html are mostly limited to the secure loader. For security purposes, I want index.html (as stored in cache) to never change, even if my website is hacked.

How can I cache index.html without the server being able to tamper with the cache? I am wondering if ServiceWorkers can help. Effectively, the index.html would register a service worker for fetching itself from an immutable cache (no network request is even made).

Share Improve this question edited Feb 3, 2017 at 12:16 Randomblue asked Jan 17, 2017 at 14:42 RandomblueRandomblue 116k150 gold badges362 silver badges557 bronze badges 11
  • 1 Not certain what you are trying to achieve? Once user visits index.html, the html document itself and resources linked from index.html should not be changed? – guest271314 Commented Feb 3, 2017 at 21:16
  • 4 What you seek to achieve -if possible- would ALSO prohibit you from ever updating said method for recurring visitors while it would offer no protection at all for new visitors. Seems far too many drawbacks for the limited advantage you might achieve. – user3277192 Commented Feb 6, 2017 at 4:37
  • @swa66 The secure loader would have logic to be updatable in a controlled fashion. – Randomblue Commented Feb 7, 2017 at 15:17
  • 1 @swa66 Cryptographic signatures for which only the legitimate website owner has. – Randomblue Commented Feb 8, 2017 at 12:06
  • 1 With all due respect, </disclaimer> you do not design a website considering it is going to be hacked and you will lose control of it each and every day after your 4pm tea. You just don't. Reason of your question rather smells like you are going to hack some website and put a content there, and you do not want legitimate admin to be able to change it afterwards. Again, forgive me if it is not the case but this smells plain maleficent to me. – Umur Karagöz Commented Feb 10, 2017 at 9:27
 |  Show 6 more ments

3 Answers 3

Reset to default 4 +100

in chrome you can use FileSystem API

http://www.noupe./design/html5-filesystem-api-create-files-store-locally-using-javascript-webkit.html this allows you to then save and read files from a sand-boxed file-system though the browser.

As for other support it's not been confirmed as an addition to the HTML5 specification set yet. so it's only available in chrome.

You could also use the IndexDB system this is supported in all modern browsers.

you can use both these services inside a Service Worker to manage the loading and manage of the content however i have to question why would you want to you prevent your self from ever updating your index.html

This design goal of "secure javascript loading"/TOFU is typically associated with javascript crypto and browser secrets (e.g. Cyph, Mega), so I'll include some relevant remendations along the way.

You're in dangerous territory. There are many dragons.

Option 1: Implement feross/infinite-app-cache to permanently cache the app in the browser

Here's the least amount of code required to achieve a TOFU web app in all browsers.

index.html:

<html manifest="manifest.appcache">

manifest.app cache:

CACHE MANIFEST
/manifest.appcache
/index.html
/script-loader.js

And make sure you serve the manifest with a content type of "text/cache-manifest".

Note: This standard is deprecated, however it will take some years before browsers disable this feature.

Problem: If an attacker (who has already hacked your server) can trick your users into visiting a URL not in the app cache, they can serve arbitrary code to extract/use browser secrets etc.

Solution: User input cannot be intercepted by a malicious page on the same domain (unless you do something really silly), so encrypt any browser secrets using a user-supplied password and dchest/scrypt-async-js.

Another thing to consider is the cached contents of a page CAN be extracted by the malicious page simply by using AJAX - so you HAVE to use user input, not just a random token rendered to the page.

Option 2: Extend the above solution with HPKP Suicide and a Service Worker

Implement HPKP Suicide to permanently "bake" the app into the browser by bricking the user's connection to the server.

If (and only if) you have implemented HPKP Suicide, service workers are now safe to use because the enforced max-age of 24 hours has no effect if the browser can't re-download the service worker.

index.html or script-loader.js:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js').catch(function (error) {
      console.error(error);
    });
  });
}

service-worker.js:

var files = [ 
  '/',
  'index.html',
  'script-loader.js'
].map(file => new Request(file))

self.addEventListener('install', () => {  
  caches.open('cache')
    .then(cache => {
      files.map(file => {
        fetch(file)
          .then(response => cache.put(file, response))
          .catch(err => console.error(err))
      })
    })
    .catch(err => console.error(err))
})

self.addEventListener('fetch', (e) => {
  var url = e.request.url.split('#')[0]

  if (!files.filter(file => file.url === url).length) {
    return e.respondWith(new Response('Not Found', { status: 404 }))
  }

  return e.respondWith(
    caches.match(e.request).then(cachedResponse => {
      if (cachedResponse) return cachedResponse

      return Promise.all([
        caches.open('cache'),
        fetch(e.request.clone())
      ]).then((results) => {
        var cache = results[0]
        var response = results[1]

        cache.put(e.request, response.clone())

        return response
      })
    })
  )
})

Important note: Without HPKP Suicide, using a service worker is LESS secure than infinite app cache because the service worker has a max-age of 24 hours, even if your server's cache directives set a higher age. Using service workers will also pletely disable app cache. It's a waste of time trying to achieve this with service workers if you haven't implemented HPKP Suicide.

Discussion

  • HPKP suicide has lost the guarantee of permanence in Chrome as they have recently implemented a max-max-age of 60 days (see issue #523654 for discussion)
  • I strongly remend you use TweetNaCl.js for all browser crypto: it takes effort to use incorrectly and unless you are encrypting large files the performance difference with WebCrypto is insignificant

You can also use Cache API, which can save the responses of your requests (all kinds of requests - html pages, api requests, images). But note, that it's currently supported only in Chrome and Firefox.

On that page is also example how to use it with fetch

本文标签: javascriptPermanent browser cache using ServiceWorkerStack Overflow