자바 스크립트로드 및 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을 방문한 사용자는 아무것도 설치할 필요없이 공개 키가 사전로드 된 공개 키 암호화 클라이언트를 얻게됩니다. 해시 확인으로 인해 ISP 가 트래픽에 방해가 되더라도 실제 암호화 소프트웨어를 사용했다고 확신 할 수 있습니다 .

불행하게도,이 의사 코드의 실제 버전은 QR 코드에 비해 상당히 길다. 내 도전은 : 얼마나 짧게 만들 수 있습니까? 구현은 다음과 같습니다.

  • Chrome 및 Firefox의 주소 표시 줄에서 올바르게 실행되는 data : ... URL이어야합니다. (유효한 데이터를 만들려면 : URL, %를 % 25로 인코딩하고 줄 바꿈을 제거해야합니다)
  • URL과 SHA-256 해시가 포함되어 있어야하며 처음에 일반 텍스트 문자열 리터럴로 사용하는 것이 좋습니다.
  • XMLHttpRequest (또는 유사한 API)를 사용하여 URL에서 파일을 다운로드하십시오 . (이 기능을 사용하려면 서버에 Access-Control-Allow-Origin : * 헤더가 포함되어 있어야합니다.)
  • 해당 URL이 성공적으로로드되고 결과에 예상 해시가있는 파일 인 경우 eval (). 그렇지 않으면 아무것도하지 않거나 오류 메시지가 표시됩니다.
  • Chrome과 Firefox에있는 모든 내장 JavaScript 함수는 공정한 게임이지만 라이브러리를로드하는 것은 불가능합니다.
  • 가능한 적은 바이트를 사용하십시오

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암호화 라이브러리를 보낸 다음 암호화 라이브러리가로드 된 상태에서 두 번째 요청을하면이 점이 맞지 않습니까? 나는 해결책을 연구하는 데 시간을 보냈고 결국 그 일을 끝내었지만 문제가되는 암호 라이브러리를 엉망으로 만들지 않도록 ISP를 신뢰한다는 것을 깨달았습니다.
— JayQuerie.com

권리; 안전을 위해 첫 번째로로드 된 파일은 완전히 URL에있는 코드를 사용하여 해시를 검증해야합니다. 운 좋게도 이것은 하나의 해시 함수 (필요하지만 SHA-256 일 필요는 없음) 만 필요합니다. 이는 순진하게 4700 바이트였으며 아마도 최적화가 높으면 <1kb 일 수 있습니다. (그러면 더 이상 URL에 포함되지 않기 때문에 크기가 중요하지 않은 더 큰 라이브러리의로드 및 확인을 처리합니다.)
— jimrandomh

"라이브러리로드가 불가능합니다." - script요소 를 생성 하고 async속성을 설정하고 false문서에 삽입하는 등 코드에서 수행해야하는 경우를 제외하고 라이브러리를로드하지 않아야 합니까?
— John Dvorak

2
@ JanDvorak, 그러나 라이브러리가 수정되지 않았 음을 어떻게 확인합니까?
— Peter Taylor

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 및 해시 값은 하드 코딩됩니다. 해시는 십진수 더블 워드로 인코딩되며, 내 코드의 값은 예제 Python 서버의 스크립트에 해당합니다.

또한 URL 인코딩을 귀찮게하지 않았지만 javascript:URL 표시 줄에 수동으로 입력 한 다음 코드를 붙여 넣을 때도 콘솔에서 작동합니다.

구현은 호환되지 않지만 512MB보다 작은 파일에서는 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.