
Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.
Розповідає автор блогу m1el.github.io
У цій статті я розповім Вам про найдорожчий антипатерн, який я знаю, – управління структурованими даними з використанням рядкових методів. Я називатиму це “антипатерн printf”.
“Найдорожчий антипатерн” не порожні слова
Я підрахував кількість уразливостей за їх типами, використовуючи дані із cve.mitre.org, і ось, що я отримав:
- rexec: 19268
- DoS: 14849
- xss: 9236
- memory: 8212
- sqlinj: 6230
- privilege: 3321
- dirtraversal: 2762
- arith: 1260
- csrf: 1117
Ви можете критикувати мене за те, як я це рахував, і спробувати зробити це самостійно.
Ви можете помітити, що XSS (Cross Site Scripting – “Міжсайтовий скриптинг”) і SQL ін’єкції є значною частиною списку. Я вважаю, що в більшості вразливостей цих типів винен саме антипатерн printf. Вставляти якісь рядки прямо в HTML – жахлива ідея. І з SQL те саме.
Приклади використання на різних мовах
Тепер, коли Ви знаєте значення антипатерну, я доведу Вам, що це повсюдне явище. Ви можете думати, що він властивий тільки для HTML і SQL – саме тому і виникли вразливості.
- Майже кожен сайт на PHP містить щось на кшталт: Hello, !
- SQL запит сформований за допомогою рядкових операцій (див. документацію з
mysql_query
) :$query = sprintf ("SELECT firstname, lastname, address, age FROM friends WHERE firstname='%s' AND lastname='%s'", mysql_real_escape_string ($firstname), mysql_real_escape_string ($lastname));
- Багато програмістів створюють динамічні елементи в JavaScript, використовуючи щось віддалено innerHtml (наприклад, тут), що нагадує:
var OriginalContent = $ (this).text ();$(this).html ("");
- Програміст-бідолаха, якого змусили писати динамічний сайт на C, може видати:
sprintf (buffer, "%s24時間
“, …)
- Генерування JSON з використанням строкового форматування (наприклад, тут) :
git log --pretty=format:'{ %n " commit": "%H",%n "abbreviated_commit": "%h",%n " tree": "%T",%n "abbreviated_tree": "%t",%n " parent": "%P",%n "abbreviated_parent": "%p",%n " refs": "%D",%n " encoding": "%e",%n " subject": "%s",%n "sanitized_subject_line": "%f",%n " body": "%b",%n "commit_notes": "%N",%n "verification_flag": "%G"?,%n " signer": "%GS",%n "signer_key": "%GK",%n " author": { %n " name": "%aN",%n " email": "%aE",%n " date": "%aD"%n },%n " commiter": { %n " name": "%cN",%n " email": "%cE",%n " date": "%cD"%n }%n},'
- Використання
sed
для роботи з XML:# http://askubuntu.com/questions/442013/using-sed-to-search-and-replace-text-in-xml-filesed - i 's##UpdateAndExit#' File.XML# http://askubuntu.com/questions/284983/print-text-between-two-xml-tagssed - n '/<serverName/,/</serverName/p' big_xml_file.xml
- Генерування коду за допомогою ExtJS (наприклад, тут):
'', ' dest["{name}"] = value === undefined ? __field{#}.convert (__field{#}.defaultValue, record) : __field{#}.convert (value, record);n',...// використання експлоита :// Ext.define ('m',{extend:'Ext.data.Model',fields:['id']});// var store = Ext.create ('Ext.data.Store',{model:m});// store.loadRawData ({metaData:{fields:['"+alert (1) +"']}});
- MAL (Make – A – Lisp) задає
load-file
, як результат конкатенції рядків:
Define a load – file function using mal itself. In your main program call the rep function with this string: ” (def! load – file (fn* (f) (eval (read – string (str ” (do ” (slurp f) “)”))))) “.
Як же правильно обробляти дані?
Отже, якщо я використовую рядкові функції для генерації HTML, що мені треба використати замість цього?
Як Ви можете бачити нижче, всі запропоновані рішення мають один сенс – використати спеціальні методи створення об’єктів, а лише потім серіалізувати їх. Головне не обробляти серіалізовані дані як рядки.
Для HTML
- hiccup (більше прикладів тут):
(html [:ul (for [x (range 1 4)] [:li x])])(defn index [] [:div {:id " content"} [:h1 {:class "text-success"} "Hello Hiccup"]])
- lxml e – factory:
html = page = ( E.html ( # create an Element called " html" E.head ( E.title ("This is a sample document") ), E.body ( E.h1 ("Hello"!, CLASS ("title")), E.p ("This is a paragraph with ", E.b ("bold"), " text in it"!), E.p ("This is another paragraph, with a", "n ", E.a ("link", href="http://www.python.org") "".), E.p ("Here are some reservered characters: <spam&egg>".), ) ))
- Працюйте з DOM (Document Object Model), потім серіалізуйте; простий приклад:
html = etree.parse ('template.html') name_node = html.xpath ('//div[@id="user-name"]')[0] name_node.text = user.nameprint (etree.tostring (html))
- Використайте серверний AngularJS або React.
- Управляйте DOM безпосередньо, наприклад, через jQuery:
var OriginalContent = $ (this).text ();$(this).empty ().append ($('').val (OriginalContent)) // Жахливий код! Жахливий!// $(this).html ("");
Для JSON
- Використайте літерали або list compehensions для створення об’єкта JSON, потім серіалізуйте його;
- генеруйте JSON об’єкти, а потім серіалізуйте.
Для SQL
- Використайте placeholder ‘и (параметри запиту);
- працюйте з абстрактними деревами синтаксису (Abstract Syntax Trees – AST) SQL, а потім робіть запит.
Для XML
- Працюйте з DOM, потім переводьте в XML;
- користуйтеся XSLT.
Для мета-програмування
- Використайте Lisp;
- працюйте з AST для Вашої мови.
Де корінь проблеми?
На мій погляд, у лінощах.
– О, ми можемо створити розмітку просто сполучаючи рядки? Авжеж, давайте так і зробимо!
– Проте якщо рядок міститиме<img/src/onerror=alert#00>
це призведе до виконання коду JavaScript!
– Дідько, ну давай напишемо функціюhtmlspecialchars
і викликатимемо її щоразу, коли треба буде вставити текст у HTML.
– Однак якщо написати, JS все одно спрацює!
– Тільки ідіот писатиме таке!
– А якщо ми забудемо викликати цю функцію в якому-небудь місці, у нас виникнуть проблеми?
– Просто писатимемо акуратніше наступного разу.
І ось, ми генеруємо HTML, використовуючи об’єднання рядків. Звичайно, якщо Ви писатимете код дуже акуратно, то уникнете вразливостей. Навіщо Вам такі проблеми? Ви ж не використовуєте ручне управління пам’яттю для створення веб-сайту, чи не так? Немає сенсу ходити над прірвою, якщо Ви можете просто писати у безпечному ключі.
Трохи про браузери
У цієї історії є веселіша сторона – браузери. Їх розробники пристосовували свої парсери для роботи з неправильним HTML, оскільки багато сайтів містили кривий код. Люди вибирали браузери, які могли сприймати як HTML буквально довільні набори байтів і коректно відображали їх улюблені веб-сайти. Так само були розроблені XSS фільтри, оскільки сайти записували необроблені запити користувачів прямо в HTML. Суть таких фільтрів проста – якщо браузер може запобігти 90 % XSS атак на користувача, користувач буде задоволений. Так що, ці фільтри не можуть захистити від усіх атак.
Браузери борються лише з симптомами проблеми – сама проблема комфортно почувається в головах програмістів, які думають, що строкові функції – відповідний інструмент для створення динамічних веб-сторінок.
Насамкінець
Структура HTML жахлива тому, що від початку люди управляли нею як рядками. Дуже багато проблем (включаючи, але не обмежуючись: XSS, некоректна розмітка, різний парсинг браузерів) зумовлені неправильним розумінням суті формату HTML.
Припустимо, що доступні інструменти не підштовхували б людей до створення HTML у вигляді рядків – Інтернет тоді був би кращим.
Припустимо, що ми вибрали б інший шлях для обробки веб-сторінок, тоді ми не сприймали б його як рядок, який можна записати через printf.
Можна з абсолютною впевненістю сказати, що вразливостей було б значно менше, якби програмісти не сприймали структурні формати даних як щось, для роботи з чим можна використати рядкові функції.
Переклад виконав Петро Сокових
Оригінал: “The Most Expensive Anti – Patternf”
Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?