1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-secguide

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
JavaScript安全指南.md 44 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 30.11.2024 00:55 2e0ef3e

JavaScript-страницы

I. Реализация кода

1.1 Безопасные операции с использованием нативного DOM API

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, "&amp;");
    sStr = sStr.replace(/>/g, "&gt;");
    sStr = sStr.replace(/</g, "&lt;");
    sStr = sStr.replace(/"/g, "&quot;");
    sStr = sStr.replace(/'/g, "&#39;");
    return sStr;
}

let { user } = params;
user = htmlEncode(user);
// ...
text.innerHTML = `Follow @${user}`;

// Хорошо: использование безопасного DOM API вместо 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, ограничивая цели перенаправления страниц или ресурсы, которые будут импортированы.

Пример:

// Хорошо: проверка URL перед добавлением внешнего CSS
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.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, "&amp;");
    sStr = sStr.replace(/>/g, "&gt;");
    sStr = sStr.replace(/</g, "&lt;");
    sStr = sStr.replace(/"/g, "&quot;");
    sStr = sStr.replace(/'/g, "&#39;");
    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 должно соответствовать рекомендациям из пункта JavaScript-страницы 1.3.1 для ограничения целей перенаправления ресурсов.

  • При использовании .attr(attributeName, value), если значение первого параметра attributeName может контролироваться извне, применяется белый список для ограничения разрешённых операций с атрибутами. 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 для анализа json
const sUserInput = getURLParam("json_val");
const jsonstr1 = `{"name":"a","company":"b","value":"${sUserInput}"}`;
const json1 = eval(`(${jsonstr1})`);

// Хорошо: использовать JSON.parse для анализа
const sUserInput = getURLPass("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 Обязательное использование безопасного способа междоменного взаимодействия

  • Для изолированных сеансов входа (таких как p_skey), которые связаны с конфиденциальной информацией пользователя (такой как WeChat Web, QQ Space, QQ Mailbox, общедоступная платформа), запрещено использовать document.domain для понижения домена. Вместо этого следует использовать postMessage.

1.5.2 Обязательное использование postMessage для ограничения Origin

  • В прослушивателе событий 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 и т. д.

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 [Обязательно] безопасно обрабатывать имена загружаемых файлов

  • Переименовать загруженный файл в случайную строку длиной более 16 символов для сохранения. 1.3.4

С обязательным применением мер по укреплению безопасности:

— чувствительные ресурсы, такие как удостоверения личности, банковские карты и т. п., должны быть защищены с помощью шифрования, аутентификации и водяных знаков;

— URL-адреса, указывающие на подобные файлы, должны обеспечивать непредсказуемость; при этом необходимо гарантировать отсутствие интерфейсов, которые могли бы массово отображать URL-адреса таких ресурсов;

— при доступе к чувствительным ресурсам должен осуществляться контроль разрешений; по умолчанию только пользователь может просматривать и работать со своими собственными чувствительными ресурсами.

Файлы типа изображений должны содержать бизнес-водяные знаки, чтобы указать, что эти изображения могут использоваться только в рамках текущего бизнеса.

1.4.1

Необходимо ограничить доступ к сетевым ресурсам по адресам:

— следует зафиксировать протоколы, доменные имена и пути доступа к сетевым ресурсам;

— если это требуется для бизнеса, внешние стороны могут указывать адреса сетевых ресурсов, но доступ к частным внутренним IP-адресам и доменам должен быть запрещён. Например, можно использовать RFC-определённые частные сети в качестве примера, а также добавлять собственные частные сетевые сегменты в список запрещённых.

10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.0/8

1.4.2

Рекомендуется шифровать передачу запросов к сетевым ресурсам:

— предпочтительно использовать протокол HTTPS для запросов к сетевым ресурсам.

Связанные уязвимости: высокий риск — SSRF, высокий риск — HTTP-перехват.

1.5.1

Обязательно не хранить и не показывать высокочувствительную информацию:

— пароли, ответы на вопросы безопасности, биометрические данные и другие формы аутентификационной информации не должны отображаться;

— в нефинансовых бизнесах номера кредитных карт CVV и журналы не должны храниться.

1.5.2

Обязательно маскировать общие чувствительные данные:

— показывать только первую и последнюю цифры номера удостоверения личности (например, 3**********1);

— скрывать шесть средних цифр номера мобильного телефона (например, 134************48);

— отображать только район для адресов работы или проживания;

— показывать только последние четыре цифры номера банковской карты (например, ********8639).

1.5.3

Рекомендовано выводить поля в соответствии с потребностями бизнеса:

— выводить только необходимые поля, избегая ненужного раскрытия личной информации пользователей;

— обрабатывать чувствительные пользовательские данные на сервере и затем выводить их, не допуская первоначального вывода на клиентскую сторону и последующей обработки через клиентский код.

Связанная уязвимость: высокий риск — раскрытие чувствительной информации пользователя.

1.6.1

Обязательно устанавливать правильный тип пакета ответа HTTP:

— тип содержимого в заголовке ответа Content-Type должен соответствовать фактическому типу данных ответа. Например, если ответ API является JSON, то заголовок ответа должен использовать application/json; если это XML, то следует установить text/xml.

1.6.2

Обязательно добавлять безопасные заголовки ответов:

— все интерфейсы и страницы должны включать заголовок X-Content-Type-Options: nosniff;

— все интерфейсы и страницы должны иметь заголовок X-Frame-Options, который может быть установлен в соответствии с требованиями безопасности, включая DENY, SAMEORIGIN или ALLOW-FROM origin. Для получения дополнительной информации рекомендуется обратиться к документации MDN (https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options);

— рекомендуется использовать компонент Helmet.

1.6.3

Перед внешним вводом данных в ответный HTML-код необходимо выполнить кодирование:

Сценарии Правила кодирования
Ввод между HTML-тегами Требуется кодировать следующие специальные символы: &, <, >, ", ', /. Пример: & → &amp;, < → <, > → >, " → ", ' → ', / → /
Входные данные в обычных атрибутах HTML (таких как href, src, style и т. д.), кроме событий on Необходимо кодировать данные. Кодируются все символы, кроме арабских цифр и букв. После кодирования ASCII-коды символов должны быть меньше 256. Формат после кодирования: &#xHH (начинается с &#x, HH обозначает шестнадцатеричное число символа)
Данные в коде JavaScript Требует кодирования JavaScript. Кодируются все символы, кроме арабских цифр и букв. После кодирования ASCII-коды символов должны быть меньше 256. Результат кодирования: \xHH (\x обозначает начало, HH — шестнадцатеричный код символа)
Данные в CSS Требуют кодирования CSS. Кодируются все символы, кроме арабских цифр и букв. После кодирования ASCII-коды символов должны быть меньше 256. Результат кодирования: \HH (\ обозначает начало, HH — шестнадцатеричный код символа)
Вводные данные в URL-атрибутах Требует URL-кодирования данных.

1.6.4

Запрещено показывать физические ресурсы, программную логику и другую чувствительную информацию в ответах:

— в производственной среде (официальной) при возникновении ошибок приложения ответы не должны раскрывать чувствительную информацию, такую как физические пути, внутренний исходный код программы, журналы отладки, внутренние учётные записи, внутренние IP-адреса и т. д.

Пример неправильного кода:

Access denied for user 'xxx'@'xx.xxx.xxx.162' (using password: NO)

1.6.5

Рекомендуется применять дополнительные меры безопасности:

— внедрить политику CSP, включая использование новейших строгих режимов с nonce-.

Правильный код:

// Хороший пример: использование компонента Helmet для безопасного конфигурирования заголовков ответов
const express = require("express");
const helmet = require("helmet");
const app = express();
app.use(helmet());

// Хороший пример: правильное конфигурирование Content-Type, добавление безопасных заголовков ответов и внедрение CSP
Router.get("/", (req, res) => {
    res.header("Content-Type", "application/json");
    res.header("X-Content-Type-Options", "nosniff");
    res.header("X-Frame-Options", "SAMEORIGIN");
    res.header("Content-Security-Policy", "script-src 'self'");
});

Связанное уязвимое место: средний риск — XSS, средний риск — перехват перехода.

1.7.1

Обязательное использование безопасных методов выполнения кода:

— запрещено использовать функцию eval;

— запрещено создавать функции с использованием new Function("input")();

— при использовании setInteval и setTimeout необходимо проверять передаваемые параметры.

Связанное уязвимое место: высокий риск — выполнение кода.

1.8.1

Обязательные ограничения для имён функций обратного вызова в JSONP:

— имена функций обратного вызова для интерфейсов JSONP должны быть ограничены фиксированным белым списком. Например, имена функций могут состоять только из букв, цифр и подчёркивания. Пример: [a-zA-Z0-9_-]+

1.8.2

Обязательная безопасная конфигурация CORS:

— используя CORS, необходимо строго фильтровать и проверять значение заголовка Origin. Можно использовать сравнение «точно равно» или строгие регулярные выражения для проверки. Пример: ^https://domain\.qq\.com$

// хороший пример: используется сравнение «точно равно», проверяется значение Origin
if (req.headers.origin === 'https://domain.qq.com') {
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
    res.setHeader('Access-control-allow-credentials', true);
}

Связанное уязвимое место: средний риск — XSS, средний риск — CSRF, средний риск — подделка межсайтовых запросов. Вот перевод текста на русский язык:

Проверка подлинности и авторизация

  • 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 Серверная отрисовка (SSR)

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 %>. Хотя по умолчанию существует фильтрация <%= data %>, при написании HTML-шаблонов следует учитывать следующее:

  1. При вводе данных пользователем в атрибут HTML-данных, например <base data-id = "<%= __USER_INPUT__ %>">, необходимо использовать двойные кавычки.
  2. Когда пользовательский ввод поступает в атрибут HTML <script></script> или on*, например <script>var id = <%= __USER_INPUT__ %></script>, необходимо выполнить фильтрацию методом белого списка или методом чёрного списка, фреймворк/компонент здесь не действует.

1.11.3 Обязательно: при самостоятельной реализации хранилища состояний и его сериализации JSON.Stringify перед внедрением в HTML необходимо выполнить безопасную фильтрацию

1.12 Перенаправление URL

1.12.1 Обязательно: ограничение перенаправления целевых адресов

Применяется к следующим сценариям:

  1. Использование кода ответа 30x и установка Location в заголовке для перенаправления.
  2. Печать <script>location.href=__Redirection_URL__</script> на странице ответа.

Используйте белый список для ограничения переадресации URL-адресов по протоколу (по умолчанию разрешены только HTTP и HTTPS), домену (по умолчанию разрешено только корневое доменное имя компании) или укажите фиксированное значение.

// Реализация успешного входа в систему с использованием express

// Плохо: не проверяется адрес перенаправления страницы
app.get("/login", (req, res) => {
    // Если неавторизованный пользователь пытается получить доступ к другой странице, он будет направлен на эту функцию обработки для входа в систему
  // Используйте параметр loginCallbackUrl для записи ранее посещённого URL, после успешного входа перенаправьте на loginCallbackUrl:
    const { loginCallbackUrl } = req.query;
    if (loginCallbackUrl) {
        res.redirect(loginCallbackUrl);
    }
});

// Хорошо: белый список ограничивает адрес переадресации
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 для записи ранее посещённого URL, после успешного входа перенаправьте на loginCallbackUrl:
    const { loginCallbackUrl } = req.query;
    if (loginCallbackUrl && isValidUrl(loginCallbackUrl)) {
        res.redirect(loginCallbackUrl);
    }
});

// Хорошо: белый список ограничивает адрес переадресации, реализуется через возврат html
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 для записи ранее посещённого URL, после успешного входа перенаправьте на loginCallbackUrl:
    const { loginCallbackUrl } = req.query;
    if (loginCallbackUrl && isValidUrl(loginCallbackUrl)) {
        // Использование encodeURI, экранирование угловых скобок и двойных кавычек, предотвращение побега двойных кавычек из упаковки
        const redirectHtml = `<script>location.href = "${encodeURI(loginCallbackUrl)}";</script>`;
        res.end(redirectHtml);
    }
});

Связанные уязвимости: средний риск — уязвимость произвольного перенаправления URL.

1.13 Cookie и состояние входа

1.13.1 Рекомендуется: добавить защиту http-only для хранения важных данных состояния входа в файлах cookie

Связанная уязвимость: защита безопасности — усиление функций безопасности. 2.2.1 Обязательно использовать не-root пользователя для запуска Node.js

2.3 Конфигурационная информация

2.3.1 Обязательно запретить жёсткое кодирование аутентификационных данных

  • Запретить жёстко кодировать в исходном коде AK/SK, данные базы, приватные ключи и другие конфигурационные данные.
  • Следует использовать систему конфигурации или систему управления ключами KMS.

2.3.2 Обязательно запретить жёсткое кодирование IP-конфигурации

  • Запретить жёстко кодировать IP-информацию в исходном коде.

Почему это необходимо?

Жёсткое кодирование IP может привести к дополнительным работам при последующем изменении или удалении машин, что влияет на надёжность системы.

2.3.3 Обязательно запретить жёсткое кодирование чувствительной информации о сотрудниках

  • Запретить включать чувствительную информацию о сотрудниках в исходный код, включая, но не ограничиваясь: ID сотрудника, номер телефона, номера WeChat/QQ и т. д.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-secguide.git
git@api.gitlife.ru:oschina-mirror/mirrors-secguide.git
oschina-mirror
mirrors-secguide
mirrors-secguide
main