Simplest way to obfuscate and deobfuscate a string in JavaScript
Asked Answered
T

5

68

I'm looking for a way to obfuscate and deobfuscate a string in JavaScript; by which I mean encryption and decryption when security is not an issue. Ideally something native to JS (like base64_encode() and base64_decode() in PHP) to "turn a string into something else and back again" without having to write a function.

Any suggestions welcome!

Theocritus answered 22/1, 2013 at 12:42 Comment(7)
The part about "encryption and decryption when security is not an issue" has me really confused. If security of the content is of no consequence, what possible value does encryption provide?Hawse
this makes completely no sense to me. If you obfuscate a string with a native function everybody can just call the decrypt function. There is no improvement at all and you can just leave the string as it is. Also this is very likely to break on unicode strings.Signora
@Signora It's useful in deterring the average Joe from inspecting a given logic in your application. Strictly speaking, even windows binaries can be reverse engineered, and as such, you could say compiling a C++ source into executable doesn't provide any additional security. In my opinion, this is just a matter of making the life of thieves more miserable, albeit on different levels.Achromatism
I sometimes find it useful, for elegance rather than for security, to obfuscate long URLs containing cleartext params that are to be shared.Sayer
This topic is useful to me because I need to have a json list of 'backlisted' words which will live on a client's page, but I dont want an exhaustive list of naughty strings directly in the source.Tiphanie
Here's another perfectly reasonable scenario for using obfuscation -- for writing tests to verify encryption/decryption are being applied properly that are isolated from any specific encryption algorithm, not to mention faster.Altigraph
Yet another use-case for insecure obfuscation--you may be using the strings as keys in a totally internal, distributed SSTable, and you want to spread out the unpredictable input evenly over the keyspace.Bellerophon
V
107

You can use btoa() and atob(). btoa() is like base64_encode() and atob() like base64_decode().

Here is an example:

btoa('Some text'); // U29tZSB0ZXh0
atob('U29tZSB0ZXh0'); // Some text

Keep in mind that this is not a secure way to keep secrets. Base64 is a binary-to-text encoding scheme that represents binary data in an ASCII string format by translating it into a radix-64 representation.

Virtuoso answered 22/1, 2013 at 12:43 Comment(6)
Seems to work in most modern browsers so suits my requirements perfectly. Thank you!Theocritus
Worth noting that the encoded string will be larger than the non-encoded one.Tunicate
Cross browser support seems good now. caniuse.com/#search=btoa It's safe to use.Calabresi
Thank you. I love the use-cases for this.Amphichroic
It is very important to note this will fail on strings containing unicode characters that do not belong to the 0-127 range of the ASCII character set, and therefore this function is insufficient for use on user input from almost any modern web app where people might use non-ascii chars. The MDN docs discuss how to deal with this, you must first convert the unicode characters to a byte string.Devil
Id like to add for anyone looking in the future, btoa and atob are deprecated and are only kept around for legacy web APIs, converting between base 64 encoded strings and binary data should be performed using Buffer.from(str, 'base64') and buf.toString('base64')Electrojet
L
46

It's worth noting that

(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]

evaluates to the string "fail" without ever looking like a string. Seriously, enter it into node and be amazed. You can spell anything in JavaScript by being crazy.

Lan answered 1/8, 2017 at 3:15 Comment(3)
go into node and break it into its parts. It starts to make some morbid sense.Lan
Interesting! got it working with most letters! jsfiddle.net/pg07yf87/2 Edit: this site also does it for you jsfuck.comHipparch
It works like this: 'false'[0] + 'false'[1] + ('false' + 'undefined')[10] + 'false'[2]Unprofitable
S
20

I'm obviously too late for an answer, but I was just working on another solution for the problem and base64 seemed to be to weak.

It works like this:

"abc;123!".obfs(13) // => "nopH>?@."
"nopH>?@.".defs(13) // => "abc;123!"

Code:

/**
 * Obfuscate a plaintext string with a simple rotation algorithm similar to
 * the rot13 cipher.
 * @param  {[type]} key rotation index between 0 and n
 * @param  {Number} n   maximum char that will be affected by the algorithm
 * @return {[type]}     obfuscated string
 */
String.prototype.obfs = function(key, n = 126) {
  // return String itself if the given parameters are invalid
  if (!(typeof(key) === 'number' && key % 1 === 0)
    || !(typeof(key) === 'number' && key % 1 === 0)) {
    return this.toString();
  }

  var chars = this.toString().split('');

  for (var i = 0; i < chars.length; i++) {
    var c = chars[i].charCodeAt(0);

    if (c <= n) {
      chars[i] = String.fromCharCode((chars[i].charCodeAt(0) + key) % n);
    }
  }

  return chars.join('');
};

/**
 * De-obfuscate an obfuscated string with the method above.
 * @param  {[type]} key rotation index between 0 and n
 * @param  {Number} n   same number that was used for obfuscation
 * @return {[type]}     plaintext string
 */
String.prototype.defs = function(key, n = 126) {
  // return String itself if the given parameters are invalid
  if (!(typeof(key) === 'number' && key % 1 === 0)
    || !(typeof(key) === 'number' && key % 1 === 0)) {
    return this.toString();
  }

  return this.toString().obfs(n - key);
};
Spooky answered 30/12, 2016 at 16:0 Comment(4)
This code breaks for...in enumeration of strings, don't use this in production.Allyl
@PatrickRoberts for ... in should always bundle with .hasOwnProperty(). You can also define obfs and defs without modifying String.prototypeMelonie
@GanQuan that was my point. The answer shouldn't be polluting builtin prototypes at all. The .hasOwnProperty() "good practice" (/s) came about due to the frequency of polluting builtins, not because it's actually made necessary by the language design itself. Because of how common it is for libraries to pollute builtins with arbitrary extensions like this, I think for...in has unfortunately become rarely, if ever, the appropriate choice for enumeration of anything, strings included.Allyl
You have a nice little obfuscator but then it's exposed globally for anyone to see on the String prototype. Kinda beats the purpose.Brottman
T
3

1. Using strange strings

Go to http://www.jsfuck.com/, enter the input string, and get the result in the text box below.

Test this in the console and it will return "hi":

(+(+!+[]+[+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]

console.log("Original: hi")
console.log("Converted: " + (+(+!+[]+[+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]])

It doesn't even look like a string!

2. Using atob and btoa

To use this method, just convert the string to base64 using atob("string") and then decrypt the string with btoa("encoded")

Trygve answered 8/6, 2022 at 10:14 Comment(0)
V
0

A simple method to obfuscate a string is to use a script like this, using Node.js. This code is in Typescript.

import readline from 'readline';

const rl = readline.createInterface({
    input:process.stdin,
    output:process.stdout
});
const randNum = (max:number=10000)=>Math.floor(Math.random() * max);
// finds an equation that will make the target number.
const findEquation = (target:number)=>{
    let equation = "";
    const rand = randNum();
    const diff = target - rand;
    const rand2 = randNum()
    const product = diff * rand2;
    equation = `${rand}+(${product} / ${rand2})`;
    return equation;
}
const randCharSequence = (length:number)=>{
    let str = "";
    for(let i = 0; i < length; i++){
        str += String.fromCharCode(randNum(256));
    }
    return str
}
const sep = randCharSequence(8)
rl.question("Enter the string to obfuscate:\n", (str)=>{
    let obfuscated = "(''";
    str.split("").forEach(char=>{
        const code = findEquation(char.charCodeAt(0));
        obfuscated += `+(String.fromCharCode(${code})+\"${sep}\")`
    })
    obfuscated += `).split(\"${sep}\").join('')`;
    
    console.log("Obfuscated String:");
    console.log(obfuscated);
    rl.close();
});

When you run this script, you are prompted to enter the string to obfuscate. Then, the script prints out an obfuscated string. It uses equations that involve numbers of up to 10000 to generate numbers, that then get converted and concatonated into a string. But, that's not all. It adds a randomly generated string of 8 characters to the output, that gets removed when it is ran. This makes it harder to get a look at the string when reading the code, but keep in mind, that a person with access to the code, can easily add a console.log statement, and then get the string. Note, this isn't encryption, it just makes the code harder to understand and read.

Volauvent answered 3/6, 2023 at 2:58 Comment(1)
Also, once you have the string, you can base64 decode/encode it to make it harder to understaend.Volauvent

© 2022 - 2024 — McMap. All rights reserved.