t2ym / thin-hook

Thin Hook Preprocessor
Other
4 stars 1 forks source link

Block access via automation like puppeteer #266

Open t2ym opened 6 years ago

t2ym commented 6 years ago

Block access via automation like puppeteer

Current Status thin-hook@0.2.0 in stack branch merged to develop and master branches

Update README.md for the next release 0.2.0

Proof of Concept Implementation

Notes

t2ym commented 6 years ago

Using Symbols for contexts to avoid disguised string contexts via puppeteer

__hook__('op', ThisObject, ['prop'], __context_mapper__[1]) $hook$.global(__hook__, __context_mapper__[24], 'o', 'let')[__context_mapper__[25]]

Notes:

New helper function for Symbols

$hook$.$ = function contextSymbolGenerator(symbolToContext, contexts) {
    let result = [];
    let contextToSymbol = {};
    let hookGlobal = hook.global;
    for (let i = 0; i < contexts.length; i++) {
      symbolToContext[result[i] = _Symbol()] = contexts[i];
      contextToSymbol[contexts[i]] = result[i];
      hookGlobal[result[i]] = contextToSymbol;
    }
    return result;
  }

Example (__context_mapper__ is actually __ + hex(sha256(context + code)) + __)

const __context_mapper__ = $hook$.$(__hook__, [
  'examples/example2.js,C',
  '_p_C;examples/example2.js,C',
  'examples/example2.js,C,add',
  'examples/example2.js,C,add,plus'
]);
$hook$.global(__hook__, __context_mapper__[0], 'C', 'class')[__context_mapper__[1]] = class C {
  add(a, b) {
    return __hook__((a = 1, b = 2) => {
      let plus = (...args) => __hook__((x, y) => x + y, null, args, __context_mapper__[3]);
      return __hook__(plus, null, [
        a,
        b
      ], __context_mapper__[2], 0);
    }, null, arguments, __context_mapper__[2]);
  }
};
t2ym commented 6 years ago

cache-automation.js to automate cache collection for cache-bundle.json

gulp.task('get-version', (done) => { return gulp.src(['demo/original-index.html'], { base: 'demo' }) .pipe(through.obj((file, enc, callback) => { let html = String(file.contents); let versionIndex = html.indexOf('/hook.min.js?version=') + '/hook.min.js?version='.length; let versionIndexEnd = html.indexOf('&', versionIndex); version = 'version_' + html.substring(versionIndex, versionIndexEnd); callback(null, file); })) .pipe(through.obj((file, enc, callback) => { done(); })); });

// One-time special cache-bundle.json generation gulp.task('cache-bundle-automation-json', (done) => { fs.writeFileSync(cacheBundlePath, JSON.stringify({ "version": version, "https://thin-hook.localhost.localdomain/automation.json": JSON.stringify({ "state": "init", // update state in the script to perform operations including reloading "serverSecret": serverSecret, "script": cacheAutomationScript },null,0) },null,2)) done(); });

- Embed `hex(sha256(serverSecret + cacheAutomationScriptCode))` as `<script context-generator src="cache-bundle.js?no-hook=true&authorization={HERE}"></script>`
- In `cacheBundleGeneration.js`, wait for the global value `__{serverSecret}__` to obtain the raw cache-bundle JSON and save as `cache-bundle.json` after normalization.  The one-time `serverSecret` is lost at this overwriting of `cache-bundle.json` 
```javascript
  // cacheBundleGeneration.js (extracted)
  let rawCacheBundleJSON;
  while (!rawCacheBundleJSON) {
    try {
      rawCacheBundleJSON = await page.evaluate(new Function(`return async function cacheBundle() {
        try {
          return __${serverSecret}__; // the variable disappears once read
        }
        catch (e) {
          return [][0]; // undefined;
        }
      }`)());
    }
    catch (e) {
      // try again
      console.log(e.message);
    }
    await new Promise(resolve => setTimeout(resolve, 5000));
  }
  console.log('cacheBundle raw length = ', rawCacheBundleJSON.length, ' bytes');