Skip to content

Latest commit

 

History

History
1163 lines (625 loc) · 136 KB

README.russian.md

File metadata and controls

1163 lines (625 loc) · 136 KB

Node.js Лучшие практики

Node.js Лучшие практики


85 items Last update: Oct 12, 2019 Updated for Node 12.12.0

nodepractices Следите за нами в Twitter! @nodepractices


Читайте на других языках: CNCN, BRBR, RURU (ESES, FRFR, HEHE, KRKR, TRTR and EUEU in progress! )


Создано и поддерживается нашим руководящим комитетом and соавторами

Последние лучшие практики и новости

  • ✅ Новая лучшая практика: 7.1: Не блокируйте цикл событий by Keith Holliday

  • 🇷🇺 Перевод на русский: Alex Ivanov недавно опубликовал Russian translation

  • Мы ищем авторов текстов: хотите помочь с примерами TypeScript? Пожалуйста, обратитесь, открыв вопрос.



Добро пожаловать! 3 вещи, которые вы должны знать в первую очередь:

1. На самом деле вы читаете десятки лучших статей Node.js - этот репозиторий представляет собой краткий обзор и список наиболее популярных материалов по рекомендациям Node.js, а также материалов, написанных здесь соавторами.

2. Это самая большая подборка, и она растет каждую неделю - в настоящее время представлено более 80 передовых практик, руководств по стилю и архитектурных советов. Новые выпуски и запросы на добавление, чтобы обновлять эту живую книгу, создаются каждый день. Мы бы хотели, чтобы вы внесли свой вклад здесь, будь то исправление ошибок в коде, помощь с переводами или предложение блестящих новых идей. Смотрите наши правила написания здесь.

3. У большинства лучших практик есть дополнительная информация - большинство маркеров включают в себя ссылку 🔗 Подробнее, которая расширяет практику с примерами кода, цитатами из выбранных блогов и дополнительной информацией.



Оглавление

  1. Практики структуры проекта (5)
  2. Практики обработки ошибок (11)
  3. Практики стиля кода (12)
  4. Тестирование и общие методы контроля качества (12)
  5. Переход к производственным практикам (18)
  6. Практики безопасности (25)
  7. Практики эффективности (2) (Работа в процессе️ ✍️)



1. Практики структуры проекта

✔ 1.1 Структурируйте свое решение по компонентам

TL;DR: Наихудшая ловушка для больших приложений -- поддержка огромной базы кода с сотнями зависимостей -- такой монолит замедляет разработчиков, поскольку они пытаются внедрить новые функции. Вместо этого разделите ваш код на компоненты, каждый получает свою собственную папку или выделенную кодовую базу, и убедитесь, что каждый модуль остается маленьким и простым. Посетите "Подробнее" ниже, чтобы увидеть примеры правильной структуры проекта.

Иначе: Когда разработчики, которые пишут новые функции, изо всех сил пытаются понять влияние своих изменений и боятся сломать другие зависимые компоненты, развертывания становятся медленнее и рискованнее. Также считается сложнее масштабировать, когда все бизнес-единицы не разделены.

🔗 Подробнее: Структурируйте свое решение по компонентам



✔ 1.2 Выделяйте ваши компоненты в отдельный слой, держите Express в его границах

TL;DR: Каждый компонент должен содержать "слои" -- выделенный объект для сети, логики и кода доступа к данным. Это не только четко разделяет задачи, но и значительно облегчает проверку и тестирование системы. Хотя это очень распространенный шаблон, разработчики API, как правило, смешивают слои, передавая объекты веб-слоя (Express req, res) в бизнес-логику и уровни данных - это делает ваше приложение зависимым и доступным только для Express.

Иначе: Приложение, которое смешивает веб-объекты с другими слоями, не может быть доступно для тестирования кода, заданий CRON и других вызовов в обход Express.

🔗 Подробнее: Выделяйте ваши компоненты в отдельный слой, держите Express в его границах



✔ 1.3 Оборачивайте общие утилиты в пакеты npm

TL;DR: В большом приложении, которое составляет большую кодовую базу, универсальные утилиты, такие как регистратор, модуль шифрования и т.п., должны быть обернуты вашим собственным кодом и представлены как частные пакеты npm. Это позволяет делиться ими между несколькими кодовыми базами и проектами.

Иначе: Вам придется изобрести собственный велосипед для развертывания и поддержания зависимостей.

🔗 Подробнее: Оборачивайте общие утилиты в пакеты npm



✔ 1.4 Разделяйте Express "приложение" и "сервер"

TL;DR: Избегайте неприятной привычки определять все приложение Express в одном огромном файле -- разделите определение "Экспресс" как минимум на два файла: декларация API (app.js) и сетевые задачи (www). Для еще лучшей структуры локализуйте объявление API в компонентах.

Иначе: Ваш API будет доступен для тестирования только через HTTP-вызовы (медленнее и намного сложнее создавать отчеты о покрытии). Скорее всего, вы не будете испытывать огромное удовольствие, если будете хранить сотни строк кода в одном файле.

🔗 Подробнее: Разделяйте Express "приложение" и "сервер"



✔ 1.5 Используйте безопасную и иерархическую конфигурацию с учетом среды

TL;DR: Идеальная и безупречная конфигурация должна обеспечивать (а) считывание ключей из файла И из переменной среды, (б) хранение секретов вне основной кодовой базы, (в) иерархическую структуру для облегчения поиска. Есть несколько пакетов, которые могут помочь поставить галочку в большинстве таких полей, как rc, nconf, config, and convict

Иначе: Невыполнение каких-либо требований к конфигурации приведет к срывам в работе разработчиков или devops-команды. А вероятно, и тех и других.

🔗 Подробнее: Используйте конфигурацию с учетом среды, безопасную и иерархическую конфигурацию




⬆ К началу

2. Практики обработки ошибок

✔ 2.1 Используйте Async-Await или обещания для обработки асинхронных ошибок

TL;DR: Обработка асинхронных ошибок в стиле обратного вызова, вероятно, является самым быстрым путем в ад (еще говорят "Callback Hell" или "The Pyramid of Doom"). Лучший подарок, который вы можете сделать своему коду, -- это использовать надежную библиотеку обещаний или async-await, что позволяет использовать более компактный и знакомый синтаксис кода, такой как try-catch.

Иначе: Callback-стиль Node.js, function(err, response), является многообещающим способом создания непригодного для использования кода из-за сочетания таких проблем, как обработка ошибок со случайным кодом, чрезмерные вложения и неудобные шаблоны проектирования.

🔗 Подробнее: Используйте Async-Await или обещания для асинхронной обработки ошибок



✔ 2.2 Используйте только встроенный объект Error

TL;DR: Многие выдают ошибки в виде строки или некоторого пользовательского типа -- это усложняет логику обработки ошибок и взаимодействие между модулями. Отклоните ли вы обещание, сгенерируете исключение или сгенерируете ошибку -- использование лишь встроенного объекта Error увеличит единообразие и предотвратит потерю информации.

Иначе: При вызове какого-либо компонента нельзя быть уверенным, какой тип ошибок приходит в ответ -- это значительно затрудняет правильную обработку ошибок. Хуже того, использование пользовательских типов для описания ошибок может привести к потере информации о критических ошибках, таких как трассировка стека!

🔗 Подробнее: Используйте только встроенный объект Error



✔ 2.3 Различайте операционные ошибки и ошибки программиста

TL;DR: Операционные ошибки (например, API получил неверный ввод) относятся к таким случаям, когда влияние ошибки полностью осознается и может быть обработано вдумчиво. Ошибка программиста (например, попытка прочитать undefined-переменную) относится к неизвестным ошибкам, которые требуют безопасного перезапуска приложения.

Иначе: Вы всегда можете перезапустить приложение, когда появляется ошибка, но зачем подводить ~5000 онлайн-пользователей из-за незначительной и прогнозируемой операционной ошибки? Обратное также не является идеальным -- отсутствие перезапуска приложения в случае, если возникла неизвестная проблема (ошибка программиста), может привести к непредсказуемому поведению. Понимание типа ошибки позволит действовать тактично и применять сбалансированный подход, основанный на предоставленном контексте.

🔗 Подробнее: Различайте операционные ошибки и ошибки программиста



✔ 2.4 Обрабатывате ошибки централизованно, а не в промежуточных обработчиках Express

TL;DR: Логика обработки ошибок, например, как уведомление по почте администратора или ведение журнала, должна быть инкапсулирована в выделенный и централизованный объект, который вызывается всеми точками входа (обработчиками Express, cron-задачами, юнит-тестами) при возникновении ошибки.

Иначе: Отсутствие обработки ошибок в едином месте приведет к дублированию кода и, возможно, к неверной обработке ошибок.

🔗 Подробнее: Обрабатывайте ошибки централизованно. Не в промежуточных слоях



✔ 2.5 Документирование ошибок API при использовании Swagger или GraphQL

TL;DR: Пусть ваши вызовы API знают, какие ошибки могут прийти взамен, чтобы они могли обрабатывать их вдумчиво без сбоев. Для API RESTful это обычно делается с помощью каркасов документации, таких как Swagger. Если вы используете GraphQL, вы также можете использовать свою схему и комментарии.

Иначе: Клиент API может принять решение о сбое и перезапуске только потому, что он получил ошибку, которую он не может понять. Примечание: вызывающим абонентом вашего API можете быть и вы сами (очень типично для микросервисной среды)

🔗 Подробнее: Документироваие ошибок API при использовании Swagger или GraphQL



✔ 2.6 Изящно выходите из процесса, когда в город приезжает незнакомец

TL;DR: При возникновении неизвестной ошибки (ошибка разработчика, см. рекомендацию 2.3) - существует неопределенность в отношении работоспособности приложения. Обычная практика предполагает осторожный перезапуск процесса с использованием инструмента управления процессами, такого как Forever или PM2.

Иначе: Когда происходит незнакомое исключение, некоторый объект может быть в неисправном состоянии (например, источник событий, который используется глобально и больше не генерирует события из-за некоторого внутреннего сбоя), и все будущие запросы могут давать сбой или вести себя безумно.

🔗 Подробнее: Изящно выходите из процесса, когда неизвестное случается



✔ 2.7 Используйте надежный регистратор для улучшения видимости ошибок

TL;DR: Набор развитых инструментов ведения журналов, таких как Winston, Bunyan, Log4js или Pino ускорит обнаружение и понимание ошибок. Так что забудьте о console.log.

Иначе: Сканирование через console.logs или вручную через грязный текстовый файл без запросов инструментов или приличного просмотра журнала может занять вас на работе до поздна.

🔗 Подробнее: Используйте проверенный логгер, чтобы увеличить видимость ошибок



✔ 2.8 Тестируйте потоки ошибок, используя ваш любимый тестовый фреймворк

TL;DR: Будь то профессиональный автоматический контроль качества или простое ручное тестирование разработчиком -- убедитесь, что ваш код не только удовлетворяет положительным сценариям, но также обрабатывает и возвращает правильные ошибки. Среды тестирования, такие как Mocha & Chai, могут легко справиться с этим (см. Примеры кода в "Gist popup").

Иначе: Без тестирования, будь то автоматически или вручную, вы не сможете полагаться на свой код для возврата правильных ошибок. Без значения ошибок -- нет обработки ошибок.

🔗 Подробнее: Тестируйте потоки ошибок с использованием вашей любимой тестовой среды



✔ 2.9 Находите ошибки и простои с использованием продуктов APM

TL;DR: Продукты для мониторинга и производительности (a.k.a APM) проактивно измеряют вашу кодовую базу или API, чтобы они могли автоматически подсвечивать ошибки, сбои и медленные части, которые вы пропустили.

Иначе: Вы можете потратить огромные усилия на измерение производительности и времени простоя API, возможно, вы никогда самостоятельно не узнаете, какие части кода в реально сценарии самые медленные, и как они влияют на UX.

🔗 Подробнее: Обнаружение ошибок и простоев с использованием продуктов APM



✔ 2.10 Ловите необработанные отказы от обещаний

TL;DR: Любое исключение, выданное в обещании, будет проглочено и отброшено, если разработчик не забудет явно обработать. Даже если ваш код подписан на process.uncaughtException! Преодолейте это, зарегистрировавшись на событие process.unhandledRejection.

Иначе: Ваши ошибки будут проглочены и не оставят следов. Не о чем беспокоиться!

🔗 Подробнее: Перехватывайте необработанные отказы от обещаний



✔ 2.11. Быстро проваливайтесь, проверяя аргументы, используя выделенную библиотеку

TL;DR: Это должно быть частью вашей лучшей практики Express - вводите данные API, чтобы избежать неприятных ошибок, которые потом будет намного сложнее отследить. Код проверки обычно утомителен, если вы не используете очень классную вспомогательную библиотеку, такую ​​как Joi.

Иначе: Учтите это -- ваша функция ожидает числовой аргумент "Скидка", который вызывающая сторона забывает передать, позже ваш код проверяет, если Скидка !=0 (сумма разрешенной скидки больше нуля), тогда она позволит пользователю пользоваться скидкой. О, Боже, какая неприятная ошибка! Видишь?

🔗 Подробнее: Быстро проваливайтесь, проверяя аргументы, используя выделенную библиотеку




⬆ Return to top

✔ 2.12 Для полноты стектрейсов всегда делайте await промисам прежде чем вернуть их из функции

TL;DR: Дабы не иметь пропусков вызовов в стектрейсах, при возвращении промисов всегда выполняйте return await. Если функция возвращает промис то эта функция обязана быть объявлена как async и явно разрешить промис через await прежде чем вернуть его

Иначе: Функция возвращающая неразрешенный промис будет отсутствовать в стектрейсе. Такие пропуски, вероятнее всего, усложнят процесс понимания пути возникновения ошибки, особенно если причина ненормального поведения как раз в отсутствующей функции и находится

🔗 Подробнее: возвращение промисов

⬆ К началу

3. Практики стиля кода

✔ 3.1 Используйте ESLint

TL;DR: ESLint является стандартом де-факто для проверки возможных ошибок кода и исправления стиля кода не только для выявления проблем с пробелами, но и для выявления серьезных анти-паттернов, которые выдают разработчики ошибки без классификации. Хотя ESLint может автоматически исправлять стили кода, другие инструменты, такие как prettier и beautify более эффективны при форматировании исправлений и работают совместно с ESLint.

Иначе: Разработчики сосредоточатся на утомительных проблемах с интервалами и шириной линии, и время может быть потрачено впустую на продумывание стиля кода проекта.

🔗 Подробнее: Использование ESLint и Prettier



✔ 3.2 Специальные плагины Node.js

TL;DR: Помимо стандартных правил ESLint, которые охватывают ванильный JavaScript, добавьте специальные плагины Node.js, например eslint-plugin-node, eslint-plugin-mocha и eslint-plugin-node-security.

Иначе: Многие неисправные шаблоны кода Node.js могут скрыться за радаром. Например, разработчикам могут потребоваться файлы (variableAsPath) с переменной, указанной в качестве пути, которая позволяет злоумышленникам выполнить любой сценарий JS. Линтеры Node.js могут обнаружить такие паттерны и заранее сообщить о проблеме.



##! 3.3 Начинайте кодовый блок фигурными скобками на той же линии.

TL;DR: Открывающие фигурные скобки блока кода должны находиться на той же строке, что и оператор открытия.

Пример кода

// Делайте так
function someFunction() {
  // code block
}

// Избегайте
function someFunction() {
  // code block
}

Иначе: Отклонение от этой передовой практики может привести к неожиданным результатам, как видно из топика StackOverflow ниже:

🔗 Подробнее: "Why do results vary based on curly brace placement?" (StackOverflow)



##! 3.4 Разделяйте свои выражения правильно

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

TL;DR: Используйте ESLint для получения информации о проблемах разделения. Prettier или Standardjs могут автоматически решить эти проблемы.

Иначе: Как видно из предыдущего раздела, интерпретатор JavaScript автоматически добавляет точку с запятой в конце оператора, если его нет, или считает, что оператор не завершен, где он должен, что может привести к нежелательным результатам. , Вы можете использовать присваивания и избегать немедленного вызова выражений функций для предотвращения большинства непредвиденных ошибок.

Пример кода

// Делайте так
function doThing() {
    // ...
}

doThing()

// Делайте так

const items = [1, 2, 3]
items.forEach(console.log)

// Избегайте — будет выброшена ошибка
const m = new Map()
const a = [1,2,3]
[...m.values()].forEach(console.log)
> [...m.values()].forEach(console.log)
>  ^^^
> SyntaxError: Unexpected token ...

// Избегайте — будет выброшена ошибка
const count = 2 // it tries to run 2(), but 2 is not a function
(function doSomething() {
  // do something amazing
}())
// ставим точку с запятой перед непосредственно вызванной функцией, после определения const сохраняем возвращаемое значение анонимной функции в переменной или вообще избегаем последовательного написания IIFE

🔗 Подробнее: "Semi ESLint rule" 🔗 Подробнее: "No unexpected multiline ESLint rule"



✔ 3.5 Назовите свои функции

TL;DR: Назовите все функции, включая замыкания и обратные вызовы. Избегайте анонимных функций. Это особенно полезно при профилировании приложения узла. Обозначение всех функций позволит вам легко понять, на что вы смотрите при проверке снимка памяти.

Иначе: Отладка производственных проблем с использованием дампа ядра (снимка памяти) может стать сложной, так как вы замечаете значительное потребление памяти анонимными функциями.



✔ 3.6 Используйте соглашения об именах переменных, констант, функций и классов

TL;DR: Используйте lowerCamelCase при именовании констант, переменных и функций и UpperCamelCase (также с большой буквы) при именовании классов. Это поможет вам легко различать простые переменные/функции и классы, которые требуют реализации. Используйте описательные имена, но старайтесь, чтобы они были короткими.

Иначе: Javascript -- это единственный язык в мире, который позволяет напрямую вызывать конструктор ("Class") без его инициализации. Следовательно, классы и конструкторы функций должщны различаться, начинаясь с UpperCamelCase.

Пример кода

// для класса мы используем UpperCamelCase
class SomeClassExample {}

// для константы мы используем служебное слово const и lowerCamelCase
const config = {
  key: "value",
};

// для переменных и функций мы используем lowerCamelCase
let someVariableExample = "value";
function doSomething() {}



✔ 3.7 Предпочитайте const, а не let. Забудьте var

TL;DR: Использование const означает, что как только переменная назначена, она не может быть переназначена. Предпочтение const поможет вам не поддаваться искушению использовать одну и ту же переменную для разных целей и сделает ваш код более понятным. Если переменная должна быть переназначена, например, в цикле for используйте let, чтобы объявить ее. Другим важным аспектом let является то, что переменная, объявленная с ее использованием, доступна только в той области блока, в которой она была определена. var является областью функции, а не областью блока и не должен использоваться в ES6 теперь, когда у вас есть const и let в вашем распоряжении.

Иначе: Отладка становится намного более громоздкой, когда необходимо следовать за переменной, которая часто изменяется.

🔗 Подробнее: JavaScript ES6+: var, let, or const?



✔ 3.8 Подключайте модули вначале, а не внутри функций

TL;DR: Подключайте модули в начале каждого файла, до и вне каких-либо функций. Эта простая рекомендация не только поможет вам легко и быстро определить зависимости файла прямо вверху, но и позволит избежать пары потенциальных проблем.

Иначе: Подключения выполняются синхронно с Node.js. Если они вызываются из функции, она может заблокировать обработку других запросов в более критическое время. Кроме того, если требуемый модуль или какая-либо из его собственных зависимостей выдает ошибку и приводит к сбою сервера, лучше узнать об этом как можно скорее, что может быть не так, если этот модуль требуется изнутри функции.



✔ 3.9 Подключайте модули по папкам, а не по файлам напрямую

TL;DR: При разработке модуля/библиотеки в папке поместите файл index.js, который раскрывает внутреннюю часть модуля, чтобы каждый потребитель проходил через него. Это служит "интерфейсом" для вашего модуля и облегчает будущие изменения, не нарушая контракт.

Иначе: Изменение внутренней структуры файлов или подписи может нарушить интерфейс с клиентами.

Пример кода

// Делайте так
module.exports.SMSProvider = require("./SMSProvider");
module.exports.SMSNumberResolver = require("./SMSNumberResolver");

// Избегайте
module.exports.SMSProvider = require("./SMSProvider/SMSProvider.js");
module.exports.SMSNumberResolver = require("./SMSNumberResolver/SMSNumberResolver.js");



✔ 3.10 Используйте оператор ===

TL;DR: Предпочитайте оператор строгого равенства === более слабому абстрактному оператору равенства ==. == сравнивает две переменные после преобразования их в общий тип. В === нет преобразования типов, и обе переменные должны иметь одинаковый тип, чтобы быть равными.

Иначе: Неравные переменные могут возвращать true при сравнении с оператором ==.

Пример кода

"" == "0"; // false
0 == ""; // true
0 == "0"; // true

false == "false"; // false
false == "0"; // true

false == undefined; // false
false == null; // false
null == undefined; // true

" \t\r\n " == 0; // true

Все приведенные выше операторы вернут false, если используются с ===.



✔ 3.11 Используйте Async Await, избегайте колбэков

TL;DR: Node 8 LTS теперь имеет полную поддержку Async-await. Это новый способ работы с асинхронным кодом, который заменяет обратные вызовы и обещания. Async-await не блокирует и делает асинхронный код синхронным. Лучший подарок, который вы можете дать своему коду, -- это использовать async-await, который обеспечивает гораздо более компактный и знакомый синтаксис кода, такой как try-catch.

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

🔗 Подробнее: Guide to async await 1.0



✔ 3.12 Используйте стрелочные функции (=>)

TL;DR: Хотя рекомендуется использовать async-await и избегать параметров функций при работе со старыми API, которые принимают обещания или обратные вызовы -- стрелочные функции делают структуру кода более компактной и поддерживают лексический контекст корневой функции (т.к. this)

Иначе: Более длинный код (в функциях ES5) более подвержен ошибкам и неудобен для чтения.

🔗 Подробнее: It’s Time to Embrace Arrow Functions




⬆ К началу

4. Тестирование и общие методы контроля качества

✔ 4.1 Как минимум, напишите тестирование API (компонента)

TL;DR: Большинство проектов просто не имеют автоматического тестирования из-за коротких сроков, или, как часто бывает, выхода из-под контроля и забрасывания "проекта тестирования". По этой причине расставьте приоритеты и начните с тестирования API, который является самым простым способом написания, и обеспечивает больший охват, чем модульное тестирование. Вы даже можете создавать тесты API без кода, используя такие инструменты, как Postman. После этого, если у вас будет больше ресурсов и времени, перейдите к расширенным типам тестов, таким как модульное тестирование, тестирование БД, тестирование производительности и т.д.

Иначе: Вы можете потратить долгие дни на написание модульных тестов, чтобы узнать, что вы получили только 20% покрытия системы.



✔ 4.2 Включите 3 части в каждое название теста

TL;DR: Заставьте тест говорить на уровне требований, чтобы он был понятен инженерам и разработчикам QA, которые не знакомы с внутренними компонентами кода. Укажите в названии теста, что тестируется (тестируемая единица), при каких обстоятельствах и каков ожидаемый результат.

Иначе: Развертывание только что прошло, тест под названием "Добавить продукт" не прошел. Это говорит вам, что именно работает со сбоями?

🔗 Подробнее: Включите 3 части в каждое название теста



✔ 4.3 Структурные тесты AAA-подходом

TL;DR: Структурируйте свои тесты с тремя хорошо разделенными секциями: Arrange, Act & Assert AAA). Первая часть включает в себя настройку теста, затем выполнение тестируемого модуля и, наконец, этап подтверждения. Следование этой структуре гарантирует, что читатель не тратит мозговые ЦП на понимание плана тестирования.

Иначе: Не только вы тратите долгие ежедневные часы на понимание основного кода, теперь и то, что должно было быть простой частью дня (тестирование), напрягает ваш мозг.

🔗 Read More: Structure tests by the AAA pattern



✔ 4.4. Обнаружение проблем с кодом с помощью линтера

TL;DR: Используйте линтер кода для проверки базового качества и раннего обнаружения анти-паттернов. Запустите его перед любым тестом и добавьте его в качестве git-ловушки перед фиксацией, чтобы минимизировать время, необходимое для проверки и исправления любой проблемы. Также проверьте Раздел 3 в разделе Практика стиля кода.

Иначе: Вы можете не заметить передачу некоторого анти-паттерна и возможного уязвимого кода в вашу производственную среду.



✔ 4.5 Избегайте глобальных тестовых приспособлений и параметров, добавляйте данные для каждого теста

TL;DR: Чтобы предотвратить связывание тестов и легко рассуждать о последовательности тестов, каждый тест должен добавлять и воздействовать на свой собственный набор строк БД. Всякий раз, когда тест должен получить или предположить существование некоторых данных БД, он должен явно добавить эти данные и избегать изменения любых других записей.

Иначе: Рассмотрим сценарий, в котором развертывание прерывается из-за неудачных тестов, теперь команда собирается потратить драгоценное время на исследование, которое заканчивается печальным выводом: система работает хорошо, однако тесты мешают друг другу и нарушают сборку.

🔗 Подробнее: Избегайте глобальных тестовых приспособлений и параметров, добавляйте данные для каждого теста



✔ 4.6 Постоянно проверяйте уязвимые зависимости

TL;DR: Даже самые уважаемые зависимости, такие как Express, имеют известные уязвимости. Это можно легко приручить, используя открытые и коммерческие инструменты, такие как 🔗 npm audit и 🔗snyk.io, которые могут быть вызванны из вашего CI при каждой сборке.

Иначе: Для обеспечения чистоты вашего кода от уязвимостей без использования специальных инструментов потребуется постоянно следить за публикациями в Интернете о новых угрозах. Довольно утомительно.



✔ 4.7 Помечайте свои тесты

TL;DR: Различные тесты должны выполняться в разных сценариях: быстрое раскуривание, без ввода-вывода, тесты должны выполняться, когда разработчик сохраняет или фиксирует файл, полные сквозные тесты обычно выполняются, когда отправлен новый запрос в репозиторий и т.д. Этого можно достичь, помечая тесты ключевыми словами, такими как #cold, #api, #sanity, чтобы вы могли использовать свой тестовый набор и вызывать нужное подмножество. Например, вот как вы бы вызывали только группу тестов на работоспособность с Mocha: mocha --grep 'sanity'.

Иначе: Запуск всех тестов, включая тесты, которые выполняют десятки запросов к БД, каждый раз, когда разработчик вносит небольшие изменения, может быть очень медленным и держит разработчиков подальше от запуска тестов.



✔ 4.8 Проверьте ваше покрытие тестов, он помогает определить неправильные тестовые шаблоны

TL;DR: Инструменты покрытия кода тестами, такие как Istanbul/NYC хороши по 3 причинам: они предоставляются бесплатно (никаких усилий не требуется, чтобы воспользоваться этими отчетами) это помогает выявить уменьшение охвата тестирования и, наконец, что не менее важно, подчеркивает несоответствия тестирования: просматривая цветные отчеты о покрытии кода, вы можете заметить, например, области кода, которые никогда не тестируются, как предложения catch (то есть тесты вызывают только счастливые пути, а не то, как приложение ведет себя на ошибках). Установите его на сбой сборки, если охват падает ниже определенного порога.

Иначе: Там не будет никакой автоматической метрики, сообщающей вам, когда большая часть вашего кода не покрыта тестированием.



✔ 4.9 Проверяйте устаревшие пакеты

TL;DR: Используйте предпочитаемый вами инструмент (например, 'npm outdated' или npm-check-updates) для обнаружения установленных пакетов, которые устарели, внедрите эту проверку в конвейер CI и приводите к сбою сборки в серьезном сценарии. Например, серьезный сценарий может быть, когда установленный пакет имеет 5 исправлений патча позади (например, локальная версия 1.3.1 и версия репозитория 1.3.8) или помечен автором как устаревший -- убейте сборку и предотвратите развертывание этой версии.

Иначе: Ваше производство будет запускать пакеты, которые были явно помечены их автором как рискованные.



✔ 4.10 Используйте docker-compose для тестирования e2e

TL;DR: Сквозное (e2e) тестирование, включает в себя живые данные, которые раньше были самым слабым звеном процесса CI, поскольку оно зависит от множества тяжелых сервисов, таких как DB. Docker-compose превращает эту проблему в простоту, создавая производственную среду, используя простой текстовый файл и простые команды. Это позволяет создавать все зависимые сервисы, БД и изолированную сеть для тестирования e2e. Наконец, что не менее важно, он может поддерживать среду без состояния, которая вызывается перед каждым набором тестов и умирает сразу после.

Иначе: Без docker-compose команды должны поддерживать базу данных тестирования для каждой среды тестирования, включая машины разработчиков, синхронизировать все эти базы данных, чтобы результаты тестирования не менялись в зависимости от среды.



✔ 4.11 Производите рефакторинг регулярно с использованием инструментов статического анализа

TL;DR: Использование инструментов статического анализа помогает, предоставляя объективные способы улучшить качество кода и поддерживая его в обслуживании. Вы можете добавить инструменты статического анализа в свою сборку CI, чтобы она вызывала сбой при обнаружении запахов кода. Его основные преимущества при использовании простого линтинга -- это возможность проверять качество в контексте нескольких файлов (например, обнаруживать дубликаты), выполнять расширенный анализ (например, сложность кода) и следить за историей и развитием проблем с кодом. Два примера инструментов, которые вы можете использовать: Sonarqube (2600+ stars) и Code Climate (1500+ stars).

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

🔗 Подробнее: Рефакторинг



✔ 4.12 Тщательно выбирайте свою CI-платформу (Jenkins или CircleCI или Travis или остальной мир)

TL;DR: Ваша платформа непрерывной интеграции (CICD) будет содержать все инструменты качества (такие как тестирование и линтинг), поэтому она должна поставляться с динамичной экосистемой плагинов. Jenkins раньше использовался по умолчанию для многих проектов, поскольку у него самое большое сообщество и очень мощная платформа по цене сложной установки, которая требует крутой кривой обучения. В настоящее время стало намного проще настроить CI-решение с использованием инструментов SaaS, таких как CircleCI и других. Эти инструменты позволяют создать гибкий конвейер CI без необходимости управлять всей инфраструктурой. В конце концов, это компромисс между надежностью и скоростью -- тщательно выбирайте свою сторону.

Иначе: Выбор какого-либо нишевого поставщика может заблокировать вас, когда вам понадобится дополнительная настройка. С другой стороны, работа с Jenkins может потратить драгоценное время на настройку инфраструктуры.

🔗 Подробнее: Тщательно выбирайте платформу CI




⬆ К началу

5. Переход к производственным практикам

✔ 5.1. Мониторинг!

TL;DR: Мониторинг -- это игра для выявления проблем до того, как их решат клиенты, очевидно, этому следует придать беспрецедентную важность. Рынок перегружен предложениями, поэтому подумайте о том, чтобы начать с определения основных метрик, которым вы должны следовать (мои предложения в подробностях), затем перейти к дополнительным необычным функциям и выбрать решение, которое помечает все поля. Нажмите "Подробнее" ниже для обзора решений.

Иначе: Отказ === разочарованные клиенты. Просто.

🔗 Подробнее: Мониторинг!



✔ 5.2. Увеличьте прозрачность, используя умную регистрацию

TL;DR: Журналы могут быть тупым хранилищем операторов отладки или активатором красивой панели инструментов, которая рассказывает историю вашего приложения. Планируйте свою платформу ведения журналов с первого дня: как журналы собираются, хранятся и анализируются, чтобы обеспечить возможность извлечения желаемой информации (например, частоты ошибок, всей транзакции через службы и серверы и т.д.).

Иначе: Вы в конечном итоге получаете черный ящик, о котором трудно подумать, затем вы начинаете переписывать все операторы регистрации, чтобы добавить дополнительную информацию.

🔗 Подробнее: Сделайте ваше приложение прозрачным, используя умные логи



✔ 5.3. Делегируйте все возможное (например, gzip, SSL) обратному прокси

TL;DR: Node ужасно плохо справляется с задачами, интенсивно использующими процессор, такими как архивирование, SSL и т.д. Вместо этого вы должны использовать "настоящие" сервисы промежуточного ПО, такие как nginx, HAproxy или сервисы облачного вендора.

Иначе: Ваш бедный одиночный поток будет занят выполнением инфраструктурных задач вместо того, чтобы работать с ядром приложения, и производительность будет соответственно снижаться.

🔗 Подробнее: Делегируйте все возможное (например, gzip, SSL) обратному прокси



✔ 5.4. Блокируйте зависимости

TL;DR: Ваш код должен быть одинаковым во всех средах, но удивительно, что npm по умолчанию позволяет смещать зависимости между средами -- при установке пакетов в различных средах он пытается получить последнюю версию пакета исправлений. Преодолеть это можно с помощью файлов конфигурации npm, .npmrc, которые сообщают каждой среде сохранять точную (не последнюю) версию каждого пакета. В качестве альтернативы, для более тонкого контроля используйте npm shrinkwrap. * Обновление: начиная с NPM5, зависимости по умолчанию заблокированы. Новый менеджер пакетов Yarn также предоставил нам покрытие по умолчанию.

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

🔗 Подробнее: Блокируйте зависимости



✔ 5.5. Защитите время безотказной работы, используя правильный инструмент

TL;DR: Процесс должен продолжаться и перезапускаться при сбоях. Для простых сценариев может быть достаточно инструментов управления процессами, таких как PM2, но в современном "докеризованном" мире следует также рассмотреть инструменты управления кластерами.

Иначе: Запуск десятков экземпляров без четкой стратегии и слишком большого количества инструментов (управление кластером, docker, PM2) может привести к хаосу DevOps.

🔗 Подробнее: Защищайте и перезапускайте свой процесс в случае неудачи (используя правильный инструмент)



✔ 5.6. Используйте все ядра процессора

TL;DR: В своей базовой форме приложение Node работает на одном ядре ЦП, в то время как все остальные не работают. Ваша обязанность -- копировать процесс Node и использовать все процессоры. Для небольших и средних приложений вы можете использовать Node Cluster или PM2. Для более крупного приложения рассмотрите возможность репликации процесса с использованием некоторого кластера Docker (например, K8S, ECS) или сценариев развертывания, основанных на системе инициализации Linux (например, systemd).

Иначе: Ваше приложение, скорее всего, будет использовать только 25% доступных ресурсов (!) Или даже меньше. Обратите внимание, что типичный сервер имеет 4 или более ядер ЦП, для простого развертывания Node.js используется только 1 (даже при использовании сервисов PaaS, таких как AWS beanstalk!)

🔗 Подробнее: Используйте все ядра процессора



✔ 5.7. Создавайте "конечную точку обслуживания"

TL;DR: Предоставьте набор информации, связанный с системой, например, использование памяти и REPL, и т.д. в защищенном API. Хотя настоятельно рекомендуется полагаться на стандартные инструменты и инструменты для боевых испытаний, некоторые ценные сведения и операции легче выполнять с помощью кода.

Иначе: Вы обнаружите, что выполняете много "диагностических развертываний" -- отправка кода в производство только для извлечения некоторой информации в диагностических целях.

🔗 Подробнее: Создавайте конечную точку обслуживания



✔ 5.8. Обнаружение ошибок и простоев с использованием продуктов APM

TL;DR: Продукты для мониторинга приложений и производительности (a.k.a APM) проактивно измеряют кодовую базу и API, поэтому они могут автоматически выходить за рамки традиционного мониторинга и измерять общее взаимодействие пользователей между службами и уровнями. Например, некоторые продукты APM могут выделять транзакцию, которая загружается слишком медленно на стороне конечного пользователя, предлагая при этом основную причину.

Иначе: Вы можете потратить огромные усилия на измерение производительности и времени простоя API, возможно, вы никогда не узнаете, какие ваши самые медленные части кода в реальном сценарии и как они влияют на UX.

🔗 Подробнее: Уверенный пользовательский опыт с продуктами APM



✔ 5.9. Делайте ваш код готовым к работе

TL;DR: Код с конечной целью, план производства с первого дня. Это звучит немного расплывчато, поэтому я собрал несколько советов по разработке, которые тесно связаны с техническим обслуживанием производства (см. ниже)

Иначе: Чемпион мира по IT/DevOps не спасет плохо написанную систему.

🔗 Подробнее: Делайте ваш код готовым к работе



✔ 5.10. Измеряйте и защищайте использование памяти

TL;DR: Node.js имеет противоречивые отношения с памятью: движок v8 имеет мягкие ограничения на использование памяти (1,4 ГБ), и существуют известные пути утечки памяти в коде Node -- таким образом, наблюдение за процессной памятью Node является обязательным. В небольших приложениях вы можете периодически измерять память с помощью команд оболочки, но в средних и больших приложениях стоит подумать о том, чтобы превратить ваши часы памяти в надежную систему мониторинга.

Иначе: Ваша память процесса может пропускать сотни мегабайт в день, как это было в Walmart.

🔗 Подробнее: Измеряйте и защищайте использование памяти



✔ 5.11. Получайте ваши внешние ресурсы вне Node

TL;DR: Обслуживание внешнего интерфейса с помощью специального промежуточного программного обеспечения (nginx, S3, CDN), потому что производительность Node действительно ухудшается при работе со многими статическими файлами из-за его однопоточной модели.

Иначе: Ваш единственный поток Node будет занят потоковой передачей сотен файлов html/images/angular/react вместо того, чтобы выделять все свои ресурсы на задачи, для которой он был создан -- обслуживание динамического контента.

🔗 Подробнее: Получайте ваши внешние ресурсы вне Node



✔ 5.12. Не прописывайтесь на постоянку, убивайте свои серверы почти каждый день

TL;DR: Храните любые типы данных (например, пользовательские сеансы, кэш, загруженные файлы) во внешних хранилищах данных. Попробуйте периодически "убивать" свои серверы или использовать "безсерверную" платформу (например, AWS Lambda), которая явно обеспечивает поведение без сохранения состояния.

Иначе: Сбой на данном сервере приведет к простою приложения, а не просто к гибели неисправного компьютера. Более того, гибкость масштабирования станет более сложной из-за зависимости от конкретного сервера.

🔗 Подробнее: Не прописывайтесь на постоянку, убивайте свои серверы почти каждый день



✔ 5.13. Используйте инструменты, которые автоматически обнаруживают уязвимости

TL;DR: Даже самые уважаемые зависимости, такие как Express, имеют известные уязвимости (время от времени), которые могут подвергать систему риску. Это можно легко укротить, используя общественные и коммерческие инструменты, которые постоянно проверяют уязвимости и предупреждают (локально или на GitHub), некоторые могут даже сразу же их исправлять.

Иначе: Для обеспечения чистоты кода от уязвимостей без использования специальных инструментов вам потребуется постоянно следить за публикациями в Интернете о новых угрозах. Довольно утомительно.

🔗 Подробнее: Используйте инструменты, которые автоматически обнаруживают уязвимые зависимости



✔ 5.14. Назначьте идентификатор транзакции для каждого события журнала

TL;DR: Назначьте один и тот же идентификатор, идентификатор транзакции: {некоторое значение}, каждой записи журнала в рамках одного запроса. Затем при проверке ошибок в журналах легко сделать вывод о том, что происходило до и после. К сожалению, этого нелегко добиться в Node из-за его асинхронной природы, см. Пример кодов внутри.

Иначе: Глядя на журнал ошибок производства без контекста -- что произошло раньше -- становится намного сложнее и медленнее рассуждать о проблеме.

🔗 Read More: Назначьте "TransactionId" для каждого вхождения журнала логирования



✔ 5.15. Устанавливайте NODE_ENV=production

TL;DR: Установите для переменной среды NODE_ENV значение "production" или "development", чтобы указать, должны ли активироваться производственные оптимизации -- многие пакеты npm определяют текущую среду и оптимизируют свой код для того или иного выпуска.

Иначе: Пропуск этого простого свойства может значительно снизить производительность. Например, при использовании Express для рендеринга на стороне сервера пропуск NODE_ENV замедляет его в три раза!

🔗 Подробнее: Устанавливайте NODE_ENV=production



✔ 5.16. Проектируйте автоматизированные, атомарные и без простоев на развертывание

TL;DR: Исследования показывают, что команды, которые выполняют много развертываний, снижают вероятность серьезных производственных проблем. Быстрое и автоматическое развертывание, не требующее рискованных ручных операций и простоев служб, значительно улучшает процесс развертывания. Вероятно, вам следует добиться этого, используя Docker в сочетании с инструментами CI, поскольку они стали отраслевым стандартом для упрощенного развертывания.

Иначе: Длительные развертывания -> простои производства и ошибки, связанные с персоналом -> команда неуверенная в развертывании -> меньше развертываний и функций.



✔ 5.17. Используйте LTS-релиз Node.js

TL;DR: Убедитесь, что вы используете LTS-версию Node.js для получения критических исправлений ошибок, обновлений безопасности и улучшений производительности.

Иначе: Недавно обнаруженные ошибки или уязвимости могут быть использованы для эксплуатации приложения, работающего в производственной среде, и ваше приложение может стать неподдерживаемым различными модулями и усложнить поддержку.

🔗 Подробнее: Используйте LTS-релиз Node.js в производстве



✔ 5.18. Не маршрутизируйте журналы в приложении

TL;DR: Места назначения журналов не должны жестко кодироваться разработчиками в коде приложения, но вместо этого должны определяться средой исполнения, в которой выполняется приложение. Разработчики должны записывать журналы в stdout с помощью утилиты logger и затем позвольте среде выполнения (контейнер, сервер и т.д.) направить поток stdout в соответствующее место назначения (т.е. Splunk, Graylog, ElasticSearch и т.д.).

Иначе: Маршрутизация журналов обработки приложения === трудности масштабирования, потеря журналов, плохое разделение задач

🔗 Подробнее: Код вашего приложения не должен обрабатывать журналы маршрутизации




⬆ К началу

6. Практики безопасности

54 items

✔ 6.1. Пользуйтесь правилами безопасности линтера

TL;DR: Используйте связанные с безопасностью плагины для линтера, такие как eslint-plugin-security, чтобы обнаруживать уязвимости и проблемы безопасности на ранней стадии, насколько возможно, желательно, пока они кодируются. Это может помочь выявить слабые места безопасности, такие как использование eval, вызов дочернего процесса или импорт модуля со строковым литералом (например, пользовательский ввод). Нажмите "Подробнее" ниже, чтобы увидеть примеры кода, которые попадут под стражу безопасности.

Иначе: То, что могло быть простым недостатком безопасности во время разработки, становится основной проблемой в производстве. Кроме того, проект может не следовать согласованным методам обеспечения безопасности кода, что приводит к появлению уязвимостей или секретных секретов, передаваемых в удаленные репозитории.

🔗 Подробнее: Пользуйтесь правилами безопасности линтера



✔ 6.2. Ограничивайте одновременные запросы с использованием промежуточного программного обеспечения

TL;DR: DOS-атаки очень популярны и относительно просты в проведении. Внедрите ограничение скорости с помощью внешней службы, такой как облачные балансировщики нагрузки, облачные брандмауэры, nginx, пакет rate-limiter-flexible или (для небольших и менее критичных приложений) промежуточное программное обеспечение, ограничивающее скорость (например, express-rate-limit)

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

🔗 Подробнее: Ограничивайте одновременные запросы с использованием балансировщика или промежуточного программного обеспечения



✔ 6.3 Извлекайте секреты из конфигурационных файлов или используйте пакеты для их шифрования

TL;DR: Никогда не храните секреты в виде простого текста в файлах конфигурации или исходном коде. Вместо этого используйте системы секретного управления, такие как продукты Vault, секреты Kubernetes/Docker или переменные среды. В крайнем случае, секреты, хранящиеся в системе контроля версий, должны быть зашифрованы и обработаны (переходящие ключи, срок действия, аудит и т.д.). Используйте фиксаторы pre-commit/push, чтобы предотвратить случайную передачу секретов.

Иначе: Контроль источников, даже для частных репозиториев, может быть ошибочно обнародован, после чего все секреты будут раскрыты. Доступ к управлению исходным кодом для внешней стороны непреднамеренно предоставит доступ к связанным системам (базам данных, API, службам и т.д.).

🔗 Подробнее: Извлекайте секреты из конфигурационных файлов или используйте пакет npm, который их шифрует



✔ 6.4. Предотвращайте уязвимости при внедрении запросов с помощью библиотек ORM/ODM

TL;DR: Чтобы предотвратить инъекцию SQL/NoSQL и другие злонамеренные атаки, всегда используйте ORM/ODM или библиотеку базы данных, которая экранирует данные или поддерживает именованные или индексированные параметризованные запросы, а также проверяет пользовательский ввод на ожидаемые типы. Никогда не используйте строки шаблонов JavaScript или конкатенацию строк для ввода значений в запросы, поскольку это открывает для вашего приложения широкий спектр уязвимостей. Все авторитетные библиотеки доступа к данным Node.js (например, Sequelize, Knex, mongoose) имеют встроенную защиту от инъекционных атак.

Иначе: Непроверенный или недеанонимизированный пользовательский ввод может привести к внедрению оператора при работе с MongoDB для NoSQL, а отсутствие надлежащей системы очистки или ORM легко разрешит атаки с использованием SQL-инъекции, создав гигантскую уязвимость.

🔗 Подробнее: Предотвращайте уязвимости при внедрении базы данных с помощью библиотек ORM/ODM или других пакетов DAL



##! 6.5. Сборник общих рекомендаций по безопасности

TL;DR: Это набор рекомендаций по безопасности, которые не связаны напрямую с Node.js -- реализация Node мало чем отличается от любого другого языка. Нажмите "Подробнее", чтобы просмотреть.

🔗 Подробнее: Общие рекомендации по безопасности Node.js



✔ 6.6. Настраивайте заголовки ответа HTTP для повышения безопасности

TL;DR: Ваше приложение должно использовать безопасные заголовки, чтобы предотвратить использование злоумышленниками распространенных атак, таких как межсайтовый скриптинг (XSS), клик-джеккинг и другие вредоносные атаки. Их можно легко настроить с помощью таких модулей, как helmet.

Иначе: Злоумышленники могут выполнять прямые атаки на пользователей вашего приложения, что приводит к огромным уязвимостям безопасности.

🔗 Подробнее: Используйте связанные с безопасностью заголовки для защиты вашего приложения от распространенных атак



✔ 6.7. Постоянно и автоматически проверяйте наличие уязвимых зависимостей

TL;DR: В экосистеме npm для проекта характерно наличие множества зависимостей. Зависимости всегда следует контролировать при обнаружении новых уязвимостей. Используйте инструменты, такие как npm audit или snyk, чтобы отслеживать, мониторить и исправлять уязвимые зависимости. Интегрируйте эти инструменты с настройкой CI, чтобы вы могли поймать уязвимую зависимость, прежде чем она попадет в производство.

Иначе: Злоумышленник может обнаружить ваш веб-фреймворк и атаковать все его известные уязвимости.

🔗 Подробнее: Постоянно и автоматически проверяйте наличие уязвимых зависимостей



✔ 6.8. Избегайте использования криптографической библиотеки Node.js для обработки паролей, используйте Bcrypt

TL;DR: Пароли или секреты (ключи API) должны храниться с использованием безопасной функции hash+ alt, такой как bcrypt, которая должна быть предпочтительным выбором по сравнению с реализацией JavaScript из-за соображений производительности и безопасности.

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

🔗 Подробнее: Не используйте криптографическую библиотеку Node.js для паролей, используйте Bcrypt



##! 6.9. Экранируйте вывод HTML, JS и CSS

TL;DR: Ненадежные данные, которые отправляются в браузер, могут выполняться вместо того, чтобы просто отображаться, это обычно называется атакой между сайтами (XSS). Смягчите это, используя выделенные библиотеки, которые явно помечают данные как чистый контент, который никогда не должен выполняться (т.е. кодирование, экранирование).

Иначе: Злоумышленник может сохранить вредоносный код JavaScript в вашей БД, который затем будет отправлен бедным клиентам как есть.

🔗 Подробнее: Экранируйте вывод



✔ 6.10. Проверяйте входящие схемы JSON

TL;DR: Проверьте полезную нагрузку тела входящих запросов и убедитесь, что она соответствует ожиданиям, сразу же отказывайте, если это будет не так. Чтобы избежать утомительного кодирования проверки в каждом маршруте, вы можете использовать упрощенные схемы проверки на основе JSON, такие как jsonschema или joi.

Иначе: Ваша щедрость и разрешительный подход значительно увеличивают поверхность атаки и побуждают злоумышленника опробовать множество входных данных, пока они не найдут какую-то комбинацию для сбоя приложения.

🔗 Подробнее: Проверяйте входящие JSON схемы



✔ 6.11. Поддерживайте черный список JWT

TL;DR: При использовании веб-токенов JSON (например, с Passport.js) по умолчанию отсутствует механизм для отзыва доступа из выданных токенов. Как только вы обнаружите какое-либо злонамеренное действие пользователя, у вас не будет возможности помешать им получить доступ к системе, если у них есть действующий токен. Смягчите это, внедрив черный список ненадежных токенов, которые проверяются при каждом запросе.

Иначе: Устаревшие или неуместные токены могут быть использованы злонамеренно для доступа к приложению третьей стороной, которая может выдавать себя за владельца токена.

🔗 Подробнее: Реализовывайте поддержку внесения JWT в черный список



✔ 6.12. Предотвращайте атаки методом грубой силы против авторизации

TL;DR: Простой и мощный метод заключается в ограничении попыток авторизации с использованием двух метрик:

  1. Во-первых, это число последовательных неудачных попыток одного и того же пользователя с уникальным идентификатором и IP-адресом.
  2. Во-вторых, количество неудачных попыток с IP-адреса в течение длительного периода времени. Например, заблокируйте IP-адрес, если он делает 100 неудачных попыток за один день.

Иначе: Злоумышленник может выполнить неограниченное количество автоматических попыток ввода пароля для получения доступа к привилегированным учетным записям в приложении.

🔗 Подробнее: Предотвращайте атаки методом грубой силы против авторизации



✔ 6.13. Запускайте Node.js как пользователь без полномочий root

TL;DR: Существует распространенный сценарий, когда Node.js запускается от имени пользователя root с неограниченными разрешениями. Например, это поведение по умолчанию в контейнерах Docker. Рекомендуется создать пользователя без полномочий root и либо "запечь" его в образе Docker (примеры приведены ниже), либо запустить процесс от имени этого пользователя, вызвав контейнер с флагом "-u username".

Иначе: Злоумышленник, которому удается запустить скрипт на сервере, получает неограниченную власть над локальной машиной (например, он может изменить iptable и перенаправить трафик на свой сервер).

🔗 Подробнее: Запускайте Node.js как пользователь без полномочий root



✔ 6.14. Ограничьте размер полезной нагрузки, используя обратный прокси или промежуточное ПО

TL;DR: Чем больше полезная нагрузка на тело, тем сложнее ваш отдельный поток обрабатывает ее. Это возможность для злоумышленников поставить серверы на колени без огромного количества запросов (атаки DOS/DDOS). Уменьшите это, ограничивая размер тела входящих запросов на границе (например, брандмауэр, ELB) или настраивая express body parser, чтобы принимать только полезные данные небольшого размера.

Иначе: Вашему приложению придется иметь дело с большими запросами, неспособными обработать другую важную работу, которую он должен выполнить, что приводит к снижению производительности и уязвимости к атакам DOS.

🔗 Подробнее: Ограничивайте размер полезной нагрузки с помощью обратного прокси или промежуточного ПО



✔ 6.15. Избегайте JavaScript eval утверждений

TL;DR: eval -- это зло, поскольку оно позволяет выполнять пользовательский код JavaScript во время выполнения. Это не только проблема производительности, но и важная проблема безопасности из-за вредоносного кода JavaScript, который может быть получен из пользовательского ввода. Другой языковой особенностью, которую следует избегать, является конструктор new Function. setTimeout и setInterval также никогда не должны передавать динамический код JavaScript.

Иначе: Вредоносный код JavaScript находит путь в текст, передаваемый в eval или другие функции оценки языка JavaScript в режиме реального времени, и получает полный доступ к разрешениям JavaScript на странице. Эта уязвимость часто проявляется как атака XSS.

🔗 Подробнее: Избегайте JavaScript eval утверждений



✔ 6.16. Предотвращайте ваше однопоточное выполнение от перегрузки злонамеренным RegEx

TL;DR: Регулярные выражения, будучи удобными, представляют реальную угрозу для приложений JavaScript в целом и платформы Node.js в частности. Пользовательский ввод для сопоставления текста может потребовать значительного количества циклов ЦП для обработки. Обработка RegEx может быть неэффективной до такой степени, что один запрос, который проверяет 10 слов, может заблокировать весь цикл событий на 6 секунд и установить ЦП на 🔥. По этой причине предпочитайте сторонние пакеты проверки, такие как validator.js вместо написания собственных шаблонов Regex, или используйте safe-regex для обнаружения уязвимых шаблонов регулярных выражений.

Иначе: Плохо написанные регулярные выражения могут быть подвержены DoS-атакам регулярного выражения, которые полностью блокируют цикл обработки событий. Например, популярный пакет момент был посчитан уязвимым для вредоносного использования RegEx в ноябре 2017 года.

🔗 Подробнее: Предотвращайте ваше однопоточное выполнение от перегрузки злонамеренным RegEx



✔ 6.17. Избегайте загрузки модулей с использованием переменных

TL;DR: Избегайте подключения/импорта другого файла с путем, который был задан в качестве параметра из-за опасений, что он мог возникнуть из пользовательского ввода. Это правило может быть расширено для общего доступа к файлам (то есть fs.readFile ()) или для доступа к другим чувствительным ресурсам с помощью динамических переменных, происходящих из пользовательского ввода. Eslint-plugin-security линтер может ловить такие шаблоны и предупреждать их достаточно рано.

Иначе: Вредоносный пользовательский ввод может найти путь к параметру, который используется для запроса измененных файлов, например, ранее загруженного файла в файловой системе, или для доступа к уже существующим системным файлам.

🔗 Подробнее: Избегайте загрузки модулей с использованием переменных



✔ 6.18. Запускайте небезопасный код в песочнице

TL;DR: При выполнении задачи запуска внешнего кода, который дается во время выполнения (например, плагин), используйте любую среду исполнения "песочницы", которая изолирует и защищает основной код от плагина. Это может быть достигнуто с помощью выделенного процесса (например, cluster.fork()), безсерверной среды или выделенных пакетов npm, которые действуют как песочница.

Иначе: Плагин может атаковать с помощью бесконечного множества вариантов, таких как бесконечные циклы, перегрузка памяти и доступ к чувствительным переменным среды процесса.

🔗 Подробнее: Запускайте небезопасный код в песочнице



✔ 6.19. Будьте особенно осторожны при работе с дочерними процессами

TL;DR: Избегайте использования дочерних процессов, когда это возможно, а также проверяйте и санируйте входные данные, чтобы смягчить атаки с использованием инъекций оболочки, если это необходимо. Предпочитайте использовать child_process.execFile, который по определению будет выполнять только одну команду с набором атрибутов и не позволит расширять параметры оболочки.

Иначе: Наивное использование дочерних процессов может привести к удаленному выполнению команды или атакам внедрения оболочки из-за того, что злонамеренный пользовательский ввод передан неантизированной системной команде.

🔗 Подробнее: Будьте осторожны при работе с дочерними процессами



✔ 6.20. Скрывайте детали ошибок от клиентов

TL;DR: Встроенный экспресс-обработчик ошибок по умолчанию скрывает детали ошибок. Однако велики шансы на то, что вы реализуете свою собственную логику обработки ошибок с помощью пользовательских объектов ошибок (которые многие считают наилучшей практикой). Если вы это сделаете, убедитесь, что не вернули весь объект Error клиенту, который может содержать некоторые важные сведения о приложении.

Иначе: Чувствительная информация о приложении, такая как пути к файлам сервера, используемые сторонние модули и другие внутренние рабочие процессы приложения, которые могут быть использованы злоумышленником, может быть утечка из информации, найденной в трассировке стека.

🔗 Подробнее: Скрывайте детали ошибок от клиентов



✔ 6.21. Конфигурируйте 2FA для npm или Yarn

TL;DR: Любой шаг в цепочке разработки должен быть защищен с помощью MFA (многофакторная аутентификация), npm/Yarn -- прекрасная возможность для злоумышленников, которые могут заполучить пароль разработчика. Используя учетные данные разработчика, злоумышленники могут внедрить вредоносный код в библиотеки, которые широко установлены в проектах и ​​службах. Может быть, даже через Интернет, если опубликованы в открытом доступе. Включение 2-факторной аутентификации в npm оставляет почти нулевые шансы для злоумышленников изменить код вашего пакета.

Иначе: Have you heard about the eslint developer who's password was hijacked?



✔ 6.22. Модифицируйте настройки промежуточного программного обеспечения сеанса

TL;DR: У каждого веб-фреймворка и технологии есть свои известные недостатки -- сообщить злоумышленнику, какой веб-фреймворк мы используем -- это большая помощь для него. Использование настроек по умолчанию для промежуточного программного обеспечения сеансов может подвергнуть ваше приложение атакам, направленным на модули и фреймворки, аналогично заголовку X-Powered-By. Попробуйте скрыть все, что идентифицирует и раскрывает ваш технический стек (например, Node.js, express).

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

🔗 Подробнее: Изменяйте настройки промежуточного программного обеспечения сеанса по умолчанию



✔ 6.23. Избегайте DOS-атак, явно указав, когда должен произойти сбой процесса

TL;DR: Процесс Node завершится сбоем, если ошибки не будут обработаны. Многие лучшие практики даже рекомендуют завершить работу, даже если ошибка была обнаружена и обработана. Например, Express будет аварийно завершать работу при любой асинхронной ошибке -- если только вы не заключите маршруты в условие catch. Это открывает очень приятное место для атак злоумышленников, которые распознают, какой ввод вызывает сбой процесса, и повторно отправляют один и тот же запрос. Мгновенного решения этой проблемы не существует, но несколько методов могут смягчить боль: оповещение с критической серьезностью каждый раз, когда происходит сбой процесса из-за необработанной ошибки, проверяет ввод и избегает сбоя процесса из-за неправильного ввода пользователя, оборачивает все маршруты с помощью catch и не рушится при возникновении ошибки в запросе (в отличие от того, что происходит глобально).

Иначе: Это просто обоснованное предположение: дано множество приложений Node.js, если мы попытаемся передать пустое тело JSON всем запросам POST -- несколько приложений потерпит крах. В этот момент мы можем просто повторить отправку одного и того же запроса, чтобы легко положить эти приложения.



✔ 6.24. Предотвращайте небезопасные перенаправления

TL;DR: Перенаправления, которые не проверяют пользовательский ввод, могут позволить злоумышленникам запускать фишинговые мошенничества, красть учетные данные пользователя и выполнять другие вредоносные действия.

Иначе: Если злоумышленник обнаружит, что вы не проверяете внешний вводимый пользователем ввод, он может воспользоваться этой уязвимостью, разместив специально созданные ссылки на форумах, в социальных сетях и других общедоступных местах, чтобы пользователи могли щелкнуть по нему.

🔗 Подробнее: Предотвращайте небезопасные перенаправления



✔ 6.25. Избегайте публикации секретов в реестре npm

TL;DR: Следует принять меры предосторожности, чтобы избежать риска случайной публикации секретов в открытых реестрах npm. Файл .npmignore может использоваться для внесения в черный список определенных файлов или папок, или массив files в package.json может выступать в качестве белого списка.

Иначе: Ключи API вашего проекта, пароли или другие секреты открыты для злоупотребления любым, кто сталкивается с ними, что может привести к финансовым потерям, подлогу и другим рискам.

🔗 Подробнее: Избегайте публикации секретов в реестре npm


⬆ К началу

7. Черновик: Практики эффективности

Наши соавторы работают над этим разделом. Хотите присоединиться?



✔ 7.1. Не блокируйте цикл событий

TL;DR: Избегайте ресурсоемких задач, поскольку они будут блокировать однопоточный цикл обработки событий, и выгружайте их в выделенный поток, процесс или даже другую технологию в зависимости от контекста.

Иначе: Поскольку цикл обработки событий заблокирован, Node.js не сможет обработать другой запрос, что приведет к задержкам для одновременных пользователей. 3000 пользователей ждут ответа, контент готов к отправке, но один-единственный запрос не позволяет серверу отправить результаты обратно

🔗 Read More: Не блокируйте цикл событий




✔ 7.2. Предпочитайте нативные методы JS, а не пользовательские утилиты типа Lodash

TL;DR: Часто более утомительно использовать служебные библиотеки, такие как lodash и underscore, по сравнению с нативными методами, так как это приводит к ненужным зависимостям и снижению производительности. Имейте в виду, что с введением нового движка V8 наряду с новыми стандартами ES собственные методы были улучшены таким образом, что теперь он примерно на 50% быстрее, чем служебные библиотеки.

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

🔗 Подробнее: Предпочитайте нативные методы JS над пользовательскими утилитами, такими как Lodash




Вехи

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


Переводы

Все переводы предоставлены сообществом. Мы будем рады получить любую помощь с готовыми, текущими или новыми переводами!

Завершенные переводы

Переводы в процессе



Руководящий комитет

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

Yoni Goldberg

Независимый консультант Node.js, который работает с клиентами в США, Европе и Израиле над созданием масштабируемых масштабируемых приложений Node. Многие из приведенных выше лучших практик были впервые опубликованы на goldbergyoni.com. Свяжитесь с Yoni как @goldbergyoni или me@goldbergyoni.com


Bruno Scheufler

💻 веб-инженер полного цикла, энтузиаст Node.js и GraphQL


Kyle Martin

Разработчик полного цикла и инженер по надежности сайтов из Новой Зеландии, заинтересованный в безопасности веб-приложений, а также в разработке и создании приложений Node.js для работы в глобальном масштабе.


Sagir Khan

Высококвалифицированный специалист по JavaScript и его экосистеме -- React, Node.js, MongoDB, практически все, что связано с использованием JavaScript/JSON на любом уровне системы -- создающий продукт с использованием веб-платформы для самых узнаваемых брендов мира. Индивидуальный член Фонда Node.js, сотрудничающий в рамках Сообщества Committee's Website Redesign Strategic Initiative.


Соавторы

Спасибо всем нашим соавторам! 🙏

Наши соавторы являются участниками, которые регулярно вносят свой вклад в хранилище, предлагая новые лучшие практики, разбирая проблемы, просматривая запросы на изменение и многое другое. Если вы заинтересованы в том, чтобы помочь нам научить тысячи людей создавать более качественные приложения Node.js, ознакомьтесь с нашими руководством для соавторов 🎉

Ido Richter (Founder) Keith Holliday

Прошлые соавторы

Refael Ackermann

Благодарности

Мы ценим любой вклад, от одного исправленного слова до новой лучшей практики. Список участников и документация по поддержке тут!