Tải JavaScript và SHA-256


14

Đây là một câu đố golf mã với một ứng dụng trong thế giới thực. Một số trình duyệt hiện tại, nếu bạn nhập URL trông giống như

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

sẽ thực thi mã JavaScript đã cho. Bây giờ giả sử bạn có một URL trông giống như (mã giả):

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

Nếu bạn đã in mã này trên danh thiếp dưới dạng mã QR , thì bất kỳ ai đã truy cập URL đó bằng trình duyệt phù hợp sẽ nhận được ứng dụng khách mật mã khóa công khai, với khóa công khai của bạn được tải sẵn mà không phải cài đặt bất cứ thứ gì. Do xác minh băm, bạn có thể tin tưởng rằng họ đã có phần mềm tiền điện tử thực sự, ngay cả khi ISP của họ can thiệp với lưu lượng truy cập.

Thật không may, phiên bản thực của mã giả này khá dài đối với mã QR. Thử thách của tôi là: bạn có thể làm nó ngắn đến mức nào? Một triển khai sẽ:

  • Hãy là dữ liệu: ... URL thực thi chính xác từ thanh địa chỉ của Chrome và Firefox. (Để tạo dữ liệu hợp lệ: URL, bạn sẽ phải mã hóa% dưới dạng% 25 và loại bỏ các dòng mới)
  • Có một URL và hàm băm SHA-256 được nhúng, tốt nhất là dưới dạng chuỗi ký tự văn bản đơn giản ở gần đầu
  • Tải xuống một tệp từ một URL bằng XMLHttpRequest (hoặc API tương tự). (Lưu ý rằng máy chủ sẽ cần bao gồm tiêu đề Access-Control-Allow-Origin: * để làm việc này.)
  • Nếu URL đó được tải thành công và kết quả là một tệp có hàm băm dự kiến, eval () nó. Nếu không làm gì hoặc hiển thị một thông báo lỗi.
  • Tất cả các hàm JavaScript dựng sẵn có trong cả Chrome và Firefox đều là trò chơi công bằng, nhưng việc tải thư viện là không thể.
  • Sử dụng càng ít byte càng tốt

Tôi đã tạo một phiên bản ngây thơ bằng CryptoJS ( phiên bản rút gọn ):

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>

Mà đi ra khỏi công cụ khai thác như:

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> 

Đã thử nghiệm với máy chủ Python tối thiểu này:

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()

Phần JavaScript là 4700 byte, nhưng nó có thể nhỏ hơn nhiều. Làm thế nào nhỏ nó có thể nhận được?


Câu hỏi thú vị. Gửi thư viện tiền điện tử từ máy chủ đến máy khách evalvà sau đó thực hiện yêu cầu thứ hai với thư viện tiền điện tử được tải sẽ đánh bại quan điểm này có đúng không? Tôi đã dành thời gian nghiên cứu giải pháp và cuối cùng đã làm điều đó, nhưng sau đó nhận ra rằng điều đó có nghĩa là tin tưởng ISP sẽ không gây rối với thư viện tiền điện tử vốn là một vấn đề.
JayQuerie.com

Đúng; để bảo mật, tệp được tải đầu tiên phải được xác thực băm bằng cách sử dụng mã hoàn toàn trong URL. May mắn thay, điều này chỉ cần một hàm băm (mặc dù không nhất thiết phải là SHA-256), với 4700 byte một cách ngây thơ và có thể có thể <1kb với tối ưu hóa nặng. (Điều đó sau đó xử lý tải và xác minh một thư viện lớn hơn, trong đó kích thước không quan trọng vì vậy mcuh vì nó không còn trong URL).
jimrandomh

"tải thư viện là không thể." - không nên tải các thư viện được phép, ngoại trừ nó phải được thực hiện từ mã, chẳng hạn như bằng cách tạo ra một scriptphần tử, thiết lập của nó asyncsở hữu đến falsevà chèn nó vào tài liệu?
John Dvorak

2
@JanDvorak, nhưng làm thế nào để bạn xác nhận rằng thư viện chưa được sửa đổi?
Peter Taylor

script/javascript? Bạn có ý nghĩa text/javascript.
nyuszika7h

Câu trả lời:


6

844 nhân vật

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)

Các giá trị URL và Hash được mã hóa cứng. Hash được mã hóa dưới dạng các từ kép thập phân, các giá trị trong mã của tôi tương ứng với tập lệnh trong máy chủ Python mẫu.

Tôi cũng không bận tâm đến việc mã hóa URL, nhưng nó hoạt động khi bạn nhập javascript:thủ công vào thanh URL và sau đó dán mã (và cả trong bảng điều khiển).

Việc triển khai không tuân thủ, nhưng nó sẽ hoạt động đối với các tệp nhỏ hơn 512 MB.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.