Завантаження JavaScript та SHA-256


14

Це кодова головоломка з додатком у реальному світі. Деякі поточні веб-переглядачі, якщо ви ввели URL-адресу, схожу

data:text/html,<script>alert("hi")</script>

виконає даний код JavaScript. Тепер припустимо, у вас була URL-адреса, яка виглядала так (псевдокод):

data:text/html,<script>
    myPublicKey="12345678";
    cryptoLib=download("http://example.com/somecryptolib.js");
    if(sha256sum(cryptoLib) == "12345678")
        eval(cryptoLib)
</script>

Якщо ви надрукували це на візитних картках у вигляді QR-коду , то кожен, хто зайшов до цієї URL-адреси у відповідному веб-переглядачі, отримає крипто-клієнт із відкритим ключем, попередньо завантажений ваш відкритий ключ, не встановлюючи нічого. Через перевірку хешу, ви можете бути впевнені, що вони отримали справжнє програмне забезпечення для криптовалюти, навіть якщо їх провайдер перешкоджає трафіку.

На жаль, реальна версія цього псевдокоду досить тривала для QR-коду. Моє завдання: як коротко ви можете це зробити? Реалізація:

  • Будьте даними: ... URL, який правильно виконується з адресного рядка Chrome та Firefox. (Щоб зробити дійсні дані: URL-адреса, вам доведеться кодувати% як% 25 та знімати нові рядки)
  • Майте вбудований хеш URL-адреси та SHA-256 , бажано, як початковий текст прямого рядка
  • Завантажте файл з URL-адреси за допомогою XMLHttpRequest (або подібного API). (Зверніть увагу, що для цього сервера потрібно буде включити заголовок Access-Control-Allow-Origin: *.)
  • Якщо ця URL-адреса успішно завантажена, а результат - файл із очікуваним хешем, eval () - це. Інакше нічого не робіть і не показуйте повідомлення про помилку.
  • Всі вбудовані функції JavaScript, які є в Chrome і Firefox, є чесною грою, але завантаження бібліотек неможливо.
  • Використовуйте якомога менше байтів

Я створив наївну версію за допомогою CryptoJS ( мінімізована версія ):

data:text/html,<script>
    u = 'http://localhost:8000'
    h = '5e3f73c606a82d68ef40f9f9405200ce24adfd9a4189c2bc39015345f0ee46d4'
    // Insert CryptoJS here
    r = new XMLHttpRequest;
    r.open('GET', u, false);
    r.send();
    if(CryptoJS.SHA256(r.response) == h)
        eval(r.response);
</script>

Що виходить з мініфікатора як:

data:text/html,<script>u="http://localhost:8000";h="5e3f73c606a82d68ef40f9f9405200ce24adfd9a4189c2bc39015345f0ee46d4";var CryptoJS=CryptoJS||function(k,w){var f={},x=f.lib={},g=function(){},l=x.Base={extend:function(a){g.prototype=this;var c=new g;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},t=x.WordArray=l.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=w?c:4*a.length},toString:function(a){return(a||y).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%254)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%254)&255)<<24-8*((b+e)%254);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<32-8*(c%254);a.length=k.ceil(c/4)},clone:function(){var a=l.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push((1<<30)*4*k.random()|0);return new t.init(c,a)}}),z=f.enc={},y=z.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%254)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b,2),16)<<24-4*(b%258);return new t.init(d,c/2)}},m=z.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%254)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%254);return new t.init(d,c)}},n=z.Utf8={stringify:function(a){try{return decodeURIComponent(escape(m.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return m.parse(unescape(encodeURIComponent(a)))}},B=x.BufferedBlockAlgorithm=l.extend({reset:function(){this._data=new t.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=n.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?k.ceil(f):k.max((f|0)-this._minBufferSize,0);a=f*e;b=k.min(4*a,b);if(a){for(var p=0;p<a;p+=e)this._doProcessBlock(d,p);p=d.splice(0,a);c.sigBytes-=b}return new t.init(p,b)},clone:function(){var a=l.clone.call(this);a._data=this._data.clone();return a},_minBufferSize:0});x.Hasher=B.extend({cfg:l.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){B.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new A.HMAC.init(a,d)).finalize(c)}}});var A=f.algo={};return f}(Math);(function(k){for(var w=CryptoJS,f=w.lib,x=f.WordArray,g=f.Hasher,f=w.algo,l=[],t=[],z=function(a){return (1<<30)*4*(a-(a|0))|0},y=2,m=0;64>m;){var n;a:{n=y;for(var B=k.sqrt(n),A=2;A<=B;A++)if(!(n%25A)){n=!1;break a}n=!0}n&&(8>m&&(l[m]=z(k.pow(y,0.5))),t[m]=z(k.pow(y,1/3)),m++);y++}var a=[],f=f.SHA256=g.extend({_doReset:function(){this._hash=new x.init(l.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],p=b[2],k=b[3],s=b[4],l=b[5],m=b[6],n=b[7],q=0;64>q;q++){if(16>q)a[q]=c[d+q]|0;else{var v=a[q-15],g=a[q-2];a[q]=((v<<25|v>>>7)^(v<<14|v>>>18)^v>>>3)+a[q-7]+((g<<15|g>>>17)^(g<<13|g>>>19)^g>>>10)+a[q-16]}v=n+((s<<26|s>>>6)^(s<<21|s>>>11)^(s<<7|s>>>25))+(s&l^~s&m)+t[q]+a[q];g=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&p^f&p);n=m;m=l;l=s;s=k+v|0;k=p;p=f;f=e;e=v+g|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+p|0;b[3]=b[3]+k|0;b[4]=b[4]+s|0;b[5]=b[5]+l|0;b[6]=b[6]+m|0;b[7]=b[7]+n|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;d[e>>>5]|=128<<24-e%2532;d[(e+64>>>9<<4)+14]=k.floor(b/(1<<30)*4);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=g.clone.call(this);a._hash=this._hash.clone();return a}});w.SHA256=g._createHelper(f);w.HmacSHA256=g._createHmacHelper(f)})(Math);r=new XMLHttpRequest;r.open("GET",u,!1);r.send();CryptoJS.SHA256(r.response)==h&&eval(r.response)</script> 

Тестовано на цьому мінімальному сервері Python:

import BaseHTTPServer

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_HEAD(s):
        s.send_response(200)
        s._sendHeaders()
        s.end_headers()
    def do_GET(s):
        s.send_response(200)
        s._sendHeaders()
        s.end_headers()
        s.wfile.write('alert("Success!")')
    def _sendHeaders(s):
        s.send_header("Content-type", "script/javascript");
        s.send_header("Access-Control-Allow-Origin", "*");

def run(server_class=BaseHTTPServer.HTTPServer,
    handler_class=RequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()

run()

Частина JavaScript становить 4700 байт, але вона може бути значно меншою. Скільки маленьких він може отримати?


Цікаве запитання. Передача криптобібліотеки з сервера клієнтові, evalа потім подання другого запиту із завантаженою криптобібліотекою, спростує точку цього правильного? Я провів деякий час, працюючи над рішенням, і в кінцевому підсумку робив саме це, але потім зрозумів, що це означає довіряти провайдеру, щоб не возитися з криптовалютою, що є проблемою.
JayQuerie.com

Правильно; щоб бути безпечним, перший завантажений файл повинен мати перевірку хешу, використовуючи код, який повністю знаходиться в URL-адресі. На щастя, для цього потрібна лише одна хеш-функція (яка не обов'язково повинна бути SHA-256), яка була 4700 байтів наївно і, ймовірно, може бути <1 кбіт з важкою оптимізацією. (Потім обробляється завантаження та перевірка більшої бібліотеки, де розмір не має значення mcuh, оскільки він більше не знаходиться в URL-адресі).
jimrandomh

"завантаження бібліотек неможливо." - чи не слід допускати завантаження бібліотек, за винятком того, що це потрібно робити з коду, наприклад, створивши scriptелемент, встановивши його asyncвластивість falseі вставивши його в документ?
Іван Дворак

2
@JanDvorak, але як ви підтвердите, що бібліотека не була змінена?
Пітер Тейлор

script/javascript? Ви маєте на увазі text/javascript.
nyuszika7h

Відповіді:


6

844 персонажів

K=new XMLHttpRequest;K.open("get","http://localhost:8000",O=j=n=q=0);K.send();m=K.response;l=m.length;k=l+1|63;W=[o=Math.pow];for(H=[R=o(i=2,32)];i<313+l;i++)for(W[i]||(K[q]=o(i,1/3)*R|0,H[q++]=o(i,.5)*R|0,I=2);W[i*I++]=199>I;)o[n>>2]|=(n^l?m.charCodeAt(n):128)<<24-n++%4*8;for(o[k>>2]=8*l;j<=k;H[I-7]+=a=t+T|0)i=j++&63||(a=H[0],b=H[1],c=H[2],d=H[3],e=H[4],f=H[5],g=H[6],h=H[7],0),y=W[i-15],x=W[i-2],t=h+(e<<26^e>>>6^e<<21^e>>>11^e<<7^e>>>25)+(e&f^~e&g)+K[i]+(W[i]=16>i?o[O++]:(y<<25^y>>>7^y<<14^y>>>18^y>>>3)+W[i-7]+(x<<15^x>>>17^x<<13^x>>>19^x>>>10)+W[i-16]),T=(a<<30^a>>>2^a<<19^a>>>13^a<<10^a>>>22)+(a&b^a&c^b&c),H[I=i-63|7]+=h=g,H[I-1]+=g=f,H[I-2]+=f=e,H[I-3]+=e=d+t|0,H[I-4]+=d=c,H[I-5]+=c=b,H[I-6]+=b=a;1581216710^H[0]|111684968^H[1]|4014012921^H[2]|1079115982^H[3]|615382426^H[4]|1099547324^H[5]|956388165^H[6]|4042147540^H[7]||eval(m)

Значення URL та Hash жорстко кодуються. Хеш кодується у вигляді подвійних подвійних слів, значення мого коду відповідають сценарію на прикладі сервера Python.

Я також не переймався кодуванням URL-адрес, але він працює, коли ви вводите javascript:вручну в рядку URL-адреси, а потім вставляєте код (а також у консоль).

Реалізація не сумісна, але вона повинна працювати для файлів розміром менше 512 Мб.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.