문서
/
레시피
/

바이트코드 배열 인코딩 키

바이트코드 배열 인코딩 키 외부화하기

Pro

vmBytecodeArrayEncodingKey로 직접 VM 바이트코드 암호화 키를 지정하고, 키 게터를 통해 런타임에 다시 넘겨줍니다 — 번들 밖에 보관하거나, 클라이언트 저장소에서 읽거나, 백엔드에서 가져옵니다.

이 옵션들이 하는 일

vmBytecodeArrayEncoding은 VM 바이트코드 배열을 암호화하여 출력물에 평문으로 남지 않도록 합니다. 기본적으로 암호화 키는 환경에서 도출되어 클라이언트에서 재구성되므로, 여러분이 키를 직접 다룰 일이 전혀 없습니다. 편리하긴 하지만, 키 소재는 여전히 번들 안에 존재합니다.

두 가지 옵션을 사용하면 키를 번들에서 꺼내 직접 제어할 수 있습니다.

  • vmBytecodeArrayEncodingKey컴파일 시점에 여러분이 지정하는 키입니다. 설정하면 기본 환경 도출 키 대신 사용되며, 난독화된 출력물에 포함되지 않습니다.
  • vmBytecodeArrayEncodingKeyGetter런타임에 그와 동일한 키를 반환하는 JavaScript 표현식입니다. 그대로 포함되며, 난독화된 코드가 로드될 때 브라우저에서 평가됩니다.

핵심은 분리입니다. 키가 코드 안에 없기 때문에, 번들을 순수하게 정적으로 스캔해도 키를 복구할 수 없습니다. 코드가 실행되려면 런타임에 키가 여전히 존재해야 하므로 진정한 비밀은 아니지만, 키가 어디에서 오고 누가 볼 수 있는지는 여러분이 결정합니다.

두 키가 결합되는 방식

여러분의 키는 결코 단독으로 사용되지 않습니다. 양쪽 모두에서 난독화 도구가 제어하는 내부 키와 섞입니다.

  • 컴파일 시점. vmBytecodeArrayEncodingKey는 난독화 도구가 도출한 내부 키와 결합되고, 그 결과로 만들어진 혼합 키로 바이트코드 배열이 인코딩됩니다.
  • 런타임. vmBytecodeArrayEncodingKeyGetter가 해석하는 값은 여러 런타임 요소로부터 클라이언트에서 재구성된 동일한 내부 키와 결합되어 바이트코드를 디코딩합니다.

양쪽 모두 여러분의 키를 내부 키와 섞기 때문에, 게터는 vmBytecodeArrayEncodingKey로 전달한 것과 정확히 동일한 문자열로 해석되어야 합니다. 어느 한쪽만으로는 충분하지 않습니다. 내부 키가 없는 여러분의 키로는 바이트코드를 디코딩할 수 없고, 여러분의 키가 없는 내부 키는 쓸모가 없습니다. 그래서 누가 여러분의 키를 받는지를 제어하는 것이 실제로 코드를 보호하는 일입니다.

런타임에 키 공급하기

기본적으로 게터는 동기식입니다. 난독화된 코드가 로드될 때 표현식이 즉시 키를 반환해야 합니다. 이미 클라이언트에 존재하는 어떤 소스에서든 읽어오세요 — 쿠키, localStorage, 전역 변수, 또는 서버가 주입한 DOM 요소 등입니다.

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,       // e.g. 'mySecretKey123'
    vmBytecodeArrayEncodingKeyGetter: "window.__VM_KEY__" // returns the key at runtime
});

키는 난독화된 코드가 실행되기 전에 존재해야 합니다.

// Set by a different script, a server-injected inline script, etc.
window.__VM_KEY__ = 'mySecretKey123';

다른 동기식 소스들도 같은 방식으로 동작합니다. 여러분의 앱이 이미 채우고 있는 것을 선택하세요.

// From a cookie
vmBytecodeArrayEncodingKeyGetter: "document.cookie.match(/vmKey=([^;]+)/)?.[1]"

// From localStorage
vmBytecodeArrayEncodingKeyGetter: "localStorage.getItem('vmKey')"

// From a server-injected meta tag
vmBytecodeArrayEncodingKeyGetter: "document.querySelector('meta[name=\"vm-key\"]').content"

// From a nested object
vmBytecodeArrayEncodingKeyGetter: "window.config.encryption.key"

백엔드에서 키 가져오기(비동기)

vmAsyncExecutor 필요 · v7.3.0+

동기식 게터는 이미 클라이언트에 있는 것만 읽을 수 있습니다. 서버에서 키를 가져와서 인증 뒤에 게이트를 두고 취소할 수 있게 하려면, 게터가 비동기식이어야 하며 그러려면 vmAsyncExecutor가 필요합니다. 비동기 실행기를 활성화하면 게터가 Promise를 반환할 수 있고, VM은 실행 전에 그것을 기다립니다.

JavaScriptObfuscator.obfuscate(sourceCode, {
    vmObfuscation: true,
    vmAsyncExecutor: true,
    vmBytecodeArrayEncoding: true,
    vmBytecodeArrayEncodingKey: process.env.VM_KEY,       // kept on your server, not in the bundle
    vmBytecodeArrayEncodingKeyGetter:
        'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});

서버에서는 애플리케이션이 신뢰하는 것이 무엇이든(유효한 세션, 예상되는 Origin 또는 Referer, 라이선스 확인 등) 그에 기반하여 어떤 키를 반환할지 결정합니다. 요령은 이렇습니다. 신뢰할 수 없는 호출자를 거부하는 대신, 잘못된 키를 반환하는 것입니다. 그러면 바이트코드는 쓰레기로 디코딩되고 보호된 코드가 스스로 실패하는데, 이는 공격자에게 무엇을 우회해야 하는지 정확히 알려주는 뻔한 401보다 은밀합니다.

// Express example — the exact checks depend on your app
app.get('/api/vm-key', (req, res) => {
    const origin = req.get('origin');
    const trusted =
        req.session?.user &&                       // a valid session, and
        origin === 'https://app.example.com';      // the expected production origin

    res.type('text/plain').send(
        // Real key for valid users; a decoy for everyone else
        // (no session, or a localhost / unexpected origin).
        trusted ? process.env.VM_KEY : process.env.VM_DECOY_KEY
    );
});

이 엔드포인트에서는 빌드 시점에 vmBytecodeArrayEncodingKey로 전달한 것과 정확히 동일한 문자열을 제공하세요. 여러분의 환경 밖에서 실행되는 번들 사본은 미끼 키를 받아 아무것도 아닌 것으로 복호화되어 작동하지 않습니다.

키가 일치하지 않을 때

난독화된 코드는 게터가 난독화 중에 사용한 것과 정확히 동일한 키를 반환할 때에만 동작합니다. 키가 다르거나, 게터가 undefined, null, 또는 빈 문자열을 반환하면, 복호화는 잘못된 키스트림을 생성하고 코드는 런타임에 쓰레기 출력이나 일반적인 런타임 오류로 실패합니다.

의도적으로 키에 특화된 별도의 오류 메시지는 없습니다. 실패한 키는 다른 어떤 런타임 결함과도 구별되지 않습니다. 따라서 VM으로 보호된 번들이 이 옵션이 적용된 뒤에야 예외를 던진다면, 먼저 키 경로를 확인하세요 — 게터가 페이지에서 해석되는지, 비어 있지 않은 문자열을 반환하는지, 그리고 빌드할 때 사용한 것과 동일한 값을 반환하는지 말입니다.