I am making an AJAX chat room with the guidance of an AJAX book teaching me to use JSON and eval() function.
This chat room has normal chat function and a whiteboard feature.
When a normal text message comes from the php server in JSON format, the javascript in browser does this:
Without Whiteboard Command -------------------------------------------
function importServerNewMessagesSince(msgid) {
//loadText() is going to return me a JSON object from the server
//it is an array of {id, author, message}
var latest = loadText("get_messages_since.php?message=" + msgid);
var msgs = eval(latest);
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
} ...
The whiteboard drawing commands are sent by server in JSON format with special user name called "SVR_CMD", now the javascript is changed slightly:
With Whiteboard Command --------------------------------------------------
function importServerNewMessagesSince(msgid) {
//loadText() is going to return me a JSON object from the server
//it is an array of {id, author, message}
var latest = loadText("get_messages_since.php?message=" + msgid);
var msgs = eval(latest);
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
if (msg.author == "SVR_CMD") {
eval(msg.contents); // <-- Problem here ...
//I have a javascript drawLine() function to handle the whiteboard drawing
//server command sends JSON function call like this:
//"drawLine(200,345,222,333)" eval() is going to parse execute it
//It is a hacker invitation to use eval() as someone in chat room can
//insert a piece of javascript code and send it using the name SVR_CMD?
else {
displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
}
} ...
Now, if the hacker changes his username to SVR_CMD in the script, then in the message input start typing javascript code, insdead of drawLine(200,345,222,333), he is injecting redirectToMyVirusSite(). eval() will just run it for him in everyone's browser in the chat room.
So, as you can see, to let the eval to execute a command from an other client in the chat room is obviously a hacker invitation. I understand the book I followed is only meant to be an introduction to the functions. How do we do it properly with JSON in a real situation?
e.g. is there a server side php or .net function to javascriptencode/escape to make sure no hacker can send a valid piece of javascript code to other client's browser to be eval() ? Or is it safe to use JSON eval() at all, it seems to be a powerful but evil function?
Thank you,
Tom
What is this book? eval is evil, there is not a single reason to use it, ever.
To transform a JSON string into a javascript object, you can do the following:
var obj = JSON.parse(latest)
Which means you can then use:
[].forEach.call(obj, function( o ) {
// You can use o.message, o.author, etc.
} )
To do the opposite (javascript object -> JSON string), the following works:
var json = JSON.stringify(obj)
It only is unsafe if the executed code is generated by other clients and not by the server. Of course you would need to prevent anybody to use that name, though I don't understand why you would use the "author" field? Just send an object {"whiteboard":"drawLine(x,y,z)"} instead of {"author":"SVR_CMD","contents":"drawLine(x,y,z)"}.
But it is right, eval() is still an invitation for hackers. One can always send invalid data and try to influence the output more or less directly. The only way for escaping is a proper serialisation of the data you want to receive and send - the drawings data. How do you receive the whiteboard commands? There is no serverside "escape" function to make javascript code "clean" - it would always be a security hole.
I would expect a serialisation like
message = {
"author": "...", // carry the information /who/ draws
"whiteboard": {
"drawline": [200, 345, 222, 333]
}
}
so you can sanitize the commands (here: "drawline") easiliy.
The use of eval() might be OK if you have very complex commands and want to reduce the transferred data by building them serverside. Still, you need to parse and escape the received commands from other clients properly. But I'd recommend to find a solution without eval.
Setting eval issue aside, do not use field that can be filled by user - .author in your code - for authentication purposes. Add another field to your JSON message, say .is_server_command that when present, would signify special treating of message. This field is will be not depended on user input and thus wouldn't be hijacked by "hacker".
Related
I just wanted to know if there's a way to count how many times a message has been sent in my Discord server, so the bot can send a message. I'm new with coding, so I don't know many things. Thank you in advance!
Explanation
To store the amount of messages sent in a guild, you'll have to keep track of a count somehow. Each time a message is sent, you can increment it by 1. Then, upon a user's request, you can display that number.
One easy option would be to store this "message count" for each guild inside of a JSON file. However, this would greatly impact performance. Consider a database for much better speeds and reliability.
Example Setup
Before using this system, create a guilds.json file with a blank object ({}).
Declaring the necessary variables...
const fs = require('fs'); // fs is the built-in Node.js file system module.
const guilds = require('./guilds.json'); // This path may vary.
Adding the system to the message event listener...
client.on('message', message => {
// If the author is NOT a bot...
if (!message.author.bot) {
// If the guild isn't in the JSON file yet, set it up.
if (!guilds[message.guild.id]) guilds[message.guild.id] = { messageCount: 1 };
// Otherwise, add one to the guild's message count.
else guilds[message.guild.id].messageCount++;
// Write the data back to the JSON file, logging any errors to the console.
try {
fs.writeFileSync('./guilds.json', JSON.stringify(guilds)); // Again, path may vary.
} catch(err) {
console.error(err);
}
}
});
Using the system in a command...
// Grab the message count.
const messageCount = guilds[message.guild.id].messageCount;
// Send the message count in a message. The template literal (${}) adds an 's' if needed.
message.channel.send(`**${messageCount}** message${messageCount !== 1 ? 's' : ''} sent.`)
.catch(console.error);
JSON is highly prone to corruption if a queue system is not created that will make sure multiple reads and writes are not happening to a file all at the same time. For the purpose of what you want, I would use something like SQLite that requires minimal setup, is easy to learn, and has helper frameworks to make it easier to use such as Keyv and Sequelize.
Here is a good guide on how to use sqlite in the nodejs runtime environment.
I want to give the user the possibility of writing symbolic maths formulas which are later evaluated with certain values.
The user might - for example - want to enter some formula a * (b + 1) where a and b may be different upon each evaluation. My approach so far was using the built in JavaScript engine in Java but as I read through this tutorial on scripting, I realized that the engine is actually really powerful.
The formulas are stored in configuration files, so someone might send such a configuration file to another user, which would then be executed on their machine.
Unfortunately I don't know JavaScript, so I don't know if the user could actually inject any seriously malicious code.
The formula above would be stored as a JavaScriptFormulaProcessor object like this:
JavaScriptFormulaProcessor processor =
new JavaScriptFormulaProcessor("a * (b + 1)", "a", "b");
Initializing the engine:
public JavaScriptFormulaProcessor(String formula, String... variableNames) throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
StringBuilder builder = new StringBuilder(variableNames[0]);
for (int i = 1; i < variableNames.length; i++) {
builder.append(", ").append(variableNames[i]);
}
String script = "function f("+builder.toString()+") { return "+formula+"}";
engine.eval(script);
invocable = (Invocable) engine;
}
Executing the function:
public void execute(Number[] functionInputs) throws ScriptException {
try {
Object result = invocable.invokeFunction("f", functionInputs);
// process result..
} catch (NoSuchMethodException e) {
throw new RuntimeException(e); // should actually never be thrown..
}
}
Does this code create an attack vector for my application? Bonus question: if yes, any better suggestions?
If formula is under the users' control, then this code is extremely vulnerable because Java methods can be accessed and run from within the ScriptEngine.
See also this question: Why can you execute Java-Code from a JS-ScriptEngine eval(String)?
As an example, consider this formula:
String formula = "(java.lang.Runtime.getRuntime().exec('some-malicious-script'), a+b)";
Apart from calculating the sum, this script would run java.lang.Runtime.getRuntime().exec().
You can run any static Java method this way.
I would say that this code is not safe as you allow JavaScript to be evaluated by the engine.
What I would do:
Send the config file to a server, where the receiver gets the config file from. Write a parser server side that only accepts valid formulas and discards anything that isn't, then store it somewhere (database / file / whatever). Then send a 100% safe package that you made yourself after parsing to the receiver. This way you assure that whatever the receiver gets is firstly validated by you.
NOTE: If you do this, you need to write some sort of converter in javascript that converts your package to the javascript - formulas in order for it to be evaluated by the code that you present in your question. You could choose to only validate server side, and then just send the user-made package originally sent to the receiver, though you'd allow yourself to make a mistake in validating causing the receiver to still run unsafe code.
I'm supposed to parse a very large JSON array in Javascipt. It looks like:
mydata = [
{'a':5, 'b':7, ... },
{'a':2, 'b':3, ... },
.
.
.
]
Now the thing is, if I pass this entire object to my parsing function parseJSON(), then of course it works, but it blocks the tab's process for 30-40 seconds (in case of an array with 160000 objects).
During this entire process of requesting this JSON from a server and parsing it, I'm displaying a 'loading' gif to the user. Of course, after I call the parse function, the gif freezes too, leading to bad user experience. I guess there's no way to get around this time, is there a way to somehow (at least) keep the loading gif from freezing?
Something like calling parseJSON() on chunks of my JSON every few milliseconds? I'm unable to implement that though being a noob in javascript.
Thanks a lot, I'd really appreciate if you could help me out here.
You might want to check this link. It's about multithreading.
Basically :
var url = 'http://bigcontentprovider.com/hugejsonfile';
var f = '(function() {
send = function(e) {
postMessage(e);
self.close();
};
importScripts("' + url + '?format=json&callback=send");
})();';
var _blob = new Blob([f], { type: 'text/javascript' });
_worker = new Worker(window.URL.createObjectURL(_blob));
_worker.onmessage = function(e) {
//Do what you want with your JSON
}
_worker.postMessage();
Haven't tried it myself to be honest...
EDIT about portability: Sebastien D. posted a comment with a link to mdn. I just added a ref to the compatibility section id.
I have never encountered a complete page lock down of 30-40 seconds, I'm almost impressed! Restructuring your data to be much smaller or splitting it into many files on the server side is the real answer. Do you actually need every little byte of the data?
Alternatively if you can't change the file #Cyrill_DD's answer of a worker thread will be able to able parse data for you and send it to your primary JS. This is not a perfect fix as you would guess though. Passing data between the 2 threads requires the information to be serialised and reinterpreted, so you could find a significant slow down when the data is passed between the threads and be back to square one again if you try to pass all the data across at once. Building a query system into your worker thread for requesting chunks of the data when you need them and using the message callback will prevent slow down from parsing on the main thread and allow you complete access to the data without loading it all into your main context.
I should add that worker threads are relatively new, main browser support is good but mobile is terrible... just a heads up!
I was wondering if there is a way to declare an object property as a function, but also as an object, at the same time.
I have a JavaScript program that provides a simple API that sends AJAX requests to a server. My goal is trying to make this API as simple and human-readable as possible.
Basically, I'd like to make it possible to do this:
var app = new App();
app.get.client(123) // Get client ID 123
app.get.client.list() // Get an array of all clients
app.login('username', 'password') // Send credentials to log as username/password
app.login.as('John') // Login using credentials stored in a server-side constant
I doubt that's even possible as I've never anything like it, but I can't think of a more clear and human-readable way to lay out methods. Sure would be nice!
A function’s an object too!
app.get.client = function(id) {
// Get client by ID
};
app.get.client.list = function() {
// List them
};
works as you’d expect.
Personally, though, I’d find:
app.clients.byId(123)
app.clients
app.login('username', 'password')
app.loginAs('John')
more readable.
I am developing a fat client page based on Javascript that will allow users to carry out tasks outwith another web client application (Oracle Siebel).
The way the web page will be called from the browser will be by a simple window.open() call.
When this happens a URL will be passed which contains some parameters at the end which will change the functionality of the fat client page depending on what value they have.
e.g
userlevel=1 //normal user
userlevel=2 //advanced user
In an example full URL would be like so
www.mypage.com/index.htm?id=25215125%userlevel=2%context=full
However a user who wants to change their access only need to figure out that if they change their user level then they can change their access rights on this fat client page.
Yes, I know this is risky and before you ask why I am not using a server supported thin client with controls that cannot be altered by the user. I simply have to do it this way!
This system will be in a "trusted" environment and this users will have at best average IT skills.
So all I need to do is figure out a way to obfuscate/ scramble the URL parameters (if possible) and then decipher them at the fat client.
e.g.
www.mypage.com/index.htm?1sdf908ga90-821098650f8asdg098g0a98
I tested it out on the browser and no complaints so far so I guess I just need to develop a piece of logic to decipher it.
e.g. I could use MD5?
Any examples or ideas?
Thanks
Try Base64 encoding it. https://stackoverflow.com/a/4699739/1088652
That'll shorten it and obfuscate it, so that users can't just throw values in the URL.
Params integrity can be ensured with HMAC. You generate hash using secret key and all the params, you include this hash inside of URL, then at server side you generate hash using same params and compare values.
function generateSignature(array $params, $hmacKey)
{
// sort the array by key using SORT_STRING order
ksort($params, SORT_STRING);
$escapeFunc = function ($val) {
return str_replace(':', '\\:', str_replace('\\', '\\\\', $val));
};
// generate the signing data string
$signData = implode(':', array_map($escapeFunc, array_merge(array_keys($params), array_values($params))));
// base64-encode the binary result of the HMAC computation
$merchantSig = base64_encode(hash_hmac('sha256', $signData, pack("H*", $hmacKey), true));
return $merchantSig;
}