1.1.1 [Обязательно] Работа с HTML-тегами: ограничение или фильтрация значений переменных
innerHTML=
, outerHTML=
, document.write()
, document.writeln()
необходимо выполнять кодирование специальных символов (&, <, >, ", '
) или использовать безопасные альтернативы DOM API, такие как innerText=
.Пример небезопасного использования:
// Предположим, что params содержит пользовательский ввод, а text — узел DOM.
// Плохо: включение ненадёжного содержимого в HTML-теги.
const { user } = params;
// ...
text.innerHTML = `Follow @${user}`;
Пример безопасного использования:
innerHTML
:function htmlEncode(iStr) {
let sStr = iStr;
sStr = sStr.replace(/&/g, "&");
sStr = sStr.replace(/>/g, ">");
sStr = sStr.replace(/</g, "<");
sStr = sStr.replace(/"/g, """);
sStr = sStr.replace(/'/g, "'");
return sStr;
}
let { user } = params;
user = htmlEncode(user);
// ...
text.innerHTML = `Follow @${user}`;
innerHTML
:const { user } = params;
// ...
text.innerText = `Follow @${user}`;
1.1.2 [Обязательно] Безопасная работа с атрибутами HTML: ограничение или фильтрация передаваемых значений переменных
При использовании element.setAttribute(name, value)
и если значение первого параметра name
может контролироваться извне, применяйте белый список для ограничения допустимых операций над атрибутами.
При работе с a.href
, ifame.src
, form.action
, embed.src
, object.data
, link.href
, area.href
, input.formaction
и button.formaction
, если значение второго параметра value
может контролироваться извне, следуйте рекомендациям из раздела JavaScript-страницы, пункт 1.3.1, чтобы ограничить цели перенаправления страниц или ресурсы, которые будут включены.
Пример безопасной работы с атрибутами:
// Хорошо: добавляем внешний CSS перед setAttribute.
function addExternalCss(e) {
const t = document.createElement('link');
t.setAttribute('href', e),
t.setAttribute('rel', 'stylesheet'),
t.setAttribute('type', 'text/css'),
document.head.appendChild(t)
}
function validURL(sUrl) {
return !!((/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i).test(sUrl) || (/^[\w][\w/.\-_%]+$/i).test(sUrl) || (/^[/\\][^/\\]/i).test(sUrl));
}
let sUrl = "https://evil.com/1.css"
if (validURL(sUrl)) {
addExternalCss(sUrl);
}
1.2.1 [Обязательно] Ограничение или фильтрация переменных значений, передаваемых в небезопасные функции jQuery
.html()
, .append()
, .prepend()
, .wrap()
, .replaceWith()
, .wrapAll()
, .wrapInner()
, .after()
, .before()
, когда значения переменных могут быть контролируемыми извне.$()
и необходимости в версии jQuery 1.x (или ниже 1.12) или jQuery2.x (или ниже 2.2), рассмотрите возможность замены на более новую версию. Если замена невозможна, используйте кодирование специальных символов для значений параметров, содержащих специальные символы (&, <, >, ", '
).Пример небезопасной работы:
// Плохо: добавление ненадёжных данных после использования .after().
const { user } = params;
// ...
$("p").after(user);
Пример безопасной работы:
Выполняем кодирование специальных символов перед работой с .html()
:
function htmlEncode(iStr) {
let sStr = iStr;
sStr = sStr.replace(/&/g, "&");
sStr = sStr.replace(/>/g, ">");
sStr = sStr.replace(/</g, "<");
sStr = sStr.replace(/"/g, """);
sStr = sStr.replace(/'/g, "'");
return sStr;
}
// const user = params.user;
user = htmlEncode(user);
// ...
$("p").html(user);
Работая с .attr()
, который изменяет a.href
, ifame.src
, form.action
, embed.src
, object.data
, link.href
, area.href
, input.formaction
или button.formaction
, следуйте рекомендациям из пункта 1.3.1, чтобы ограничить цели перенаправления или ресурсы.
Используя .attr(attributeName, value)
, применяйте белый список, чтобы ограничить допустимые операции над атрибутами, когда значение attributeName
может быть контролируемым извне.
Применяя $.getScript(url [, success)
, убедитесь, что URL-адрес ресурса безопасен. 1.2.2 Обязательное ограничение/фильтрация переменных небезопасных функций Vue.js
v-html
, не следует использовать HTML-разметку, предоставленную пользователем. Если это необходимо для бизнеса, то сначала нужно выполнить фильтрацию ненадёжного содержимого с помощью библиотеки фильтрации форматированного текста.// Плохо: прямое рендеринг ненадёжного контента
<div v-html="userProvidedHtml"></div>
// Хорошо: использование библиотеки фильтрации форматированного текста для обработки ненадёжного содержимого перед рендерингом
<!-- Использование -->
<div v-xss-html="{'mode': 'whitelist', dirty: html, options: options}" ></div>
<!-- Конфигурация -->
<script>
new Vue({
el: "#app",
data: {
options: {
a: ["href", "title", "target", "class", "id"],
div: ["class", "id"],
span: ["class", "id"],
img: ["src", "alt"],
},
},
});
</script>
При использовании v-bind
для операций с a.href
, ifame.src
, form.action
, embed.src
, object.data
, link.href
, area.href
, input.formaction
и button.formaction
, необходимо убедиться, что серверная часть уже ссылается на JavaScript страницу класса спецификации 1.3.1 и ограничивает перенаправление на целевой адрес.
При использовании v-bind
для управления стилем, следует разрешать только внешние значения для определённых контролируемых свойств CSS.
// Плохо: v-bind разрешает внешние значения, настраиваемые свойства CSS и числовые значения
<a v-bind:href="sanitizedUrl" v-bind:style="userProvidedStyles">
click me
</a>
// Хорошо: v-bind позволяет внешним значениям контролировать только определённые свойства CSS
<a v-bind:href="sanitizedUrl" v-bind:style="{
color: userProvidedColor,
background: userProvidedBackground
}">
click me
</a>
1.3 Перенаправление страницы
1.3.1 Обязательное ограничение цели перенаправления
Использовать белый список для ограничения протокола префикса (по умолчанию разрешены только HTTP и HTTPS), домена (по умолчанию разрешено только корневое доменное имя компании) или указать фиксированное значение;
Это применимо к использованию функций методов, таких как location.href
, window.open()
, location.assign()
и location.replace()
, а также к присвоению или обновлению атрибутов HTML, таких как a.href
, iframe.src
, form.action
, embed.src
, object.data
, link.href
, area.href
, input.formaction
и button.formaction
.
// Плохо: перенаправление на внешний ненадёжный адрес
const sTargetUrl = getURLParam("target");
location.replace(sTargetUrl);
// Хорошо: ограничение белого списка для перенаправления адреса
function validURL(sUrl) {
return !!((/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i).test(sUrl) || (/^[\w][\w/.\-_%]+$/i).test(sUrl) || (/^[/\\][^/\\]/i).test(sUrl));
}
const sTargetUrl = getURLParam("target");
if (validURL(sTargetUrl)) {
location.replace(sTargetUrl);
}
// Хорошо: определение фиксированного значения для перенаправления
const sTargetUrl = "http://www.qq.com";
location.replace(sTargetUrl);
1.4 Анализ JSON и динамическое выполнение
1.4.1 Обязательный безопасный анализ JSON
JSON.parse()
для анализа строки JSON. В старых браузерах можно использовать безопасный Polyfill (обёртку) для замены eval()
.// Плохо: прямой вызов eval для анализа json
const sUserInput = getURLParam("json_val");
const jsonstr1 = `{"name":"a","company":"b","value":"${sUserInput}"}`;
const json1 = eval(`(${jsonstr1})`);
// Хорошо: использовать JSON.parse для анализа
const sUserInput = getURLparam("json_val");
JSON.parse(sUserInput, (k, v) => {
if (k === "") return v;
return v * 2;
});
// Хорошо: в старых браузерах использовать безопасный Polyfill для замены eval()
<script src="https://github.com/douglascrockford/JSON-js/blob/master/json2.js"></script>;
const sUserInput = getURLparam("json_val");
JSON.parse(sUserInput);
1.5 Междоменное взаимодействие
1.5.1 Обязательное безопасное междоменное взаимодействие на стороне клиента
document.domain
для понижения домена и обеспечения междоменного взаимодействия. Вместо этого следует использовать postMessage.1.5.2 Обязательная проверка источника при использовании postMessage
В функции обратного вызова события message следует сначала проверить источник с помощью event.origin
, прежде чем выполнять конкретные операции.
Во время проверки источника следует использовать оператор строгого равенства (===
) вместо indexOf()
.
// Плохо: использование indexOf для проверки Origin
window.addEventListener("message", (e) => {
if (~e.origin.indexOf("https://a.qq.com")) {
// ...
} else {
// ...
}
});
// Хорошо: при использовании postMessage ограничьте Origin и используйте === для сравнения
window.addEventListener("message", (e) => {
if (e.origin === "https://a.qq.com") {
// ...
}
});
``` **res.send({
data: txt,
});
});**
// Хорошо: проверка данных по типу, для ввода
const Router = require("express").Router();
const validator = require("validator");
Router.get("/email_with_validator", (req, res) => {
const txt = req.query.txt || "";
if (validator.isEmail(txt)) {
res.send({
data: txt
});
} else {
res.send({ err: 1 });
}
});
*Связанные уязвимости: защита от проникновения на глубину — функции повышения безопасности*
#### 1.2 Выполнение команды
**1.2.1 [Обязательно] использовать child_process для выполнения системных команд, ограничивая или проверяя содержимое команд и параметров**
- Применимые сценарии включают: `child_process.exec`, `child_process.execSync`, `child_process.spawn`, `child_process.spawnSync`, `child_process.execFile`, `child_process.execFileSync`
- Вызывая вышеуказанные функции, следует сначала рассмотреть ограничение диапазона, предоставляя выбор пользователю.
- Используя `child_process.exec` или `child_process.execSync`, если можно перечислить входные параметры или формат, необходимо ограничить белый список. Если невозможно перечислить команду или параметр, необходимо фильтровать или экранировать определённые символы, включая: ```|;&$()><`!```
- Используя `child_process.spawn` или `child_process.execFile`, следует проверять передаваемые команды и параметры в контролируемом списке.
```js
const Router = require('express').Router;
const validator = require('validator');
const { exec } = require('child_process');
// Плохо: не ограничено или не отфильтровано, команда выполняется напрямую
Router.get('/vul_cmd_inject', (req, res) => {
const txt = req.query.txt || 'echo 1';
exec(txt, (err, stdout, stderr) => {
if (err) { res.send({ err: 1 }) }
res.send({ stdout, stderr });
});
});
// Хорошо: использование белого списка для ограничения диапазона внешних исполняемых команд
Router.get('/not_vul_cmd_inject', (req, res) => {
const txt = req.query.txt || 'echo 1';
const phone = req.query.phone || '';
const cmdList = {
sendmsg: './sendmsg '
};
if (txt in cmdList && validator.isMobilePhone(phone)) {
exec(cmdList[txt] + phone, (err, stdout, stderr) => {
if (err) { res.send({ err: 1 }) };
res.send({stdout, stderr});
});
} else {
res.send({
err: 1,
tips: `you can use '${Object.keys(cmdList)}'`,
});
}
});
// Хорошо: фильтрация/экранирование определённых символов перед выполнением команды
Router.get('/not_vul_cmd_inject', (req, res) => {
const txt = req.query.txt || 'echo 1';
let phone = req.query.phone || '';
const cmdList = {
sendmsg: './sendmsg '
};
phone = phone.replace(/(\||;|&|\$\(|\(|\)|>|<|\`|!)/gi, '');
if (txt in cmdList) {
exec(cmdList[txt] + phone, (err, stdout, stderr) => {
if (err) { res.send({ err: 1 }) };
res.send({stdout, stderr});
});
} else {
res.send({
err: 1,
tips: `you can use '${Object.keys(cmdList)}'`,
});
}
});
Связанные уязвимости: высокий риск — выполнение произвольной команды*
#### 1.3 Операции с файлами
**1.3.1 [Обязательно] ограничивать диапазон операций с файлами по расширению**
- В соответствии с бизнес-требованиями используйте белый список для ограничения расширения файлов.
**1.3.2 [Обязательно] проверять и ограничивать диапазон путей к файлам**
- Следует установить фиксированный путь для загрузки и доступа к файлам. Если необходимо объединить внешние переменные значения, проверьте, содержат ли они `..` или `.`, что может привести к переходу по пути. Если они присутствуют, их следует отклонить.
- Когда используются методы функций модуля `fs`, такие как `fs.truncate`, `fs.truncateSync`, `fs.chown`, `fs.chownSync`, `fs.lchown`, `fs.lchownSync`, `fs.stat`, `fs.lchmodSync`, `fs.lstat`, `fs.statSync`, `fs.lstatSync`, `fs.readlink`, `fs.unlink`, `fs.unlinkSync`, `fs.rmdir`, `fs.rmdirSync`, `fs.mkdir`, `fs.mkdirSync`, `fs.readdir`, `fs.readdirSync`, `fs.openSync`, `fs.open`, `fs.createReadStream`, `fs.createWriteStream`, следует проверить первый параметр, который является частью пути, чтобы убедиться, что он не содержит символов перехода по пути `.` или `..`.
- Если используется метод `sendFile` фреймворка express, следует проверить первый параметр, являющийся частью пути, чтобы убедиться, что он не содержит символы перехода по пути `.` или `..`.
- Во время проверки следует использовать значение параметра пути до обработки модулем `path`, или определить, выходит ли обработанный путь за пределы текущего рабочего каталога. Методы, которые могут быть использованы, включают, но не ограничиваются: `path.resolve`, `path.join`, `path.normalize` и т. д.
```javascript
const fs = require('fs');
const path = require('path');
let filename = req.query.ufile;
let root = '/data/ufile';
// Плохо: путь к файлу не проверяется
fs.readFile(root + filename, (err, data) => {
if (err) {
return console.error(err);
}
console.log(`异步读取: ${data.toString()}`);
});
// Плохо: после обработки пути модулем path всё ещё существует риск перехода по пути
filename = path.join(root, filename);
if (filename.indexOf('..') < 0) {
fs.readFile(filename, (err, data) => {
if (err) {
return console.error(err);
}
console.log(data.toString());
});
};
// Хорошо: проверяется имя файла/путь, наличие символов перехода по пути . или ..
if (filename.indexOf('..') < 0) {
filename = path.join(root, filename);
fs.readFile(filename, (err, data) => {
if (err) {
return console.error(err);
}
console.log(data.toString());
});
};
1.3.3 [Обязательно] безопасно обрабатывать имена загружаемых файлов
Смысл текста: чувствительные ресурсы, такие как удостоверения личности и банковские карты, должны быть защищены с помощью мер безопасности, таких как шифрование, аутентификация и водяные знаки.
1.4.1
Смысл текста: необходимо ограничить доступ к сетевым ресурсам, фиксируя протоколы, доменные имена и пути доступа. Если это требуется для бизнеса, внешние стороны могут указывать адреса сетевых ресурсов, но доступ к внутренним частным адресам и доменам должен быть запрещён.
1.4.2
Смысл текста: рекомендуется использовать шифрование при запросе сетевых ресурсов.
1.5.1
Смысл текста: высокочувствительная информация, такая как пароли, ответы на вопросы безопасности и физиологические данные, не должна храниться или отображаться. Нефинансовая информация, включая номера кредитных карт и логи, также не должна сохраняться.
1.5.2
Смысл текста: общая чувствительная информация должна быть защищена от несанкционированного доступа путём маскировки. Например, номер удостоверения личности может показывать только первую и последнюю цифры, а номер мобильного телефона — скрывать шесть цифр в середине.
1.6.1
Смысл текста: тип содержимого ответа HTTP должен соответствовать фактическому содержанию ответа. Например, если ответ API является JSON, то заголовок Content-Type должен быть application/json.
1.6.2
Смысл текста: все интерфейсы и страницы должны включать заголовки безопасности X-Content-Type-Options: nosniff и X-Frame-Options. Значения этих заголовков должны быть выбраны в соответствии с требованиями безопасности.
1.7.1
Смысл текста: использование функций eval и new Function("input")() для создания функций запрещено. При использовании setInterval и setTimeout необходимо проверять передаваемые параметры.
1.8.1
Смысл текста: имена функций обратного вызова в JSONP-интерфейсах должны находиться в белом списке и содержать только буквы, цифры и подчёркивания.
1.8.2
Смысл текста: при использовании CORS необходимо строго фильтровать и проверять значение заголовка Origin. 1.10.2
Результаты хакерской атаки: обход нормальной логики, вход в чужой аккаунт без знания имени пользователя и пароля.
1.10.2
Перед операциями NoSQL необходимо проверять права доступа и роли.
Выполнение логики вставки, удаления, изменения и запроса данных NoSQL перед проверкой прав доступа.
// Пример использования express и MongoDB (mongoose) для удаления статьи
// Плохо: перед удалением статьи не выполняется проверка прав доступа.
app.post("/deleteArticle", (req, res) => {
db.articles.deleteOne({ article_id: req.body.article_id }, (err, users) => {
// TODO: обработать остальное
});
});
// Хорошо: перед выполнением операции NoSQL выполняется проверка прав доступа
app.post("/deleteArticle", (req, res) => {
checkPriviledge(ctx.uin, req.body.article_id);
db.articles.deleteOne({ article_id: req.body.article_id }, (err, users) => {
// TODO: обработать остальное
});
});
Связанные уязвимости: высокий риск — несанкционированные действия, высокий риск — внедрение NoSQL.
1.11.1
Обязательная безопасная серверная отрисовка Vue (Vue SSR).
Запрет прямого включения ненадёжного внешнего содержимого в выражение {{{ data }}}
.
Защита шаблонов от загрязнения.
// Плохо: пользовательский ввод заменяет шаблон
const app = new Vue({
template: appTemplate.replace("word", __USER_INPUT__),
});
renderer.renderToString(app);
Для уже отрисованного HTML-текста (после renderToString) если требуется снова вставить ненадёжный внешний ввод, необходимо сначала выполнить безопасную фильтрацию, см. 1.6.3.
// Плохо: после рендеринга HTML снова добавляется ненадёжный ввод
return new Promise(((resolve) => {
renderer.renderToString(component, (err, html) => {
let htmlOutput = html;
htmlOutput += `${__USER_INPUT__}`;
resolve(htmlOutput);
});
}));
1.11.2
Обязательное безопасное использование EJS, LoDash, Underscore для серверной отрисовки.
Использование функции рендера, защита шаблонов от загрязнения.
LoDash.Template:
// Плохо: ввод пользователя отправляется в шаблон
const compiled = _.template(`<b>${__USER_INPUT__}<%- value %></b>`);
compiled({ value: "hello" });
EJS:
// Плохо: ввод пользователя отправляется в шаблон
const ejs = require("ejs");
const people = ["geddy", "neil", "alex"];
const html = ejs.render(`<%= people.join(", "); %>${__USER_INPUT__}`, { people });
Ejs, LoDash и Underscore предоставляют HTML-шаблоны по умолчанию, подобные <%= data %>
. Хотя по умолчанию существует фильтрация, при написании HTML-шаблонов следует учитывать:
<base data-id = "<%= __USER_INPUT__ %>">
, необходимо использовать двойные кавычки.<script></script>
или в атрибут on* HTML, например <script>var id = <%= __USER_INPUT__ %></script>
, необходимо выполнить фильтрацию методом, описанным в 1.6.3, или с помощью белого списка. В этом случае фреймворк/компонент не будет работать.1.11.3
Обязательно выполнять безопасную фильтрацию при самостоятельном создании контейнера состояния и его последующей JSON.Stringify сериализации перед внедрением в HTML.
1.12.1
Ограничение перенаправления на целевые адреса.
Применимые сценарии включают:
<script>location.href=__Redirection_URL__</script>
на странице ответа.Используйте белый список для ограничения переадресации на основе протокола (по умолчанию разрешены только HTTP и HTTPS), домена (по умолчанию разрешено только доменное имя компании) или фиксированного значения.
Пример использования Express для успешного входа в систему и последующего перенаправления:
Плохо: адрес перенаправления не проверяется.
// Использование Express для успешной обработки входа
// Плохо: не проверяет адрес перенаправления
app.get("/login", (req, res) => {
// Если неавторизованный пользователь пытается получить доступ к другой странице, он перенаправляется на эту функцию для входа
// Сохраняем предыдущий адрес попытки доступа в параметре loginCallbackUrl, и после успешного входа перенаправляем на loginCallbackUrl:
const { loginCallbackUrl } = req.query;
if (loginCallbackUrl) {
res.redirect(loginCallbackUrl);
}
});
Хорошо: ограничение переадресации с использованием белого списка.
Функция isValidURL проверяет, соответствует ли URL определённому шаблону.
function isValidURL(sUrl) {
return !!((/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i).test(sUrl) || (/^[\w][\w/.\-_%]+$/i).test(sUrl) || (/^[/\\][^/\\]/i).test(sUrl));
}
app.get("/login", (req, res) => {
// Если неавторизованный пользователь пытается получить доступ к другой странице, он перенаправляется на эту функцию для входа
// Сохраняем предыдущий адрес попытки доступа в параметре loginCallbackUrl, и после успешного входа перенаправляем на loginCallbackUrl:
const { loginCallbackUrl } = req.query;
if (loginCallbackUrl && isValidUrl(loginCallbackUrl)) {
res.redirect(loginCallbackUrl);
}
});
Хорошо: ограничение перенаправления с использованием белого списка, реализация через возврат HTML.
Функция isValidURL проверяет, соответствует ли URL определённому шаблону. Затем используется encodeURI для фильтрации левых и правых угловых скобок и двойных кавычек, чтобы предотвратить выход из экранированных двойных кавычек.
function isValidURL(sUrl) {
return !!((/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i).test(sUrl) || (/^[\w][\w/.\-_%]+$/i).test(sUrl) || (/^[/\\][^/\\]/i).test(sUrl)));
}
app.get("/login", (req, res) => {
// Если неавторизованный пользователь пытается получить доступ к другой странице, он перенаправляется на эту функцию для входа
// Сохраняем предыдущий адрес попытки доступа в параметре loginCallbackUrl, и после успешного входа перенаправляем на loginCallbackUrl:
const { loginCallbackUrl } = req.query;
if (loginCallbackUrl && isValidUrl(loginCallbackUrl)) {
const redirectHtml = `<script>location.href = "${encodeURI(loginCallbackUrl)}";</script>`;
res.end(redirectHtml);
}
});
Связанная уязвимость: средний риск — произвольное перенаправление. 2.2.1. [Обязательно] Запуск Node.js от имени не-root пользователя
2.3.1. [Обязательно] Запрет жёсткого кодирования аутентификационных данных
AK/SK
, паролей к базе данных
, сертификатов закрытых ключей
и т. д.2.3.2. [Обязательно] Запрет жёсткого кодирования IP-конфигурации
Почему это необходимо?
Жёстко заданные IP-адреса могут привести к дополнительным работам при последующем масштабировании или изменении инфраструктуры, что может повлиять на надёжность системы.
2.3.3. [Обязательно] Запрет жёсткого кодирования чувствительной информации о сотрудниках
идентификаторами сотрудников
, номерами телефонов
, аккаунтами в WeChat/QQ
и др.Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )