Предполагая, что вы правильно проверяете предоставленные пользователем операнды и операторы, чтобы убедиться, что они содержат нужные вам данные вместо исполняемого кода javascript, вы можете объединить два операнда с оператором между ними и подать его на eval()
, чтобы получить он выполнен.
eval()
является опасным, поскольку он может выполнять любой код JavaScript. Пользователь может загружать исполняемый и, возможно, злонамеренный код JavaScript в качестве оператора, и eval()
будет его оценивать. Поэтому, когда вы выполняете конкатенацию, вы должны сделать это после проверки того, что операнд безопасен. Чтобы подчеркнуть этот момент, я напишу один из самых важных принципов компьютерной безопасности в больших шрифтах:
Весь вход злой, пока не будет доказано обратное.
Также обратите внимание, что eval()
вызывает интерпретатор JavaScript для интерпретации, компиляции и выполнения кода. Это медленно. Несмотря на то, что вы можете не заметить каких-либо заметных проблем с производительностью, если вы просто используете eval()
время от времени, вы можете заметить проблемы с производительностью, если вы часто вызываете eval()
, скажем, на каждом ключевом событии.
Учитывая эти недостатки eval()
, вы можете захотеть найти более аккуратное решение, подобное тому, которое было опубликовано Felix Kling. Тем не менее, это также возможно, чтобы решить эту проблему, используя eval()
безопасным способом, как показано ниже:
function compare(a, op, b)
{
// Check that we have two numbers and an operator fed as a string.
if (typeof a != 'number' || typeof b != 'number' || typeof op != 'string')
return
// Make sure that the string doesn't contain any executable code by checking
// it against a whitelist of allowed comparison operators.
if (['<', '>', '<=', '>=', '==', '!='].indexOf(op) == -1)
return
// If we have reached here, we are sure that a and b are two integers and
// op contains a valid comparison operator. It is now safe to concatenate
// them and make a JavaScript executable code.
if (eval(a + op + b))
doSomething();
}
Обратите внимание, что проверка ввода против белого списка почти всегда лучше, чем идея проверки его против черного списка. См. https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet#White_List_Input_Validation для краткого обсуждения этого вопроса.
Вот демонстрация этого решения: http://jsfiddle.net/YrQ4C/ (код также приводится ниже):
function doSomething()
{
alert('done something!')
}
function compare(a, op, b)
{
if (typeof a != 'number' || typeof b != 'number' || typeof op != 'string')
return
if (['<', '>', '<=', '>=', '==', '!='].indexOf(op) == -1)
return
if (eval(a + op + b))
doSomething();
}
// Positive test cases
compare(2, '<', 3)
compare(2, '<=', 3)
// Negative test cases
compare(2, '>', 3)
compare(2, '>=', 3)
// Attack tests
compare('alert(', '"attack!"', ')')
// Edit: Adding a new attack test case given by Jesse
// in the comments below. This function prevents this
// attack successfully because the whitelist validation
// for the second argument would fail.
compare(1, ';console.log("executed code");2==', 2)
Edit: Demo с тестом Джесси включены: http://jsfiddle.net/99eP2/
Нет, вы не можете сделать это в JS –