Русский
Русский
English
Статистика
Реклама

Https

Российские госсайты иллюзия безопасности

04.08.2020 18:23:50 | Автор: admin
В 2016 году мы задались вопросом: сколько сайтов федеральных органов власти поддерживают HTTPS? Мы узнали, вы готовы? Фактически 2 (прописью: два, Карл!) сайта из 85. Формально 32 поддерживали, т.е. на серверах был включен HTTPS, но дальше все упиралось в традиционное российское разгильдяйство: SSL-сертификат просрочен, самоподписан или вообще от другого сайта, соединение по HTTPS автоматически переключается на HTTP или переадресуется в админку сайта, веб-сервер уязвим к ROBOT, POODLE и прочим излишествам нехорошим, HTTPS-подключение только по SSL и прочий чад кутежа.

Поэтому, даже согласно нашим скромным критериям действительный SSL-сретификат, поддержка TLS 1.2 и отказ от использования уязвимых или ненадежных криптоалгоритмов типа DH и RC4 фактически HTTPS поддерживали только 2 сайта (напоминаю, из 85 обследованных).

Сегодня мы снова задались тем же вопросом, хотя и несколько ужесточив критерии, но даже при этом ситуация оказалась существенно лучше: 27 сайтов из 82 могут считаться реально поддерживающими HTTPS и еще 23 условно поддерживают его. Условно в том смысле, что при определенных условиях, зависящих в большей степени от клиентской стороны: актуальная версия браузера, сконфигурирована по уму, ручками указали HTTPS соединение защищено, не обеспечили чего-то из перечисленного depends on.

Еще 8 сайтов лишь имитируют поддержку HTTPS (все то же разгильдяйство): самоподписанные (Пробирная палата) и кривые (Минобороны и ФАДН) SSL-сертификаты, уязвимые шифронаборы (Минэкономразвития), кое-где до сих пор не слышали об обновлениях софта и их веб-сервера светят в Сеть приветливыми баннерами We have ROBOT & POODLE! (Минстрой, Росреестр, Росфинмониторинг и Роснедра).

Оставшиеся 24 сайта, начиная с президентского и заканчивая ЦИКовским, поступили еще проще: нет HTTPS нет проблемы. СВР зачем нам защищенное соединение? ФСБ сообщайте о подготовке теракта по HTTP! ФСО нам нечего скрывать, вам тоже. Мы точно не знаем, конечно, но, видимо, какая-то такая логика: чай не сайт банка и не ВКонтагтег какой-нибудь, можно и без защищенного соединения обойтись.

В общем, все то, что сегодня за несколько тысяч рублей в год обеспечивает любой мало-мальски приличный виртуальный хостинг: нормальный SSL-сертификат от Let's Encrypt, актуальная версия веб-сервера и криптографических библиотек с настройками по уму, большинству российских органов власти до сих пор недоступно. Зато у каждого, поди, есть какой-нибудь подведомственный ГИВЦ с соответствующим штатом и бюджетом
Подробнее..

Спутниковую связь по-прежнему легко прослушать. Перехват трафика кораблей и самолётов

16.08.2020 22:17:50 | Автор: admin

Перехваченные DVB-потоки НАТО (2002)

Много лет хакеры проводят демонстративные взломы гражданских и военных спутниковых коммуникаций, но безопасность тех остаётся на низком уровне. На последней конференции Black Hat 2020 оксфордский студент Джеймс Павур продемонстрировал, какой спутниковый трафик сейчас в радиоэфире и какую приватную информацию из него можно извлечь.

В течение нескольких лет Павур прослушивал с европейской территории сигналы 18 интернет-спутников. Получатели информации физические лица, корабли и самолёты на площади около 100 миллионов квадратных километров от США до Китая и Индии. Для организации такой станции доступно оборудование стоимостью около $300:



Вот некоторые примеры, что интересного удалось обнаружить:

  • Китайский авиалайнер, получающий незашифрованную навигационную информацию и лётные данные. Трафик шёл по тому же соединению, которое пассажиры использовали для отправки электронной почты и просмотра веб-страниц, повышая вероятность взломов со стороны пассажиров.
  • Системный администратор, который авторизовался в системе управления ветряной турбиной на юге Франции, с перехватом сессионного куки для аутентификации.
  • Перехват сообщений с египетского нефтяного танкера, сообщившего о неисправности генератора, когда судно вошло в порт Туниса. Передача не только позволила узнать, что корабль на месяц выйдет из строя, хакер также получил имя и номер паспорта инженера, назначенного для устранения неполадки.
  • Круизное судно, передающее конфиденциальную информацию о своей локальной сети Windows, включая информацию о логинах в базе LDAP.
  • Электронное письмо адвоката в Испании своему клиенту о предстоящем деле.
  • Пароль сброса аккаунта для доступа в сеть яхты греческого миллиардера.

За время исследования Павур собрал более 4 терабайт данных с 18 спутников. Он также проанализировал новые протоколы, такие как Generic Stream Encapsulation, и сложные модуляции, включая APSK. Хотя суть атаки не изменилась за последние более чем 15 лет, но до сих пор остаётся множество уязвимых спутниковых потоков, которые раскрываются и анализируются при перехвате трафика.

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

Установка позволяет перехватывать практически все передачи от провайдера пользователю через спутник, но отслеживание сигналов в обратном направлении гораздо сложнее. В результате мы видим только содержимое HTTP-сайтов, которые просматривает пользователь, или незашифрованные электронные письма, но не запросы GET или отправленные пароли.

Использование HTTPS решает большинство проблем, но DNS-запросы не шифруются, и в большинстве случаев всё равно можно деанонимизировать клиента.

В первое время Павур исследовал морской трафик, который передаётся кораблям.



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



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

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



По сути, для хакера спутники, корабли и самолёты просто компьютеры, которые отправляют данные по открытому каналу.
Подробнее..

Приглашение к обсуждению методики составления индекса HTTPS-защищенности сайтов

15.10.2020 16:12:34 | Автор: admin
Мы выпустили уже несколько обзоров поддержки HTTPS на сайтах российских органов власти и столкнулись с неизбежной необходимостью четче формализовать критерии, по которым она оценивается. Понятно, что если сервер подтверждает защищенность соединения чужим TLS-сертификатом, то это шляпа и высокого места в рейтинге соответствующему сайту не занять.

Но дальше возникают менее однозначные вопросы, например: поддержка TLS_RSA_EXPORT_WITH_RC4_40_MD5 это полная шляпа или просто недостаток? А если этот шифронабор из 60-х 90-х первым предлагается клиенту для согласования? А если все остальные не сильно лучше? А что такое сильно лучше? Скажем, TLS_PSK_DHE_WITH_AES_128_CCM_8 лучше или нет?

В результате родилась методика составления индекса, позволяющая формально оценивать степень надежности HTTPS-соединения по 31 пункту с разбивкой на 5 групп, от это вообще не HTTPS до так держать!

Чем точно не является индекс, так это российским ответом NIST/HIPAA/PCI DSS и т.п. по двум причинам.

Первая индекс учитывает только надежность HTTPS-соединения. Производительность веб-сервера (NPN/ALPN/session resumption) и т.п. материи индекс не рассматривает, не для того он придумывался.

Вторая NIST.SP.800 и прочие стандарты индустрии ориентируются на эллиптические кривые NIST, доверия к которым чуть более, чем никакого, зато есть вопросы с точки зрения ECDLP/ECC (задорную ремарку про шапочку из фольги можно невозбранно оставить в комментах).

Хотя кто знает, может со временем из индекса и вырастет суверенный стандарт с духовностью и скрепами Кузнечиком и Магмой (но не раньше, чем IETF признает их частью стандартных шифронаборов для TLS).

Основная идея индекса: в 2020 году настоящим HTTPS можно считать лишь TLS 1.2 и выше с соответствующим комплектом шифронаборов и эллиптических кривых. Старым шифронаборам, даже если они не имеют известных уязвимостей, пора на свалку истории. Рассуждения про необходимость поддержки legacy-клиетов в пользу бедных: Windows XP до сих пор популярен, но его пользователи не шастают сегодня по Интернету через Internet Explorer 8 с его доисторическим Schannel, а используют для этих целей браузеры на основе Chromium/Firefox, использующими NSS. То же самое относится к пользователям старых версий Android они либо поставили альтернативный браузер, не полагающийся на системную криптобиблиотеку, либо не могут пользоваться большинством современных сайтов даже через HTTP (без поддержки CSS3 и прочих современных свистоперделок).

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

1. Минимальные критерии

1.1. Поддерживается соединение по протоколу HTTP с шифрованием (HTTPS), обеспечиваемым использованием криптографического протокола TLS. HTTPS-соединение устанавливается с идентификатором протокола (схема URI) https по TCP-порту 443.

1.2. Шифрование соединения подтверждается действительным, не самоподписанным и не пустым TLS-сертификатом сайта стандарта X.509 версии 3, выданным авторитетным центром сертификации (CA).

2. Дополнительные критерии

2.1. Сервер не подвержен известным уязвимостям в реализации поддержки защищенного соединения (BEAST, POODLE, GOLDENDOODLE и т.п.)

2.2. TLS-компрессия не поддерживается.

2.3. Поддерживается только безопасное пересогласование (secure renegotiation) по инициативе сервера; пересогласование по инициативе клиента не поддерживается.

3. Рекомендуемые критерии

3.1. Соединение по протоколу HTTP автоматически (принудительно) переключается на HTTPS.

3.2. Публичный ключ TLS-сертификата сайта имеют длину 2048 бит. Сертификат подписан цифровой подписью по алгоритму SHA256 с шифрованием по алгоритму RSA или ECDSA.

3.3. Поддерживается протокол TLS версии 1.2.

3.4. Протоколы SSL и TLS версии 1.1 не поддерживаются.

3.5. Поддерживаются стандартные шифронаборы на основе стойких алгоритмов.

3.6. Слабые, неподходящие и уязвимые шифронаборы не поддерживаются.

3.7. Поддерживаются ECDLP/ECC-безопасные эллиптические кривые.

3.8. Задан порядок согласования шифронаборов.

3.9. Используются устойчивые параметры алгоритмов согласования ключей на основе протокола Диффи-Хеллмана (DH).

3.10. Поддерживаются важные расширения TLS.

3.11. Поддерживается Server Name Indication (SNI).

4. Расширенные критерии

4.1. Опубликована полная и не избыточная цепочка TLS-сертификатов с их верной последовательностью в цепочке.

4.2. TLS-сертификат сайта поддерживает Certificate Transparency.

4.3. TLS-сертификат сайта поддерживает Certificate Revocation List (CRL) и Online Certificate Status Protocol (OCSP).

4.4. TLS-сертификаты в альтернативных цепочках соответствуют Критериям 1.2, 4.1.

4.5. ECDLP/ECC-небезопасные эллиптические кривые не поддерживаются.

4.6. Задан порядок согласования эллиптических кривых.

4.7. Заголовки ответа сервера (HTTP response headers) содержат заголовок HTTP Strict Transport Security с директивой includeSubDomains.

5. Бонусные критерии

5.1. TLS-сертификат сайта поддерживает OCSP Stapling.

5.2. TLS-сертификат сайта выдан с использованием процедуры проверки организации (OV) или расширенной проверки (EV).

5.3. Поддерживается протокол TLS версии 1.3.

5.4. Задан порядок согласования устойчивых шифронаборов от более устойчивых к менее устойчивым (по сопоставимым параметрам).

5.5. Ресурсные записи DNS для доменного имени сайта включают запись CAA (Certification Authority Authorization).

5.6. Ресурсные записи DNS для доменного имени сайта включают записи DS и TLSA (поддерживаются DNSSEC и DANE).

5.7. Поддерживается Encrypted Server Name Indication (ESNI).

5.8. Заголовки ответа сервера (HTTP response headers) содержат заголовок Content-Security-Policy с директивой upgrade-insecure-requests.
Подробнее..

Сайты региональных органов власти все еще печальнее, чем у федералов

06.12.2020 22:08:26 | Автор: admin
image

Вот мы и выпустили сводный доклад по итогам мониторинга сайтов высших органов власти регионов Надежность сайтов органов государственной власти субъектов Российской Федерации 2020. Оценивали их с трех сторон: а) можно ли эти сайты считать официальными с точки зрения закона, б) обеспечивают ли они надежное HTTPS-соединение, и в) что и откуда они загружают, т.е. насколько потенциально уязвимы к XSS и как щедро сливают данные о своих посетителях третьим лицам?

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

Что касается официальности сайтов: у федералов 2 из 82 исследованных сайтов органов власти оказались неофициальным. Первоначально мы посчитали неофициальным еще и сайт Росгвардии, администрируемый подведомственным ей центром информационных технологий, но та отстояла свою точку зрения: центр это войсковая часть, т.е. часть самой Росгвардии, поэтому нарушения закона тут нет, а есть наша невнимательность (но вот TLS-сертификат у них на сайте уже вне всяких сомнений второй месяц как протух).

Поэтому региональные сайты мы проверяли по уже доработанной методике, предусматривающей запрос в ЕГРЮЛ и выяснили: из 184 сайтов, названных официальными, 27 (15%) таковыми не являются, т.к. соответствующие доменные имена администрируются подведомственными госучреждениями, коммерческими и некоммерческими организациями, и даже физическими лицами, хотя закон четко устанавливает, что это разрешено только государственным органам (читай органам власти). Особенно отличились Северо-Западный и Сибирский федеральные округа, где официальных сайтов не имеют более 30% высших органов власти.

С замиранием сердца делали запрос в ЕГРЮЛ по ИНН администратора сайта Парламента Чеченской Республики, который указан в реестре регистратора как ООО Парламент ЧР. Я, конечно, заранее извиняюсь, Рамзан Ахматович, но у парламентов в России иная организационно-правовая форма, и в ФНС считают так же (они пусть сами извиняются). В общем, сенсации не случилось администратор там Аппарат Парламента Чеченской Республики, а за ООО пусть извиняется тот, кто внес такую запись в реестр доменов.

Тут внимательный читатель может перебить меня вопросом: а почему исследовались 184 сайта, когда субъектов федерации 85? Отвечаю: исследовались сайты региональных правительств, парламентов и губернаторов при их наличии (сайта, а не губернатора). Но если вы думаете, что количество губернаторских сайтов легко вычисляется по формуле 184 85 85 (= 14), то вы заблуждаетесь, все намного сложнее. Например, у Правительства Москвы нет своего сайта, есть только сайт Мэра Москвы, где правительству отведен свой угол. Зато органы власти некоторых других субъектов располагают сразу двумя сайтами, причем оба называются официальными.

Для примера, у Правительства Республики Тыва сразу два сайта, оба названы официальными (gov.tuva.ru и rtyva.ru) и оба таковыми не являются с точки зрения закона, т.к. первое доменное имя администрируется АО Тывасвязьинформ, а второе вообще анонимным физическим лицом. У Правительства Костромской области тоже два сайта (adm44.ru и kostroma.gov.ru), оба официальные, но с разным наполнением.

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

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

Интересно тут другое: наверное, все хотя бы слышали про Электронную Москву, Электронную Бурятию, Электронный Татарстан и прочие электронные программы по освоению бюджетов на информатизацию госуправления. А знаете, у кого в результате оказалась лучшая поддержка HTTPS на официальном сайте? У правительств Ульяновской и Московской областей и парламентов Владимирской области и ЯНАО.

Я вот ничего даже не слышал про Электронное ЯНАО, может такой программы и не существует, а хотя бы одного пряморукого админа в ЯНАО найти смогли (пятое место по площади среди субъектов федерации, населения как в одном районе Москвы). А в электронной Бурятии то ли с населением еще хуже (Росстат возражает), то ли денег на нормального админа не хватило, но сервера обоих органов власти законодательной и исполнительной приветливо машут любознательным исследователям букетом из CVE-2014-0160, CVE-2014-0224, CVE-2016-2107, CVE-2019-1559 и далее со всеми остановками.

Из занимательного: при попытке проверить сайт Администрации Ненецкого автономного округа, мы наткнулись на блокировку по IP ряда исследовательских инструментов. На это у администратора хватило и усердия, и знаний, и желания, а вот на закрытие CVE-2012-4929 (кто не в курсе, первая цифра год описания уязвимости, 8 лет назад, Карл!) и прочих дыр уже ни сил, ни желания не осталось, а может и знаний тоже.

Лидером по поддержке защищенного соединения является Южный федеральный округ, где 53% исследованных сайтов обеспечивают достаточно надежное HTTPS-соединение. Следом за ним идут Центральный и Уральский (47% и 40% соответственно). Отстающие Приволжский и Северо-Кавказский, в которых лишь 13% сайтов высших органов власти не имеют значимых проблем с поддержкой защищенного соединения.

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

Например, у федералов Google Analytics на 4 месте по популярности, а у регионалов на 7; это даже в абсолютных цифрах меньше, чем у федералов. Но если собственно счетчик GA стоит лишь на 9% региональных сайтов, то гуглокод вообще на 63%, так что данные о посетителях они все равно успешно собирают. А вот на третьем месте среди счетчиков у регионалов внезапно оказался Bitrix. Это который вроде бы CMS ну и еще немного сбора статистики.

Рекордсменом по любви к аналитике стало Правительство Алтайского края, чей сайт украшен сразу 6 счетчиками, но его успех несколько меркнет по сравнению с сайтами Администрации Костромской области, парламентов Калининградской области, Удмуртской Республики и Москвы, которые украшены кодом счетчика OpenStat, уже два года не подающим признаков жизни. Это, конечно, не электронные пропуска шаманить, это ж HTML, а у ДИТ лапки загребущие.

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

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

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

Как подружить современный TLS и устаревшие браузеры?

20.02.2021 14:06:35 | Автор: admin

Тему подсказало обсуждение предыдущего поста, в котором прозвучал голос заботливого администратора веб-сервера: TLS 1.2 и AEAD выбор здорового человека, но кто пожалеет пользователей устаревших браузеров? Давайте это обсудим мнимую несовместимость современного TLS и устаревших браузеров.

Гипотеза звучит следующим образом: если на веб-сервере оставить поддержку только устойчивых версий протокола защиты транспортного уровня TLS (1.2 и 1.3) и шифронаборов (AEAD), пользователи устаревших браузеров зайти на сайт не смогут.

Совершим необязательный экскурс в историю В 2005 году (т.е. 16 лет тому назад), американское Агентство национальной безопасности анонсировало корпус стандартов, руководств, рекомендаций и прочих поддерживающих документов по использованию криптографических алгоритмов NSA Suite B Cryptography.

В 2009 году IETF опубликовал RFC5430 Suite B Profile for Transport Layer Security, где установил, какие протоколы и шифронаборы должны использоваться для HTTPS-соединения, чтобы соответствовать требованиям АНБ. Уже тогда для соответствия этим требованиям веб-сервер и веб-браузер должны были поддерживать TLS версии 1.2 и шифронаборы TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 и TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.

То есть, как минимум последние 11 лет разработчики знали, каким требованиям должна соответствовать их продукция, чтобы иметь возможность претендовать на американский госзаказ. Поэтому следующий факт вы, вероятно, воспримите без удивления: все реально используемые сегодня браузеры поддерживают упомянутый шифронабор в варианте с 128-битным шифроключом, а многие (но не все) и с 256-битным.

На этом можно было бы закончить статью, порекомендовав всем администраторам веб-серверов оставить поддержку только TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 и не забивать себе голову мифами о несовместимости TLS 1.2 с устаревшими браузерами, если бы не нюанс: в TLS версии младше 1.3 алгоритм цифровой подписи в шифронаборе должен совпадать с алгоритмом в TLS-сертификате, а веб-серверов с сертификатом, подписанным по алгоритму ECDSA, в доменной зоне .RU менее 6%, оставшиеся используют для подписи RSA.

Кто виноват вопрос отдельного исследования, нас же интересует что делать? Давайте для начала вспомним, что к устойчивым шифронаборам сегодня относятся не только рекомендуемая АНБ комбинация ECDHE+ECDSA+AES, но и другие:

Весь зверинец
TLS_DHE_RSA_WITH_AES_128_CCM;
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
TLS_DHE_RSA_WITH_AES_256_CCM;
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256;
TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384;
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
TLS_ECDHE_ECDSA_WITH_AES_128_CCM;
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
TLS_ECDHE_ECDSA_WITH_AES_256_CCM;
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256;
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384;
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256;
TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384;
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256.

Итого 19 устойчивых шифронаборов, однако не все из них подходят для использования в реальной жизни на публичном веб-сервере: алгоритм шифрования CAMELLIA, как и AES в режиме CCM, поддерживается чуть более, чем никем, с CHACHA20 и его верным спутником POLY1305 знакомы лишь относительно современные браузеры, а для использования шифронаборов на основе ECDSA, как уже было сказано, требуется соответствующий TLS-сертификат. Таким образом у нас остается лишь 4 дополнительных шифронабора:

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384.

Соблазнительно предположить, что если браузер поддерживает комбинацию ECDHE+ECDSA, то уж с ECDHE+RSA он точно справится, но это не всегда так многие умеют только в DHE+RSA, и чтобы удовлетворить запросы всех старых браузеров, на сайте с RSA сертификатом необходимо поддерживать два шифронабора:

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.

Это даст нам совместимость как минимум со следующими операционными системами и браузерами:

Android 4.4.2 + Android Browser (Chrome);
Windows XP + Chrome 49/Firefox 49/ Opera 12.18;
Windows 7 + Internet Explorer 11/Chrome 31/Firefox 31;
OS X + Firefox 29/Chrome 37;
iOS 9 + Safari 9;
Java 8b.

Про *nix системы не скажу зависит от сборки, но очевидно, что проблемы как таковой не существует. Единственная проблема возникнет с Windows Phone 8.1 + IE 10, которые поддерживают только одну устойчивую комбинацию ECDHE+ECDSA.

А что же делать пользователям Windows XP + IE 6 или какого-нибудь Android 2.3? спросит заботливый админ. Продолжать сидеть без доступа к современному Интернету, отвечу я, поскольку даже поддержка веб-сервером протокола SSL 2.0 им ничем не поможет. Дело в том, что даже если накатить на Windows XP все выпущенные для него обновления (по май 2019 года) и обновить штатный браузер до версии 8, это принесет лишь поддержку TLS 1.2, но не устойчивых шифронаборов. Кроме того, Windows XP как не знал, так и не узнает, что такое Server Name Indication (SNI), HTML 5 Live HTML и CSS 3.

Готовы ради упертых полутора землекопов держать для сайта выделенный IP и не обновлять верстку? Тогда добавьте поддержку, например, TLS_RSA_WITH_AES_256_CBC_SHA256, но скорее следует предположить, что современный пользователь Windows XP уже давно пользуется альтернативным браузером, который поддерживает даже TLS 1.3.

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

Чтоб два раза не вставать, рассмотрим кратко и ситуацию с эллиптическими кривыми. NSA Suite B Cryptography признает только две из них NIST P-384 и NIST P-256, поддержка которых на веб-сервере обеспечит доступ к сайту как современных, так и устаревших браузеров. Собственно, достаточно поддерживать только NIST P-384 для старичков и Curve25519 для современных браузеров; ну разве что еще NIST P-521 добавить, для некоторых передовых старичков.

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

Для сайта с TLS-сертификатом, подписанным по алгоритму ECDSA, включим поддержку шифронабора TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.

Для сайта с TLS-сертификатом, подписанным по алгоритму RSA, включим поддержку шифронаборов TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 и TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.

Для обоих сайтов включим поддержку эллиптической кривой NIST P-384 или NIST P-256 и зададим порядок предпочтения шифронаборов и эллиптических кривых, согласно которым вышеуказанные наборы и кривые предлагаются браузерам для согласования после более устойчивых, например после TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 и Curve25519 соответственно.
Подробнее..

Миф про мобильный CHACHA20

02.03.2021 14:06:25 | Автор: admin

При подготовке Методики расчета Индекса надежности HTTPS мы перерыли массу тематической литературы и не раз сталкивались с рекомендацией поддерживать на веб-сервере шифронаборы на основе алгоритма шифрования CHACHA20 в целях снижения нагрузки на мобильные клиенты, которые не умеют в аппаратный AES. В этом контексте обычно упоминались процессоры Mediatek и скопом старые бюджетные мобильные процессоры.

Действительно ли CHACHA20 со своим верным спутником POLY1305 позволяют не слишком греться мобильным клиентам и стоит ли его поддерживать на веб-сервере? Давайте это обсудим!

CHACHA20 был создан известным специалистом по криптографии Дэниэлом Бернштейном, которого мы любим, в частности, за Curve25519, а также за правозащитную деятельность, благодаря которой только олдфаги помнят, что означало _EXPORT_ в имени шифронабора. Алгоритм неплохо изучен, работает в AEAD-режиме, не имеет известных слабостей и уязвимостей, и является одним из двух алгоритмов шифрования, одобренных IETF для использования в TLS 1.3 (второй бессмертный AES).

Его теоретическая криптостойкость при использовании в TLS оценивается по-разному, в интервале между AES-128 и AES-256 в режиме GCM, что считается достаточным по сегодняшним меркам и на обозримую перспективу. При этом отмечается, что CHACHA20 быстрее AES, т.е. потребляет меньше процессорных ресурсов на обеспечение того же уровня криптостойкости. Эта формулировка не только отдает душком телемаркетинга (при всем уважении к ее автору), но и упускает важную деталь: на процессорах без аппаратной поддержки AES.

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

Intel представил поддержку AES-NI в 2010 году в процессорах архитектуры Westmere, причем далеко не во всех: Atom, Celeron, Pentium и Core i3 она еще долго не полагалась. В поддержке AES-NI без копания в спецификациях можно быть уверенным только начиная с архитектуры Goldmont (Apollo Lake и Denverton), а это уже 2016 год.

У AMD это архитектуры Bulldozer (2011) и Jaguar (2013 год), с ARM все сложнее: поддержка AES-инструкций предусмотрена в архитектуре ARMv8-A (2011 год, первое устройство 2013 год), но фактическое воплощение их в кремнии зависит от производителя процессора и я лично не стал бы так уверенно свистеть про старые бюджетные мобильные процессоры, скорее стоит говорить о не флагманских мобильных процессорах вообще, в т.ч. выпускаемых поныне.

Проще говоря, встретить процессор без аппаратной поддержки AES не так уж и сложно. Получается, CHACHA20, действительно, отличная альтернатива AES? Давайте взглянем, например, на результаты этого исследования. На процессорах без поддержки AES CHACHA20 заметно опережает его в производительности, зачастую в разы. К сожалению, замеров температуры нам не показали, но если речь идет о серверном процессоре, очевидно, что разница в потребляемых процессорных ресурсах значима.

Ситуация меняется на прямо противоположную, когда речь заходит о процессорах с поддержкой AES. Вряд ли стоит винить в этом CHACHA20, которому никто не предложил персональный набор инструкций в процессоре, а что бывает, когда оба участника играют по одним правилам, мы видели на старых процессорах (напоминаю: AES сливает).

Так что, дружно включаем поддержку CHACHA20 на веб-серверах? Почему бы и нет, хотя бы из того соображения, что все яйца в одну корзину не кладут, и если вдруг завтра в самом AES или его реализации в конкретной криптобиблиотеке найдут дыру, мы легким движением руки сможем отключить его до выяснения, оставшись на CHACHA20, а не судорожно искать, чем заменить AES, да как это включается.

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

Давайте вспомним, как вообще происходит согласование шифронабора при установлении HTTPS-соединения: клиент передает серверу список поддерживаемых им шифронаборов, в порядке от балды и изменить этот порядок можно только через групповые политики Windows и только для Internet Explorer браузеров, использующих SChannel (поправьте, если ошибаюсь). Сервер сравнивает полученный от клиента список со списком поддерживаемых им самим шифронаборов и сообщает клиенту, какой из них он выбрал для защиты соединения. Если на сервере задан приоритет шифронаборов, согласован будет первый совпавший в обоих списках с учетом заданного на сервере приоритета. Если не задан, то админу сервера надо оторвать руки мы погружаемся в область неизведанного теории вероятностей.

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

И тут в эту стройную картину мира влезает шифронабор на базе CHACHA20, который мы добавляем из соображений снижения нагрузки на слабых с аппаратной точки зрения клиентов, ничего не зная о том, являются ли они одновременно устаревшими или нет (т.е. флагманом этого года от третьеразрядной китайской компании или середнячком пятилетней давности от первостатейного бренда). Клиент сообщает, что поддерживает TLS 1.3 и полный комплект соответствующих шифронаборов, как на базе AES, так и на базе CHACHA20. Ваше решение, админ, какой шифронабор согласовываем клиенту? Вот и я о том же

Резюмирую вышесказанное по поводу алгоритма шифрования CHACHA20.

  1. Алгоритм вполне себе хорош и годится для использования в TLS.
  2. Шифронаборы на его основе поддерживаются только достаточно современным браузерами, так что совсем без AES пока никуда.
  3. Выигрыш в производительности от его использования можно получить не только лишь на старых бюджетных мобильных процессорах, но и на десктопах и серверах. На процессорах с аппаратной поддержкой AES, ситуация меняется на прямо противоположную.
  4. При установлении HTTPS-соединения не существует способа узнать, поддерживает ли процессор клиента AES на аппаратном уровне. Соответственно, нет способа узнать, какой шифронабор окажется быстрее в каждом конкретном случае.
Подробнее..

Ajax в обход Mixed Content

09.09.2020 18:05:36 | Автор: admin

Как-то я рассказывал о своем pet-проекте. В нем контроллеры ESP8266 управляются через Интернет. Пользователь создает программу и сохраняет ее в облаке. Контроллеры периодически ходят в него и забирают новую программу, которую исполняют.


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


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



Голубые стрелки демонстрируют путь передачи программы на контроллер.


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


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


Чтобы обойти это ограничение, была реализована такая схема:



Теперь контроллер передает свой локальный IP облаку (зеленые стрелки). Этот IP имеет смысл только в контексте локальной сети. Но, FrontEnd запускается как раз в ней. И теоретически, может выполнить http запрос непосредственно к контроллеру (розовая стрелка).


К сожалению, эта схема не работает без магии. Очередная проблема в том, что FrontEnd запущен под https а контроллеры в локальной сети не могут себе это позволить. Они работают по http. И возникает ошибка Mixed Content.



Познакомиться детально с проблемой можно тут и тут.


Но если бы это был тупик, статья бы не появилась. Обратите внимание, что загружаемый контент делится на два типа: активный и пассивный. К пассивному контенту отношение браузеров более лояльное. Например, можно встроить картинку с http сайта в https сайт. Проверено на Chrome (76.0.3809.132) и Firefox (80.0.1). Думаю, вы уже понимаете на что я намекаю.


Магия заключается в том, что вместо запросов через XMLHttpRequest, можно обойтись средствами пассивного контента. Для этого достаточно в тег img установить src с GET параметрами. В моем случае это выглядит так:


<img src=http://192.168.0.33/stdin?raw=$LTSTL,10,10,19840,80,100,70,40,20*5f>

И чудесным образом получилось достичь желаемого. FrontEnd смог в режиме реального времени отсылать на контроллеры в локальной сети команды управления. Более того, использование PWA позволило создать offline приложение, которое не требовало связи с Интернет. Условно, конечно.


Видео-демонстратор. Происходит управление спектром светильника в реальном времени:



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


Во-первых, результатом ответа может быть успех или провал. Т.е. код ответа от контроллера выраженный в поведении img. Согласен, весьма посредственное решение. Но в моем случае этого достаточно необходимо просто знать был выполнен запрос или нет.


Во-вторых, картинка возвращается с характеристиками размером. В них можно передавать закодированный результат. Сложнее, но надежнее.


В-третьих, картинка может иметь графическое кодирование. Его может парсить FrontEnd. К сожалению, img не умеет отдавать бинарные данные самой картинки. Придется сначала вывести ее на canvas, а затем декодировать.


Последний вариант, который я рассматривал redirect на сайт облака с параметрами ответа. Т.е. картинка запрашивается с локального IP контроллера, тот получает параметры GET и выполняет программу. В ответ он перенаправляет запрос на сайт облака. В редиректе содержатся ответ контроллера как GET параметры. Облако парсит ответ и передает фронту.


Задача весьма специфичная. Надеюсь мой опыт окажется кому-то полезным.

Подробнее..
Категории: Html , Javascript , Https , Mixed content

Разработка защищённого WEB интерфейса для микроконтроллеров

19.04.2021 12:12:03 | Автор: admin

Упомянутые выше параметры вызваны целевым назначением устройства, пока абстрактного устройства.
А WEB интерфейс в данном случае предназначен для настройки параметров и некоего управления в локальной но при этом небезопасной сети со смартфонов или других мобильных гаджетов.

Выбор аппаратной платформы.

Платформа в виде платы должна иметь чип или модуль Wi-Fi, микроконтроллер, желательно SD карту или чип внешней памяти ёмкостью не менее нескольких мегабайт.

Выбор RTOS

В проектируемом устройстве WEB сервер играет вторичную роль. Кроме него будут работать ещё десятки задач. Организовать WEB сервер так чтобы его присутствие не влияло на работу остальной функциональности можно с помощью RTOS.
От middleware RTOS нам нужен развитый набор сервисов синхронизации, вытесняющая многозадачность, надёжный менеджер динамической памяти, быстрая файловая система для SD или eMMC карт, стек TCP/IP протоколов и очень желательно сервисные инструменты отладки.
Все это есть в Azure RTOS. Эта RTOS имеет очень длинную и славную историю с тех пор, когда ещё называлась ThreadX. Она нашла применение в нескольких миллиардах устройств. Её отличает надёжность, компактность и хорошая документация. Долгое время была коммерческой и очень дорогой. Впервые в открытом виде появилась в пакете ПО Synergy от Renesas для микроконтроллеров Synergy на базе ARM Cortex-M4, и сразу получила широкую популярность благодаря исключительно богатой палитре предоставляемого middleware. Теперь портированное middleware Azure RTOS доступно бесплатно и для STM32.

Выбор WEB сервера.

Верхним протоколом, на котором непосредственно базируется WEB сервер является HTTP.
Azure RTOS уже имеет в своём составе HTTP сервер, работающий поверх стека сетевых протоколов Azure RTOS NetX Duo. HTTP Azure, согласно документации, требует всего от 3.0 KB до 9.5 KB FLASH и от 0.5 KB до 2 KB ОЗУ. Ниже будет дана более реалистичная оценка необходимого объема ОЗУ.
Поскольку NetX Duo обладает интерфейсом BSD, то есть возможность достаточно легко адаптировать другие сторонние WEB сервера, хотя тесная связь таких серверов с нижележащим API делает такой выбор малоэффективным.

Свойства WEB сервера реализованного в Azure RTOS:
- Поддержка 2-х типов авторизации: basic (передача пароля в открытом виде) и digest (передача хэша пароля),
- Работа по протоколам HTTP и HTTPS (HTTP защищённый с помощью TLS)
- Чтение файлов страниц с SD карты или другого носителя с FAT32
- Обработка запросов: GET, POST, HEAD, PUT, DELETE
- Поточная передача без указания размера данных: Content-Length
- Работа нескольких подключений одновременно.

Полностью документация на WEB сервер здесь.

Технология работы WEB интерфейса

Способ работы сервера очень простой. Устройство получает через TCP соединение текстовую строку запроса от браузера пользователя сформированную согласно спецификации HTTP.
Устройство также должно ответить строкой по спецификации HTTP. Поэтому WEB сервер в Azure RTOS называется HTTP сервером. HTTP сервер Azure RTOS сразу готов отдавать по запросам браузера файлы с SD карты устройства. Но статические страницы мало интересны.

Самой распространённой технологией в WEB серверах встраиваемых устройств является технология Server Side Includes (SSI). В Azure WEB сервере такой технологии нет. Видимо она считается устаревшей. Действительно, SSI позволял придать динамичность страницам, когда в браузере была отключена возможность исполнять JavaScript. Теперь же JavaScript включён повсеместно, без него сложно добиться адаптируемости.
Более продвинутым считается способ взаимодействия браузера и сервера с помощью технологии AJAX. Т.е. браузер с помощью JavaScript после загрузки страницы в отдельном потоке запрашивает дополнительные данные из устройства. Сервер в устройстве на лету формирует блок запрашиваемых данных и отправляет браузеру в таком же виде как он посылал страницы т.е. по спецификации HTTP.

Никто, конечно, не мешает реализовать и SSI в сервере Azure. Это делается очень просто и выльется в конечном итоге в длинную цепочку операторов if else с проверками на вхождение строк-директив. Но такой подход вызовет слишком тесную связность между страницами и кодом в микроконтроллере.
Гораздо привлекательней выглядит AJAX, причём с передачей файлов в JSON кодировке.
Дело в том, что JSON является форматом внутреннего представления объектов JavaScript в WEB страницах. Нет ничего проще для WEB разработчика чем передавать и принимать данные в формате JSON. В JSON можно выполнить сериализацию буквально всего: параметров, таблиц параметров, баз данных параметров, иерархических деревьев параметров, представления параметров в виде виджетов и прочее.

JSON кодировка довольно простая. JSON в конечном счёте просто строка. Внутри неё нельзя использовать байт 0, поэтому эта строка легко интерпретируется как C-и строка. Одновременно это же обстоятельство позволяет без перекодировки вставлять JSON в HTTP строку ответа.
Немного сложнее дела обстоят с парсингом JSON. Парсинг JSON необходим когда браузер клиента пришлёт устройству запрос например с отредактированными настройками. Простые JSON строки можно парсить и средствами языка C-и, но большие JSON строки уже требуют серьёзных парсеров.
Здесь можно посоветовать проект Jansson. Надо только знать что Jansson активно использует динамическую память, и если передавать в JSON около сотни числовых и строковых параметров, то для парсера может понадобиться около 50 Кбайт ОЗУ, зависит от длины имён переменных и длины самих переменных.

Дополнительные замечания по HTTP серверу Azure.

MIME типы
Сервер Azure HTTP не поддерживает HTTP pipelining, но поддерживает передачу файлов один за другим в одном TCP соединении. WEB страницы как правило содержат ссылки на файлы стилей, скриптов, картинок и прочего. Все эти файлы скачиваются последовательно один за другим браузером клиента. Чтобы получить файл браузер посылает HTTP запрос. Сервер на старте отправки каждого файла отправляет HTTP заголовок, например такой:

HTTP/1.1 200 OKContent-Type: text/htmlConnection: keep-aliveContent-Length: 13210Date: Sun, 17 May 2020 00:59:19 GMTCache-Control: max-age=1Last-Modified: Sun, 17 May 2021 00:59:19 GMT

Здесь имеет большое значение содержание поля Content-Type. Если его указать неправильно браузер может неправильно отобразить страницу. Сервер Azure содержание Content-Type устанавливает в соответствии с расширением передаваемого файла. Но список самих таких известных расширений у сервера небольшой, поэтому в файле nx_web_http_server.c дополняем массив _nx_web_http_server_mime_maps следующим образом:

/* Define basic MIME maps. */static NX_WEB_HTTP_SERVER_MIME_MAP _nx_web_http_server_mime_maps[] ={    {"html",     "text/html"},    {"htm",      "text/html"},    {"txt",      "text/plain"},    {"css",      "text/css"},    {"js",       "application/javascript"},    {"gif",      "image/gif"},    {"jpg",      "image/jpeg"},    {"png",      "image/png"},    {"ico",      "image/x-icon"},};

Способы размещения контента. Для более удобного и быстрого размещения статического контента и сопутствующих файлов на WEB сервере устройства можно применить FTP сервер. FTP сервер имеется в поставке Azure RTOS. Такие среды разработки как Adobe Dreamweaver способны автоматически обновлять контент на целевом FTP сервере содержащем контент WEB сайта.

Ограничение видимости для WEB сервера Azure.
По умолчанию корневой директорией HTTP сервера Azure является корневая директория SD карты.
Чтобы сервер мог считывать файлы только из определенной поддиректории ему надо установить функцией fx_directory_local_path_set локальный путь для задачи сервера. Это удобно делать при первом вызове в callback функции перехватчика запросов сервера. Указатель на callback функцию передаётся в задачу сервера при его создании с помощью функции nx_web_http_server_create. Весь HTTP сервер выполняется в одной задаче, поэтому такой метод работает.

Сжатие контента. Современные браузеры поддерживают компрессию передаваемых страниц и сопутствующих файлов. Компрессия типичных HTML файлов, скриптов и стилей позволяет уменьшит объем передаваемых данных в 3-4 раза. Сервер HTTP Azure не поддерживает компрессию на лету, для микроконтроллеров компрессия может оказаться более продолжительным процессом чем передача несжатого контента. Есть возможность выполнить компрессию статических файлов сразу же при подготовке контента. И хранить файлы на SD карте уже сжатыми, однако HTTP сервер Azure не поддерживает распознавание сжатых файлов и соответствующую модификацию заголовков HTTP.
Проблема не так актуальна как может показаться. Например одностраничные приложения выполненные во фреймворках скачивают статичный контент всего один раз, и гораздо больший трафик может создать динамический контент. Статический контент также можно разместить на сторонних быстрых серверах способных сжимать. И можно конечно же реализовать распознавание сжатых файлов в HTTP сервере собственными силами если сжатие действительно сильно улучшит отзывчивость сервера.

Неточность в исходниках Azure HTTP сервера при базовой авторизации.
Исходные тексты содержат неточность в файле nx_web_http_server.c после строки 3689.
В таком виде базовая авторизация затягивается на 10 сек., поскольку не разрывается соединение после неавторизированного запроса. Туда следует вставить вот такой фрагмент:

if (status == NX_WEB_HTTP_BASIC_AUTHENTICATE)        {          _nx_web_http_server_connection_reset(server_ptr,server_ptr -> nx_web_http_server_current_session_ptr , NX_WEB_HTTP_SERVER_TIMEOUT_SEND);          return;        }

После такого исправления пользователь сразу, а не через 10 сек. увидит диалог с вводом имени и пароля.
Не забыть также вставить этот фрагмент для методов POST и DELETE.

Выбор WEB фреймворка

Выбор фреймворка очень важен. Фреймворки это такое сочетание JavaScript библиотек и файлов стилей CSS. Бывает просто одна библиотека, как например jQuery, бывает библиотека со стилями, как например jQuery UI. Фреймворк значительно облегчает создание адаптируемых, интуитивных и привлекательных WEB страниц, в противовес использованию голого HTML и JavaScript с архаичными стилями. С помощью фреймворков даже самый примитивный WEB сервер выполненный на Arduino, может предоставить удивительно стильный WEB интерфейс. Фреймворки автоматически адаптируют страницы так чтобы они оставались в приемлемом качестве на экранах любых размеров и в любых браузерах.

Но фреймворков очень много. В нашем случае прежде всего интересует сколько файлов и какого размера требуется для фреймворка, насколько удобен фреймворк в использовании неспециалисту, насколько богат и гибок набор стилей и виджетов. Надо помнить что файлы фреймворка скачиваются браузером вместе со страницами WEB интерфейса из устройства. Слишком большой объем фреймворка замедлит время открытия страниц.
Охватить анализом все фреймворки и библиотеки практически невозможною Они возникают как грибы после дождя. Поэтому тут я привёл таблицу как попытку оценить несколько фреймворков которые есть на слуху:

Большинство фреймворков вводят новые элементы синтаксиса разметки в страницы HTML, добиваясь таким образом новых способов выражения дизайна. Это влечёт за собой необходимость дополнительно изучать кроме HTML, JavaScript, CSS и DOM модели ещё и уникальный язык и архитектурный подход фреймворка. Это ещё более усложняет задачу выбора. Поэтому об оптимальном выборе речь не идёт.
Как локальный оптимум выбираем jQuery mobile. Он использует одну из старейших и проверенных библиотек jQuery и интегрирован в WYSIWYG среду разработки Adobe Dreamweaver, что позволяет более удобно конструировать интерфейс по сравнению со способами без WYSIWYG. jQuery mobile как следует из названия предназначен в первую очередь для мобильных устройств, и это как раз то что нужно, поскольку большинство пользователей скорее всего захотят работать с WEB интерфейсом через мобильные устройства

Защита WEB сервера, генерация и инсталляция сертификатов.

Защищённый WEB сервер работает по протоколу HTTPS на порту 433. В этом случае используется протокол шифрования и аутентификации Transport Layer Security (TLS). В Azure RTOS реализован протокол TLS версии 1.3 и также более ранние его версии.

По умолчанию сервер просит от клиента вот такой набор алгоритмов:
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)

Для того чтобы WEB c TLS работал устройство должно ещё иметь в своих недрах сертификат сервера и секретный ключ сервера. Здесь предлагается их хранить во Flash памяти в виде массивов. Сертификат сервера должен содержать подпись доверенного центра (это стоит денег и времени), поэтому проще всего сгенерировать самоподписанный сертификат.

И ключ и сертификат должны быть представлены в двоичном формате DER.

Для примера: ключ занимает 1192 байта, один самоподписанный сертификат занимает 879 байт.

Чтобы сгенерировать самоподписанный сертификат и секретный ключ надо скачать файлы openssl.exe, libeay32.dll, ssleay32.dll и выполнить в их директории несколько команд:

1. Сгенерировать корневой секретный ключ ca.key -
openssl genrsa -out ca.key 2048

2. Сгенерировать корневой сертификат CA.crt -
openssl req -config CA.conf -new -x509 -sha256 -key ca.key -days 3650 -out CA.crt

3. Сгенерировать секретный ключ сервера srv.key
openssl genrsa -out srv.key 2048

4. Сгенерировать запрос сертификата сервера с использованием корневого сертификата srv.csr -
openssl req -new -config Server.conf -out srv.csr -key srv.key

5. Верифицировать и сгенерировать сертификат сервера srv.crt -
openssl x509 -req -in srv.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out srv.crt -days 3650

6. Конвертировать сертификаты и ключ в формат DER -
openssl x509 -in CA.crt -out CA.der -outform DER
openssl x509 -in srv.crt -out srv.der -outform DER
openssl rsa -inform pem -in srv.key -outform der -out srv_key.der

На HTTP сервер устанавливаем файлы srv.der и srv_key.der предварительно сконвертировав их в массивы. Причём у нас остаётся корневой сертификат, благодаря чему даже если злоумышленники взломают устройство и похитят сертификат сервера они не смогут сгенерировать новые сертификаты после того как похищенный сертификат буде аннулирован.

Сколько ОЗУ потребует защищённый WEB сервер

Задача http сервера в Azure RTOS называется TCPSERVER Thread.
Про этому названию её легко найти в списке запущенных задач в отладчике IDE IAR Embedded Workbench for Arm.
По наблюдениям на реальном проекте размер занятого этой задачей стека не превышал 2500 байта.
Движок TLS требует нескольких объёмных структур данных:
Структура NX_SECURE_X509_CERT имеет размер 304 байта
Массив crypto_metadata должен иметь размер не менее 17596 байта на каждую сессию. У нас выбрано до 2 сессий одновременно. Следовательно надо 35192 байта
Массив tls_packet_buffer требует не менее 2000 байт и не более 64 Кбайт. Задаём ему 4000 байта. Его размер определяется размером сертификата сервера. Сертификат может сопровождаться цепочкой сертификатов поэтому надо определять этот размер каждый раз по обстоятельствам.

Итого защищённый сервер потребует не менее 42 Кбайт. Это сравнительно немного. В целом на весь TCP стек с сервером потребуется около 100 Кбайт ОЗУ. Это с учётом буфера пакетов TCP/IP пакетов, JSON парсера для минимальных структур и прочих расходов на протоколы. Если иметь в виду ещё файловую систему, то размер возрастёт на 30-70 Кбайт в зависимости от того какое быстродействие хотим получить.

Пример разработанной страницы WEB интерфейса.

Используя фреймворк jQuery mobile сконструирована вот такая страница:

Если бы фреймворка не было, то эта же страница выглядела бы так:

Содержимое HTML страницы
<!doctype html><html lang="en">  <head>    <meta charset="utf-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">    <title>IoT Logger</title>    <link href="jquery-mobile/jquery.mobile.theme-1.3.0.min.css" rel="stylesheet" type="text/css">    <link href="jquery-mobile/jquery.mobile.structure-1.3.0.min.css" rel="stylesheet" type="text/css">    <script src="jquery-mobile/jquery-1.11.1.min.js">    </script>    <script src="jquery-mobile/jquery.mobile-1.3.0.min.js">    </script>    <style>        @media only screen and (device-width: 300px), only screen and (max-width:300px) {            .css-element {                yourcsscode:;            }        }        input.cb_larger {            width: 25px;            height: 25px;            margin: -12px 0 0 -10px;        }        .tbl_hdr {            font-size: 14px;            font-style: oblique;            font-weight: normal;            text-align: left;            background-color: #DEDEDE        }    </style>  </head>  <body>    <div align="center" data-role="page" id="page">      <div data-role="header" data-position="fixed">        <p id="page_header1" style="padding: 0px 0px 0px 0px; margin: 5px 0px 0px 0px ">IoT Logger</p>        <p style="font-size: 10px; padding: 0px 0px 0px 0px; margin: 0px 0px 5px 5px; text-align: left ">        <span>ID:</span>        <em id="page_header2" style="color:deepskyblue">?</em>        <span id="page_header3" style="margin-left: 10px">?</span>        </p>        <div data-role="navbar" style="width: 400px">          <ul>            <li></li>            <li></li>            <li><li><button type="submit" id="reset_log" onclick="location.assign('device_log.html');" data-theme="b">Show log</button></li></li>          </ul>        </div>      </div>      <div data-role="content">        <div style="overflow:auto;">        <table id="ap_list" style="width:800px">          <caption style="font-size: 16px; font-weight: bold; text-align: left">          Available Access Points list for Station mode          </caption>          <tr>            <th class="tbl_hdr"></th><th class="tbl_hdr">Enable</th>            <th class="tbl_hdr">Access Point SSID</th>            <th class="tbl_hdr">Password</th>            <th class="tbl_hdr">Use DHCP</th>            <th class="tbl_hdr">IP address</th>            <th class="tbl_hdr">IP mask</th>            <th class="tbl_hdr">IP gateway</th>          </tr>          <tr>            <td>1</td>            <td><input type="checkbox" class="cb_larger" id="ssiden1" name="SSIDEN1"></td>            <td><input type="text" id="ssid1" name="SSID1" value=""></td>            <td><input type="password" id="pass1" name="PASS1" value=""></td>            <td><input type="checkbox" class="cb_larger"  id="dhcp1" name="DHCP1"></td>            <td><input type="text"    id="ipaddr1"    name="IPADDR1" value=""></td>            <td><input type="text"    id="ipmask1"    name="IPMASK1" value=""></td>            <td><input type="text" id="ipgateway1" name="IPGATEWAY1" value=""></td>          </tr>          <tr>            <td>2</td>            <td><input type="checkbox" class="cb_larger" id="ssiden2" name="SSIDEN2"></td>            <td><input type="text" id="ssid2" name="SSID2" value=""></td>            <td><input type="password" id="pass2" name="PASS2" value=""></td>            <td><input type="checkbox" class="cb_larger"  id="dhcp2" name="DHCP2"></td>            <td><input type="text"    id="ipaddr2"    name="IPADDR2" value=""></td>            <td><input type="text"    id="ipmask2"    name="IPMASK2" value=""></td>            <td><input type="text" id="ipgateway2" name="IPGATEWAY2" value=""></td>          </tr>          <tr>            <td>3</td>            <td><input type="checkbox" class="cb_larger" id="ssiden3" name="SSIDEN3"></td><td>            <input type="text" id="ssid3" name="SSID3" value=""></td>            <td><input type="password" id="pass3" name="PASS3" value=""></td>            <td><input type="checkbox" class="cb_larger"  id="dhcp3" name="DHCP3"></td>            <td><input type="text"    id="ipaddr3"    name="IPADDR3" value=""></td>            <td><input type="text"    id="ipmask3"    name="IPMASK3" value=""></td>            <td><input type="text" id="ipgateway3" name="IPGATEWAY3" value=""></td>          </tr>          <tr>            <td>4</td>            <td><input type="checkbox" class="cb_larger" id="ssiden4" name="SSIDEN4"></td>            <td><input type="text" id="ssid4" name="SSID4" value=""></td>            <td><input type="password" id="pass4" name="PASS4" value=""></td>            <td><input type="checkbox" class="cb_larger"  id="dhcp4" name="DHCP4"></td>            <td><input type="text"    id="ipaddr4"    name="IPADDR4" value=""></td>            <td><input type="text"    id="ipmask4"    name="IPMASK4" value=""></td>            <td><input type="text" id="ipgateway4" name="IPGATEWAY4" value=""></td>          </tr>          <tr>            <td>5</td>            <td><input type="checkbox" class="cb_larger" id="ssiden5" name="SSIDEN5"></td>            <td><input type="text" id="ssid5" name="SSID5" value=""></td>            <td><input type="password" id="pass5" name="PASS5" value=""></td>            <td><input type="checkbox" class="cb_larger"  id="dhcp5" name="DHCP5"></td>            <td><input type="text"    id="ipaddr5"    name="IPADDR5" value=""></td>            <td><input type="text"    id="ipmask5"    name="IPMASK5" value=""></td>            <td><input type="text" id="ipgateway5" name="IPGATEWAY5" value=""></td>          </tr>        </table>        </div>        <table id="ap_selector">          <tr>            <th>            <div style="text-align:left ; padding-top: 10px">              Selected operational mode            </div>            </th>          </tr>          <tr>            <td>            <input type="radio" id="md_sta" name="wifi_mode" value="0">            <label id="md_sta_lb" for="md_sta">            Station mode <br>            <span style="font-weight: normal; font-size: 14px">            (The device will connect to the someone <br>            of access points listed in the table above)            </span>            </label>            </td>          </tr>          <tr>            <td>            <input type="radio" id="md_ap" name="wifi_mode" value="1">            <label id="md_ap_lb" for="md_ap">            Access Point mode<br>            <span style="font-weight: normal; font-size: 14px">(The device will wait for someone to join with it)</span>            </label>            </td>          </tr>        </table>        <fieldset class="ui-grid-a" style="width: 300px">          <div class="ui-block-a">            <button type="submit" id="submit_data" data-theme="a">Submit</button>          </div>          <div class="ui-block-b">            <button type="submit" id="reset_dev" data-theme="e">Reset device</button>          </div>        </fieldset>        <div data-role="footer" data-position="fixed">          <p id="status_msg" style="font-weight:normal" hidden=true>          </p>        </div>      </div>    </div>  </body>  <script>    // Глобальные переменные для хранения полученной из дивайса информации    var params;    var dev;    var apl;    var wrl;    // Вывод строки статуса выполнения операции по нажатию кнопок    function Show_cmd_status(data, status)    {      $("#status_msg").text("Data sending status: " + status); $("#status_msg").show();      setTimeout(function() { $("#status_msg").hide(); }, 2000);    }    // Функция извлечение интересующих параметров из объекта data и помещение их в поля ввода    function Data_accept(data, status)    {      params = data;                  var v = params.find(function(item, i) { if (item["Client_AP_list"] != undefined) return true; });      apl = v["Client_AP_list"];      if (apl != undefined)      {        $("#ssid1").val(apl[0][1]);        $("#pass1").val(apl[0][2]);        $("#ipaddr1").val(apl[0][4]);        $("#ipmask1").val(apl[0][5]);        $("#ipgateway1").val(apl[0][6]);        $("#ssid2").val(apl[1][1]);        $("#pass2").val(apl[1][2]);        $("#ipaddr2").val(apl[1][4]);        $("#ipmask2").val(apl[1][5]);        $("#ipgateway2").val(apl[1][6]);        $("#ssid3").val(apl[2][1]);        $("#pass3").val(apl[2][2]);        $("#ipaddr3").val(apl[2][4]);        $("#ipmask3").val(apl[2][5]);        $("#ipgateway3").val(apl[2][6]);        $("#ssid4").val(apl[3][1]);        $("#pass4").val(apl[3][2]);        $("#ipaddr4").val(apl[3][4]);        $("#ipmask4").val(apl[3][5]);        $("#ipgateway4").val(apl[3][6]);        $("#ssid5").val(apl[4][1]);        $("#pass5").val(apl[4][2]);        $("#ipaddr5").val(apl[4][4]);        $("#ipmask5").val(apl[4][5]);        $("#ipgateway5").val(apl[4][6]);        if (apl[0][0] == 1) $("#ssiden1").attr("checked", true);        if (apl[1][0] == 1) $("#ssiden2").attr("checked", true);        if (apl[2][0] == 1) $("#ssiden3").attr("checked", true);        if (apl[3][0] == 1) $("#ssiden4").attr("checked", true);        if (apl[4][0] == 1) $("#ssiden5").attr("checked", true);        if (apl[0][3] == 1) $("#dhcp1").attr("checked", true);        if (apl[1][3] == 1) $("#dhcp2").attr("checked", true);        if (apl[2][3] == 1) $("#dhcp3").attr("checked", true);        if (apl[3][3] == 1) $("#dhcp4").attr("checked", true);        if (apl[4][3] == 1) $("#dhcp5").attr("checked", true);      }      v = params.find(function(item, i) { if (item["Parameters"] != undefined) return true; });      if (v != undefined)      {        wrl = v["Parameters"].find(function(item, i) { if (item[0] == "wifi_role") return true; });        if (wrl != undefined)        {          if (wrl[1] == 0)          {            $("#md_sta_lb").click();          } else          {            $("#md_ap_lb").click();          }        }      }      v = params.find(function(item, i) { if (item["Device"] != undefined) return true; });      dev = v["Device"];      if (dev["HW_Ver"] != undefined)      {        $("#page_header1").html(dev["HW_Ver"]);        $("#page_header2").html(dev["CPU_ID"]);        $("#page_header3").html(dev["CompDate"] + " " + dev["CompTime"]);      }    }    // Считываем параметры из полей ввода, записываем их в объект JSON    // Сериализируем объект и отправляем устройству методом POST    function Data_send()    {            apl[0][1] = $("#ssid1").val();      apl[0][2] = $("#pass1").val();      apl[0][4] = $("#ipaddr1").val();      apl[0][5] = $("#ipmask1").val();      apl[0][6] = $("#ipgateway1").val();      apl[1][1] = $("#ssid2").val();      apl[1][2] = $("#pass2").val();      apl[1][4] = $("#ipaddr2").val();      apl[1][5] = $("#ipmask2").val();      apl[1][6] = $("#ipgateway2").val();      apl[2][1] = $("#ssid3").val();      apl[2][2] = $("#pass3").val();      apl[2][4] = $("#ipaddr3").val();      apl[2][5] = $("#ipmask3").val();      apl[2][6] = $("#ipgateway3").val();      apl[3][1] = $("#ssid4").val();      apl[3][2] = $("#pass4").val();      apl[3][4] = $("#ipaddr4").val();      apl[3][5] = $("#ipmask4").val();      apl[3][6] = $("#ipgateway4").val();      apl[4][1] = $("#ssid5").val();      apl[4][2] = $("#pass5").val();      apl[4][4] = $("#ipaddr5").val();      apl[4][5] = $("#ipmask5").val();      apl[4][6] = $("#ipgateway5").val();      if ($("#ssiden1").prop("checked") == true) apl[0][0] = 1;      else apl[0][0] = 0;      if ($("#ssiden2").prop("checked") == true) apl[1][0] = 1;      else apl[1][0] = 0;      if ($("#ssiden3").prop("checked") == true) apl[2][0] = 1;      else apl[2][0] = 0;      if ($("#ssiden4").prop("checked") == true) apl[3][0] = 1;      else apl[3][0] = 0;      if ($("#ssiden5").prop("checked") == true) apl[4][0] = 1;      else apl[4][0] = 0;      if ($("#dhcp1").prop("checked") == true) apl[0][3] = 1;      else apl[0][3] = 0;      if ($("#dhcp2").prop("checked") == true) apl[1][3] = 1;      else apl[1][3] = 0;      if ($("#dhcp3").prop("checked") == true) apl[2][3] = 1;      else apl[2][3] = 0;      if ($("#dhcp4").prop("checked") == true) apl[3][3] = 1;      else apl[3][3] = 0;      if ($("#dhcp5").prop("checked") == true) apl[4][3] = 1;      else apl[4][3] = 0;      if ($("#md_sta").prop("checked") == true) wrl[1] = "0";      else wrl[1] = "1";      // Преобразуем объект JavaScript в строку JSON      json_str = JSON.stringify(params);      // Отправляем устройству методом POST строку JSON с отредактированными параметрами      $.post("data.json", json_str, Show_cmd_status);    }    // Перейти на страницу отображения лога     function Show_log()    {      location.assign("device_log.html");    }    // По клику на кнопке с id = "submit_data" посылаем отредактированные данные обратно устройству      $("#submit_data").click(Data_send);    // По клику на кнопке с id = "reset_dev" посылаем команду сброса устройства    $("#reset_dev").click(function() {$.post("reset", "", Show_cmd_status)});    // Здесь сразу запрашиваем у устройства по протоколу AJAX текущие настройки     $.get("data.json", Data_accept);  </script></html>

Как видно в заголовке страница вместе с файлом HTML будут скачаны еще 4-е файла принадлежащие фреймворку. На самом деле будет скачано больше, поскольку файлы фреймворка скачивают сами ещё несколько нужных им файлов.

Содержимое JSON файла c данными
[ {  "Device": {   "CPU_ID": "5301646835393735C86643535454227D",   "SW_Ver": "V0.0.2",   "HW_Ver": "IoT Logger 1.0.0",   "CompDate": "Apr 16 2021",   "CompTime": "13:03:54"  } }, {  "Parameters": [   [    "leds_mode",    "1"   ],   [    "wifi_role",    "1"   ]  ] }, {  "Client_AP_list": [   [    1,    "SSID",    "PASS",    0,    "192.168.1.1",    "255.255.255.0",    "192.168.1.254"   ],   [    1,    "SSID",    "PASS",    0,    "192.168.1.1",    "255.255.255.0",    "192.168.1.254"   ],   [    1,    "SSID",    "PASS",    0,    "192.168.1.1",    "255.255.255.0",    "192.168.1.254"   ],   [    1,    "SSID",    "PASS",    0,    "192.168.1.1",    "255.255.255.0",    "192.168.1.254"   ],   [    1,    "SSID",    "PASS",    0,    "192.168.1.1",    "255.255.255.0",    "192.168.1.254"   ]  ] }]

И наконец посмотрим отзывчивость WEB интерфейса на примере платформы Sinergy

Диаграмма времени закачки страницы по протоколу HTTP Диаграмма времени закачки страницы по протоколу HTTP Диаграмма времени закачки страницы по протоколу HTTPSдерДиаграмма времени закачки страницы по протоколу HTTPSдер

Здесь надо отметить что для Sinergy у Azure RTOS есть драйвер аппаратного криптографического модуля, поэтому скорость HTTP и HTTPS отличаются всего лишь в два раза.
В случае программной реализации отличие будет более значительным.
Как обстоят дела с драйверами для криптографической периферии STM32 ещё предстоит выяснить.

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

Подробнее..

Перевод Пара мыслей о геттерах и сеттерах в C

11.06.2021 18:10:18 | Автор: admin

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

TL;DR: геттеры и сеттеры не очень хорошо подходят для структуроподобных объектов.

Введение

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

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

Производительность и геттеры

Допустим, у нас есть простая структура с обычными геттерами и сеттерами:

class PersonGettersSetters {  public:    std::string getLastName() const { return m_lastName; }    std::string getFirstName() const { return m_firstName; }    int getAge() const {return m_age; }        void setLastName(std::string lastName) { m_lastName = std::move(lastName); }    void setFirstName(std::string firstName) { m_firstName = std::move(firstName); }    void setAge(int age) {m_age = age; }  private:    int m_age = 26;    std::string m_firstName = "Antoine";    std::string m_lastName = "MORRIER";    };

Сравним эту версию с версией без геттеров и сеттеров.

struct Person {    int age = 26;    std::string firstName = "Antoine";    std::string lastName = "MORRIER";};

Она намного лаконичнее и надежнее. Здесь мы не можем, например, верну фамилию вместо имени.

Оба кода полностью функциональны. У нас есть класс Person с именем (firstName), фамилией (lastName) и возрастом (age). Однако предположим, что нам нужна функция, которая возвращает некоторую сводку по конкретному человеку.

std::string getPresentation(const PersonGettersSetters &person) {  return "Hello, my name is " + person.getFirstName() + " " + person.getLastName() +  " and I am " + std::to_string(person.getAge());}std::string getPresentation(const Person &person) {  return "Hello, my name is " + person.firstName + " " + person.lastName + " and I am " + std::to_string(person.age);}

Версия без геттеров выполняет эту задачу на 30% быстрее, чем версия с геттерами. Почему? Из-за возврата по значению в геттере. При возврате по значению создается копия, что снижает производительность. Давайте сравним производительность person.getFirstName(); и person.firstName.

Как видите, прямой доступ к полю имени без геттера эквивалентен noop.

Геттер по константной ссылке

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

class PersonGettersSetters {  public:    const std::string &getLastName() const { return m_lastName; }    const std::string &getFirstName() const { return m_firstName; }    int getAge() const {return m_age; }        void setLastName(std::string lastName) { m_lastName = std::move(lastName); }    void setFirstName(std::string firstName) { m_firstName = std::move(firstName); }    void setAge(int age) {m_age = age; }  private:    int m_age = 26;    std::string m_firstName = "Antoine";    std::string m_lastName = "MORRIER";    };

Так как мы получаем ту же производительность, что и в лаконичной версии, мы можем на этом успокоиться, не так ли? Прежде чем отвечать на этот вопрос, попробуйте выполнить этот код.

PersonGettersSetters make() {    return {};   }int main() {    auto &x = make().getLastName();         std::cout << x << std::endl;        for(auto x : make().getLastName()) {        std::cout << x << ",";       }}

Вы можете заметить некоторые странные символы, выведенные в консоли. Но почему? Что произошло, когда мы сделали make().getLastName()?

  1. Вы создаете экземпляр Person.

  2. Вы получаете ссылку на фамилию.

  3. Вы удаляете экземпляр Person.

И вот у нас есть висячая ссылка! Это может привести к крашам (в лучшем случае) или чему-то еще более худшему, чему-то, что можно найти только в фильмах ужасов.

Чтобы предупредить это, мы должны ввести ref-qualified функции.

class PersonGettersSetters {  public:    const std::string &getLastName() const & { return m_lastName; }    const std::string &getFirstName() const & { return m_firstName; }        std::string getLastName() && { return std::move(m_lastName); }    std::string getFirstName() && { return std::move(m_firstName); }        int getAge() const {return m_age; }        void setLastName(std::string lastName) { m_lastName = std::move(lastName); }    void setFirstName(std::string firstName) { m_firstName = std::move(firstName); }    void setAge(int age) {m_age = age; }      private:    int m_age = 26;    std::string m_firstName = "Antoine";    std::string m_lastName = "MORRIER";    };

Вот новое решение, которое будет работать везде. Вам нужно два геттера. Один для lvalue и один для rvalue (как xvalue, так и для prvalue).

Проблемы с сеттерами

Тут особо нечего сказать. Если вы хотите добиться максимальной производительности, вы должны написать один сеттер, который принимает lvalue, и один, который принимает rvalue. Однако, как правило, достаточно иметь всего один сеттер, который принимает перемещаемое значение. Тем не менее, вам придется расплатиться за это дополнительным move. Однако таким образом у вас не получится производить небольшие изменения в переменных. Вы должны заменять всю переменную целиком. Если вы просто хотите заменить одну букву A в имени на D, то вы не сможете сделать это с помощью сеттеров. Однако с помощью прямого доступа так делать можно.

А как насчет иммутабельных переменных?

Кто-то может посоветовать вам просто сделать атрибут члена const. Однако меня это решение не устраивает. Создание константы предотвратит move-семантику и приведет к ненужному копированию.

У меня нет волшебного решения, которое я мог бы предложить вам прямо сейчас. Тем не менее, мы можем написать обертку, которую мы можем назвать immutable<T>. Эта обертка должна быть:

  1. Constructible

  2. Так как она immutable, она не должна быть assignable

  3. Она может быть copy constructible или move constructible

  4. Она должна быть конвертируемой в const T&, будучи lvalue

  5. Она должна быть конвертируемой в T, будучи rvalue

  6. Она должна использоваться, как и другие оболочки, с помощью оператора * или оператора ->.

  7. Получить адрес базового объекта должно быть легко.

Вот небольшая реализация:

#define FWD(x) ::std::forward<decltype(x)>(x)template <typename T>struct AsPointer {    using underlying_type = T;    AsPointer(T &&v) noexcept : v{std::move(v)} {}    T &operator*() noexcept { return v; }    T *operator->() noexcept { return std::addressof(v); }    T v;};template <typename T>struct AsPointer<T &> {    using underlying_type = T &;    AsPointer(T &v) noexcept : v{std::addressof(v)} {}    T &operator*() noexcept { return *v; }    T *operator->() noexcept { return v; }    T *v;};template<typename T>class immutable_t {  public:    template <typename _T>    immutable_t(_T &&t) noexcept : m_object{FWD(t)} {}    template <typename _T>    immutable_t &operator=(_T &&) = delete;    operator const T &() const &noexcept { return m_object; }    const T &operator*() const &noexcept { return m_object; }    AsPointer<const T &> operator->() const &noexcept { return m_object; }    operator T() &&noexcept { return std::move(m_object); }    T operator*() &&noexcept { return std::move(m_object); }    AsPointer<T> operator->() &&noexcept { return std::move(m_object); }    T *operator&() &&noexcept = delete;    const T *operator&() const &noexcept { return std::addressof(m_object); }    friend auto operator==(const immutable_t &a, const immutable_t &b) noexcept { return *a == *b; }    friend auto operator<(const immutable_t &a, const immutable_t &b) noexcept { return *a < *b; }  private:    T m_object;};

Таким образом, для иммутабельного объекта Person вы можете просто написать:

struct ImmutablePerson {    immutable_t<int> age = 26;    immutable_t<std::string> firstName = "Antoine";    immutable_t<std::string> lastName = "MORRIER";};

Заключение

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

  • 3-х геттеров (или даже 4-х): const lvalue, rvalue, const rvalue и, по вашему усмотрению, для неконстантного lvalue (даже если это уже просто очень странно звучит, так как проще использовать прямой доступ)

  • 1 сеттер (или 2, если вы хотите выжать максимальную производительность).

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

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

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

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

Ну а что думаете вы? Используете ли вы геттеры и сеттеры? И почему?


Перевод материала подготовлен в рамках курса "C++ Developer. Basic". Всех желающих приглашаем на двухдневный онлайн-интенсив HTTPS и треды в С++. От простого к прекрасному. В первый день интенсива мы настроим свой http-сервер и разберем его что называется от и до. Во второй день произведем все необходимые замеры и сделаем наш сервер супер быстрым, что поможет нам понять на примере, чем же все-таки язык С++ лучше других. Регистрация здесь

Подробнее..

Обходим проверку сертификата SSL

13.12.2020 00:13:26 | Автор: admin

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





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


Всем известно, что при посещении сайта у которого временно что-то случилось c сертификатом вы обнаружите предупреждение, которое показывается, если сертификат безопасности не является доверенным net::ERR_CERT_AUTHORITY_INVALID?


Привет онлайн-кинотеатрам

Все современные браузеры показывают сообщение об ошибке HSTS


Что такое HSTS
HSTS (HTTP Strict Transport Security) это механизм защиты от даунгрейд-атак на TLS, указывающий браузеру всегда использовать TLS для сайтов с соответствующими политиками.

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



Но не во всех браузерах как оказывается, есть данная возможность. Так я столкнулся с данной проблемой в Chrome на Mac OS


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


Ну что ж, поскольку, вести разработку в других, более сговорчивых браузерах было не комфортно, вот способы как обойти эту проблему:


Все хромоподобные браузеры (Chrome, Opera, Edge ) могут открыть небезопасную веб страницу, если на английской раскладке клавиатуры набрать фразу:


thisisunsafe


прямо на данной веб странице. Это даст возможность работать с сайтом без оповещение об ошибке на момент текущей сессии браузера, пока вы не закроете вкладку Chrome.


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


Для Windows


C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --ignore-certificate-errors




Для Mac OS


/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ignore-certificate-errors --ignore-urlfetcher-cert-requests &> /dev/null


Achtung! Данные манипуляции необходимо выполнять с выключенным Chrome приложением, иначе чуда не произойдет.


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


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



*Так же есть метод с добавлением сертификатов тестируемого сайта в конфиги браузера Настройки->Безопасность->Настроить сертификаты->Импорт но мне он показался не продуктивным и очень муторным, поэтому не привожу


Надеюсь моя краткая статья кому-то пригодится при разработке и тестировании сайтов =)

Подробнее..

Размещаем сайт на домашнем роутере

04.07.2020 18:11:54 | Автор: admin
Мне давно хотелось потрогать руками интернет-сервисы, настроив веб-сервер с нуля и выпустив его в Интернет. В этой статье хочу поделиться полученным опытом превращения домашнего роутера из узкофункционального устройства в практически полноценный сервер.

Началось всё с того, что служивший верой и правдой роутер TP-Link TL-WR1043ND перестал удовлетворять потребности домашней сети, захотелось 5ГГц диапазона и быстрого доступа к файлам на накопителе, подключенном к роутеру. Просмотрев профильные форумы (4pda, ixbt), сайты с отзывами и посмотрев на ассортимент местных магазинов решил приобрести Keenetic Ultra.

В пользу именно этого устройства сработали хорошие отзывы владельцев:
отсутствие проблем с перегревом (тут пришлось отказаться от продукции Asus);
надежность в работе (тут вычеркнул TP-Link);
простота в настройке (побоялся не справиться и вычеркнул Microtik).

Пришлось примириться с минусами:
нет WiFi6, хотелось взять оборудование с запасом на будущее;
4 LAN порта, хотелось больше, но это уже не домашняя категория.

В итоге получилась вот такая серверная:



слева оптический терминал Ростелекома;
справа наш подопытный роутер;
проводом к роутеру подсоединен завалявшийся m.2 SSD на 128 ГБ, помещенный в коробку USB3 с алиэкспресса, сейчас он аккуратно закреплен на стенке;
на переднем плане удлинитель с независимым отключением розеток, провод от него идет к недорогому UPS;
на заднем плане пучок витой пары на этапе ремонта квартиры сразу запланировал RJ45 розетки в местах предполагаемого размещения техники, чтобы не зависеть от замусоренности WiFi.

Итак, у нас есть оборудование, необходимо его настроить:



первичная настройка роутера занимает около 2 минут, указываем параметры подключения к провайдеру (у меня оптический терминал переключен в режим бриджа, PPPoE соединение поднимает роутер), название WiFi сети и пароль в принципе всё, роутер запускается и работает.



Ставим переадресацию внешних портов на порты самого роутера в разделе Сетевые правила Переадресация:





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

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

берем предназначенный для этой роли накопитель (флешку, карту памяти в картридере, жесткий диск или SSD во внешнем боксе и форматируем в Ext4 с помощью MiniTool Partition Wizard Free Edition (у меня нет компьютера с linux под рукой, там можно встроенными средствами). Как я понимаю, при работе система пишет на флешку только логи, поэтому, если их ограничить после настройки системы можно использовать и карты памяти, если планируете много и часто писать на накопитель лучше SSD или HDD.



После этого подключаем накопитель к роутеру и наблюдаем его на экране системного монитора



Переходим щелчком по USB-диски и принтеры в раздел Приложения и настраиваем общий ресурс в разделе Сеть Windows:



И у нас имеется сетевой ресурс, который можно использовать с компьютеров под Windows, подключив при необходимости как диск: net use y: \\192.168.1.1\SSD /persistent:yes

Скорость такого импровизированного NAS вполне достаточна для домашнего применения, по проводу он использует весь гигабит, по WiFi скорость составляет около 400-500 мегабит.



Настройка хранилища один из необходимых шагов для настройки сервера, далее нам нужно:
приобрести домен и статический IP адрес (можно обойтись и без этого, используя Dynamic DNS, но статический IP у меня уже был, поэтому проще оказалось воспользоваться бесплатными сервисами Яндекса делегировав туда домен, мы получаем DNS-хостинг и почту на своем домене);



настроить DNS сервера и добавить A-записи, указывающие на ваш IP:



Вступление в силу настроек делегирования домена и DNS занимает несколько часов, поэтому параллельно занимаемся настройкой роутера.

Для начала необходимо установить репозиторий Entware, из которого мы сможем ставить на роутер необходимые пакеты. Я воспользовался этой инструкцией, только не заливал установочный пакет по FTP, а создал папку прямо на подключенном ранее сетевом диске и скопировал туда файл обычным способом.

Получив доступ по SSH, меняем пароль командой passwd и ставим командой opkg install [имена пакетов] все нужные пакеты:



В ходе настройки на роутере оказались установлены следующие пакеты (результат вывода команды opkg list-installed):

Список пакетов
bash 5.0-3
busybox 1.31.1-1
ca-bundle 20190110-2
ca-certificates 20190110-2
coreutils 8.31-1
coreutils-mktemp 8.31-1
cron 4.1-3
curl 7.69.0-1
diffutils 3.7-2
dropbear 2019.78-3
entware-release 1.0-2
findutils 4.7.0-1
glib2 2.58.3-5
grep 3.4-1
ldconfig 2.27-9
libattr 2.4.48-2
libblkid 2.35.1-1
libc 2.27-9
libcurl 7.69.0-1
libffi 3.2.1-4
libgcc 8.3.0-9
libiconv-full 1.11.1-4
libintl-full 0.19.8.1-2
liblua 5.1.5-7
libmbedtls 2.16.5-1
libmount 2.35.1-1
libncurses 6.2-1
libncursesw 6.2-1
libndm 1.1.10-1a
libopenssl 1.1.1d-2
libopenssl-conf 1.1.1d-2
libpcap 1.9.1-2
libpcre 8.43-2
libpcre2 10.34-1
libpthread 2.27-9
libreadline 8.0-1a
librt 2.27-9
libslang2 2.3.2-4
libssh2 1.9.0-2
libssp 8.3.0-9
libstdcpp 8.3.0-9
libuuid 2.35.1-1
libxml2 2.9.10-1
locales 2.27-9
mc 4.8.23-2
ndmq 1.0.2-5a
nginx 1.17.8-1
openssl-util 1.1.1d-2
opkg 2019-06-14-dcbc142e-2
opt-ndmsv2 1.0-12
php7 7.4.3-1
php7-mod-openssl 7.4.3-1
poorbox 1.31.1-2
terminfo 6.2-1
zlib 1.2.11-3
zoneinfo-asia 2019c-1
zoneinfo-europe 2019c-1


Возможно, тут что-то лишнее затесалось, но места на накопителе много, поэтому разбираться не стал.

После установки пакетов настраиваем nginx, я пробовал с двумя доменами на втором настроен https, и пока висит заглушка. 81 и 433 внутренние порты вместо 80 и 443 используются, поскольку на нормальных портах висят админки роутера.

etc/nginx/nginx.conf
user nobody;
worker_processes 1;
#error_log /opt/var/log/nginx/error.log;
#error_log /opt/var/log/nginx/error.log notice;
#error_log /opt/var/log/nginx/error.log info;
#pid /opt/var/run/nginx.pid;

events {
worker_connections 64;
}

http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log /opt/var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;

server {
listen 81;
server_name milkov.su www.milkov.su;
return 301 milkov.su$request_uri;
}

server {
listen 433 ssl;
server_name milkov.su;
#SSL support
include ssl.conf;
location / {
root /opt/share/nginx/html;
index index.html index.htm;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

etc/nginx/ssl.conf
ssl_certificate /opt/etc/nginx/certs/milkov.su/fullchain.pem;
ssl_certificate_key /opt/etc/nginx/certs/milkov.su/privkey.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
ssl_dhparam /opt/etc/nginx/dhparams.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_stapling on;


Для того, чтобы сайт работал по https, воспользовался известным скриптом dehydrated, установив его по этой инструкции. Затруднений этот процесс не вызвал, запнулся только на том, что в тексте скрипта для работы на моем роутере надо закомментировать строчку в файле /opt/etc/ssl/openssl.cnf:

[openssl_conf]
#engines=engines

И отмечу, что генерация dhparams.pem командой openssl dhparam -out dhparams.pem 2048 на моем роутере занимает больше 2 часов, если бы не индикатор прогресса потерял бы терпение и перезагрузил.

После получения сертификатов перезапускаем nginx командой "/opt/etc/init.d/S80nginx restart". В принципе на этом настройка закончена, но сайта еще нет если положим в каталог /share/nginx/html файл index.html, увидим заглушку.
index.html
<!DOCTYPE html>
Тестовая страничка!


Тестовая страничка!


Это простая статическая тестовая страничка, абсолютно ничего интересного.






Чтобы разместить информацию красиво, непрофессионалу типа меня проще воспользоваться готовыми шаблонами, после долгого перебора различных каталогов нашел templatemo.com там неплохой выбор бесплатных шаблонов, не требующих обязательного указания авторства (что редкость в интернете, большая часть шаблонов в лицензии требуют сохранить ссылку на ресурс, откуда они получены).

Выбираем подходящий шаблон там есть на самые разные случаи, скачиваем архив, и раcпаковываем его в каталог /share/nginx/html, делать это можно уже со своего компьютера, затем редактируем шаблон (тут потребуются минимальные знания HTML, чтобы не нарушить структуру) и заменяем графику, как показано на рисунке ниже.



Резюме: роутер вполне пригоден для размещения на нем легкого сайта, в принципе если не предполагается большой нагрузки, можно поставить и php, и экспериментировать с более сложными проектами (смотрю на nextcloud/owncloud, вроде есть успешные установки на такое железо). Возможность установки пакетов поднимает его полезность например, когда надо было защитить RDP порт ПК в локальной сети, поставил knockd на роутер и проброс порта к ПК открывался только после port knocking.

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

Основы Flutter для начинающих (Часть VI)

05.06.2021 14:06:41 | Автор: admin

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

Помимо кастомизации, Flutter предоставляет возможность обработки ошибок и валидации полей формы.

И сегодня мы постараемся разобраться с этой темой на небольшом примере.

Ну что ж, погнали!

Наш план
  • Часть 1- введение в разработку, первое приложение, понятие состояния;

  • Часть 2- файл pubspec.yaml и использование flutter в командной строке;

  • Часть 3- BottomNavigationBar и Navigator;

  • Часть 4- MVC. Мы будем использовать именно этот паттерн, как один из самых простых;

  • Часть 5 - http пакет. Создание Repository класса, первые запросы, вывод списка постов;

  • Часть 6 (текущая статья) - работа с формами, текстовые поля и создание поста.

  • Часть 7 - работа с картинками, вывод картинок в виде сетки, получение картинок из сети, добавление своих в приложение;

  • Часть 8 - создание своей темы, добавление кастомных шрифтов и анимации;

  • Часть 9 - немного о тестировании;

Создание формы: добавление поста

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

@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: Text("Post List Page"),    ),    body: _buildContent(),    // в первой части мы уже рассматривали FloatingActionButton    floatingActionButton: FloatingActionButton(      child: Icon(Icons.add),      onPressed: () {      },    ),  );}

Далее создадим новую страницу в файле post_add_page.dart:

import 'package:flutter/material.dart';class PostDetailPage extends StatefulWidget {  @override  _PostDetailPageState createState() => _PostDetailPageState();}class _PostDetailPageState extends State<PostDetailPage> {    // TextEditingController'ы позволят нам получить текст из полей формы  final TextEditingController titleController = TextEditingController();  final TextEditingController contentController = TextEditingController();    // _formKey пригодится нам для валидации  final _formKey = GlobalKey<FormState>();    @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("Post Add Page"),        actions: [          // пункт меню в AppBar          IconButton(            icon: Icon(Icons.check),            onPressed: () {              // сначала запускаем валидацию формы              if (_formKey.currentState!.validate()) {                // здесь мы будем делать запроc на сервер              }            },          )        ],      ),      body: Padding(        padding: EdgeInsets.all(15),        child: _buildContent(),      ),    );  }  Widget _buildContent() {    // построение формы    return Form(      key: _formKey,      // у нас будет два поля      child: Column(        children: [          // поля для ввода заголовка          TextFormField(            // указываем для поля границу,            // иконку и подсказку (hint)            decoration: InputDecoration(                border: OutlineInputBorder(),                prefixIcon: Icon(Icons.face),                hintText: "Заголовок"            ),            // не забываем указать TextEditingController            controller: titleController,            // параметр validator - функция которая,            // должна возвращать null при успешной проверки            // или строку при неудачной            validator: (value) {              // здесь мы для наглядности добавили 2 проверки              if (value == null || value.isEmpty) {                return "Заголовок пустой";              }              if (value.length < 3) {                return "Заголовок должен быть не короче 3 символов";              }              return null;            },          ),          // небольшой отступ между полями          SizedBox(height: 10),          // Expanded означает, что мы должны          // расширить наше поле на все доступное пространство          Expanded(            child: TextFormField(              // maxLines: null и expands: true               // указаны для расширения поля на все доступное пространство              maxLines: null,              expands: true,              textAlignVertical: TextAlignVertical.top,              decoration: InputDecoration(                  border: OutlineInputBorder(),                  hintText: "Содержание",              ),              // не забываем указать TextEditingController              controller: contentController,              // также добавляем проверку поля              validator: (value) {                if (value == null || value.isEmpty) {                  return "Содержание пустое";                }                return null;              },            ),          )        ],      ),    );  }}

Не забудьте добавить переход на страницу формы:

floatingActionButton: FloatingActionButton(   child: Icon(Icons.add),   onPressed: () {      Navigator.push(context, MaterialPageRoute(         builder: (context) => PostDetailPage()      ));   },),

Запускаем и нажимаем на кнопку:

Вуаля! Форма работает.

Небольшая заметка

У новичков могут возникнуть проблемы даже с готовым кодом. И это не издевательство, такое бывает.

Поэтому для 100%-ной работы коды постарайтесь использовать схожие версии Flutter и Dart с моими:

  • Flutter 2.0.6

  • Dart SDK version: 2.12.3

Также в комментах я обратил внимание на null safety. Это очень важно, я позабыл об этом и это мой косяк.

Я уже добавил в приложение поддержку null safety. Вы наверно обратили внимание на восклицательный знак:

// ! указывает на то, что мы 100% уверены// что currentState не содержит null значение_formKey.currentState!.validate()

О null safety и о её поддержи в Dart можно сделать целый цикл статей, а возможно и написать целую книгу.

Мы задерживаться не будем и переходим к созданию POST запроса.

POST запрос для добавления данных на сервер

POST, как уже было отмечено, является одним из HTTP методов и служит для добавления новых данных на сервер.

Для начала добавим модель для нашего результата и изменим немного класс Post:

class Post {  // все поля являются private  // это сделано для инкапсуляции данных  final int? _userId;  final int? _id;  final String? _title;  final String? _body;  // создаем getters для наших полей  // дабы только мы могли читать их  int? get userId => _userId;  int? get id => _id;  String? get title => _title;  String? get body => _body;  // добавим новый конструктор для поста  Post(this._userId, this._id, this._title, this._body);  // toJson() превращает Post в строку JSON  String toJson() {    return json.encode({      "title": _title,      "content": _body    });  }  // Dart позволяет создавать конструкторы с разными именами  // В данном случае Post.fromJson(json) - это конструктор  // здесь мы принимаем объект поста и получаем его поля  // обратите внимание, что dynamic переменная  // может иметь разные типы: String, int, double и т.д.  Post.fromJson(Map<String, dynamic> json) :    this._userId = json["userId"],    this._id = json["id"],    this._title = json["title"],    this._body = json["body"];}// у нас будут только два состоянияabstract class PostAdd {}// успешное добавлениеclass PostAddSuccess extends PostAdd {}// ошибкаclass PostAddFailure extends PostAdd {}

Затем создадим новый метод в нашем Repository:

// добавление поста на серверFuture<PostAdd> addPost(Post post) async {  final url = Uri.parse("$SERVER/posts");  // делаем POST запрос, в качестве тела  // указываем JSON строку нового поста  final response = await http.post(url, body: post.toJson());  // если пост был успешно добавлен  if (response.statusCode == 201) {    // говорим, что все ок    return PostAddSuccess();  } else {    // иначе ошибка    return PostAddFailure();  }}

Далее добавим немного кода в PostController:

// добавление поста// функция addPost будет принимать callback,// через который мы будет получать результатvoid addPost(Post post, void Function(PostAdd) callback) async {  try {    final result = await repo.addPost(post);    // сервер вернул результат    callback(result);  } catch (error) {    // произошла ошибка    callback(PostAddFailure());  }}

Ну что ж пора нам вернуться к нашему представлению PostAddPage:

class PostDetailPage extends StatefulWidget {  @override  _PostDetailPageState createState() => _PostDetailPageState();}// не забываем поменять на StateMVCclass _PostDetailPageState extends StateMVC {  // _controller может быть null  PostController? _controller;  // получаем PostController  _PostDetailPageState() : super(PostController()) {    _controller = controller as PostController;  }  // TextEditingController'ы позволят нам получить текст из полей формы  final TextEditingController titleController = TextEditingController();  final TextEditingController contentController = TextEditingController();  // _formKey нужен для валидации формы  final _formKey = GlobalKey<FormState>();  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("Post Add Page"),        actions: [          // пункт меню в AppBar          IconButton(            icon: Icon(Icons.check),            onPressed: () {              // сначала запускаем валидацию формы              if (_formKey.currentState!.validate()) {                // создаем пост                // получаем текст через TextEditingController'ы                final post = Post(                  -1, -1, titleController.text, contentController.text                );                // добавляем пост                _controller!.addPost(post, (status) {                  if (status is PostAddSuccess) {                    // если все успешно то возвращаемя                    // на предыдущую страницу и возвращаем                    // результат                    Navigator.pop(context, status);                  } else {                    // в противном случае сообщаем об ошибке                    // SnackBar - всплывающее сообщение                    ScaffoldMessenger.of(context).showSnackBar(                      SnackBar(content: Text("Произошла ошибка при добавлении поста"))                    );                  }                });              }            },          )        ],      ),      body: Padding(        padding: EdgeInsets.all(15),        child: _buildContent(),      ),    );  }  Widget _buildContent() {    // построение формы    return Form(      key: _formKey,      // у нас будет два поля      child: Column(        children: [          // поля для ввода заголовка          TextFormField(            // указываем для поля границу,            // иконку и подсказку (hint)            decoration: InputDecoration(                border: OutlineInputBorder(),                prefixIcon: Icon(Icons.face),                hintText: "Заголовок"            ),            // указываем TextEditingController            controller: titleController,            // параметр validator - функция которая,            // должна возвращать null при успешной проверки            // и строку при неудачной            validator: (value) {              // здесь мы для наглядности добавили 2 проверки              if (value == null || value.isEmpty) {                return "Заголовок пустой";              }              if (value.length < 3) {                return "Заголовок должен быть не короче 3 символов";              }              return null;            },          ),          // небольшой отступ между полями          SizedBox(height: 10),          // Expanded означает, что мы должны          // расширить наше поле на все доступное пространство          Expanded(            child: TextFormField(              // maxLines: null и expands: true              // указаны для расширения поля              maxLines: null,              expands: true,              textAlignVertical: TextAlignVertical.top,              decoration: InputDecoration(                  border: OutlineInputBorder(),                  hintText: "Содержание",              ),              // указываем TextEditingController              controller: contentController,              // также добавляем проверку поля              validator: (value) {                if (value == null || value.isEmpty) {                  return "Содержание пустое";                }                return null;              },            ),          )        ],      ),    );  }}

Логика работы следующая:

  1. мы нажаем добавить новый пост

  2. открывается окно с формой, вводим данные

  3. если все ок, то возвращаемся на предыдущую страницу и сообщаем об этом иначе выводим сообщение об ошибке.

Заключительный момент, добавим обработку результата в PostListPage:

floatingActionButton: FloatingActionButton(  child: Icon(Icons.add),  onPressed: () {    // then возвращает объект Future    // на который мы подписываемся и ждем результата    Navigator.push(context, MaterialPageRoute(      builder: (context) => PostDetailPage()    )).then((value) {      if (value is PostAddSuccess) {        // SnackBar - всплывающее сообщение        ScaffoldMessenger.of(context).showSnackBar(         SnackBar(content: Text("Пост был успешно добавлен"))        );      }    });  },),

Теперь тестируем:

К сожалению JSONPlaceholder на самом деле не добавляет пост и поэтому мы не сможем его увидеть среди прочих постов.

Заключение

Я надеюсь, что убедил вас в том, что работа с формами на Flutter очень проста и не требует почти никаких усилий.

Большая часть кода - это создание POST запроса на сервер и обработка ошибок.

Полезные ссылки

Всем хорошего кода)

Подробнее..

Суверенный DNS уже здесь, а вы и не заметили

03.03.2021 22:15:39 | Автор: admin

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

Совсем недавно, буквально "на днях", в новостях проскакивали сообщения о недоступности страницы публичного DNS от Cloudflare ( https://1.1.1.1 ) из сетей российских провайдеров, например, Ростелекома, что навело меня на мысль вернуться к изучению вопроса.

Быстрый поиск по профильным ресурсам связистов показал, что уже много месяцев происходит процесс осувереннивания российского сегмента сети. Например, Роскомнадзор строит свой аналог базы RIPE для российских провайдеров и пользователей интернета. И, внезапно, национальную систему доменных имён. На форуме НАГ попадался даже документ с инструкциями по перенастройке провайдерских DNS на суверенный манер, со страшным названием "Инструкция по подключению операторов связи и владельцев АС к Национальной системе доменных имен (НСДИ).

Беглое изучение этого документа приводит к следующим выводам: НСДИ уже активно применяется. В документе приводятся несколько вариантов использования НСДИ, в том числе с возможной подменой корневых DNS (см. статью в wikipedia). С большой вероятностью настройки DNS, которые выдает провайдер вашему устройству, уже используют НСДИ. Другой вывод: в настоящее время функционирование НСДИ обеспечивается мощностями MSK-IX , что следует из принадлежности IP адресов в "Инструкции".

На момент написания статьи сервера НСДИ отдают ту же информацию, что есть в файле root.hints в современных ОС (оригинал файла находится по адресу https://www.internic.net/domain/named.root ). У меня не сложилось однозначного понимания, как НСДИ поможет осуверенниванию и какие плюсы и минусы этого решения. Прошу прокомментировать тех, кто разобрался в вопросе глубже.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru