4 шага проверки входных данных приложением?
Любое приложение, которое взаимодействует с внешним миром посредством каких либо интерфейсов, сталкивается с задачей фильтрации и проверки вводимой информации.
Количество разработчиков и производимых ими продуктов растёт, а их квалификация и качество падает. Моё мнение: это связано с отсутствием инженерных навыков у современных разработчиков, так как современный мир убеждает, что достаточно закончить курсы, а образование, умение структурированно мыслить и учиться новому это удел отраслевых пенсионеров.
Чтобы закрыть эту зияющую дыру, я решил написать этот небольшой ликбез.
Ниже будет описана последовательность действий для проверки входных данных, которые я сформировал исходя из личного опыта. Я не исключаю, что его можно дополнить или улучшить, либо реализовать иначе, но фундаментально вряд ли что-то изменится.
1. Проверка на наличие
Любые входные данные могу содержать как обязательные значения/поля, так и необязательные.
Эта проверка касается исключительно обязательных величин.
Приложению ресурсно выгодно отсечь неправильные входные данные, не углубляясь в ресурсозатратные проверки.
Что нужно сделать:
- проверить наличие всех обязательных значений/полей;
- если хотя бы одного значения/поля нет, то необходимо прекратить дальнейшую проверку и вернуть ответ с ошибкой.
На что приложение потратит ресурсы на этом этапе:
- на парсинг входных данных, если это необходимо;
- на проверку наличия обязательных значений/полей.
Такими минимальными затратами приложение может уже отсечь часть некорректных входных данных. В случае веб-приложения, этот высвободившийся ресурс может пригодиться во времена DOS атаки на ресурс.
Пример:
Ожидаемая JSON структура входных данных
Пример некорректных данных
Пример корректных данных
Всего лишь одной проверкой наличия required можно отсечь этот запрос без детальной проверки всех значений/полей.
2. Проверка на тип данных
Второй этап такой же простой как и предыдущий, и может дать не меньшую экономию ресурсов.
Входные данные состоят из значений/полей разных типов данных (речь про примитивные типы данных). Комплексные типы состоят из примитивных, следовательно, их точно так же можно проверить.
Что нужно сделать:
- проверить каждое значение/поле по его типу (string/int/uint/float/boolean);
- если хотя бы одно значение/поле имеет неожидаемый тип данных, то необходимо прекратить дальнейшую проверку и вернуть ответ с ошибкой.
Порядок проверки по типу данных следует выполнять в следующем порядке:
- обязательные значения/поля;
- наиболее часто встречаемые значения/поля при их наличии;
- все остальные при их наличии.
Пример:
Ожидаемая JSON структура аналогична как у предыдущего примера.
Пример некорректных данных
Этот этап на столько же эффективен как и предыдущий, но чутка более ресурсоёмкий.
3. Проверка на формат
Данный этап при надлежащем применении может отсечь большую часть кодовых инъекций.
Под эту проверку попадают все значения/поля, кроме типа boolean. Они уже досконально проверены на предыдущих этапах.
В чём суть:
Мы заранее знаем, что мы получим от потребителя приложения. Толи это будут какие-то числа, толи текст, или массив каких-то типов.
- если получаем int/uint/float значение и оно должно быть в рамках каких-то диапазонов или ограничений, нужно убедиться, что оно действительно так.
- если получаем string и это не свободный текст, то нужно его проверить по формату.
Способы проверки значений типа string:
- разделение на смысловые блоки и простыми логическими операциями проверяем значение (менее ресурсоёмко, но дольше в реализации);
- используем регулярные выражения (более ресурсоёмко, но быстрее в реализации в умелых руках).
Порядок проверки по типу данных следует выполнять как и на предыдущем этапе в следующей последовательности:
- обязательные значения/поля;
- наиболее часто встречаемые значения/поля при их наличии;
- все остальные при их наличии.
Пример:
Ожидаемая JSON структура входных данных
Пример некорректных данных
Как проверяем:
- так как поле some_field содержит свободный текст, его содержимое следует экранировать самостоятельно средствами языка для использования внутри приложения, либо средства драйверов СУБД для хранения и обработки в базе данных;
- поле kidneys должно быть в диапазоне [0, 2] (вспоминаем школьную математику и биологию, которая нафиг были не нужны ); следовательно, так как переданное значение не попадает в заданный диапазон, следует прекратить обработку и вернуть ошибку;
- поле email можно проверить двумя способами: простыми операциями и проверками или при помощи регулярных выражений. Ниже опишу оба способа проверки. По аналогии, если поле не проходит проверку, нужно вернуть обработку и вернуть ошибку.
Способы проверки поля email простыми операциями:
- убедиться, что значение содержит символ @ ;
- убедиться, что символ @ не первый и не последний;
- проверить длину email`а, она не должна превышать 320 символов ( <длинна имени 64>@ <длинна хоста 255>).
Для минимальной проверки email`а этих действия чаще всего достаточно.
Способы проверки поля email регулярным выражением:
Используя регулярное выражение ниже нужно выполнить в проверку значения
4. Логическая проверка
Этот этап, судя из регулярный новостей о поломках различных государственных/крупных коммерческих сайтов чужд разработчикам их создавшие.
Эта проверка наиболее ресурсоёмкая и вот почему: если в запросе фигурирует поле с идентификатором чего либо, нужно убедиться, что запись с данным идентификатором уже есть в информационной системе, за исключением тех запросов, которые должны эту запись создать.
- допустим, все данные хранятся в единой базе данных;
- для проверки идентификатора понадобится сделать запрос к базе данных, что требует ресурсов приложения на удержание запроса, на подключение к базе данных и на обработку запроса самой базой данных, и самое главное, может увеличить время исполнения в разы;
Как можно упростить решение и сделать его менее ресурсоёмким:
- использовать In-Memory хранение нужных идентификаторов в приложении;
- использовать быстрые кеш-машины для хранения нужных данных;
- использовать поисковые движки, которые будут в реальном времени индексировать нужные данные; использую поисковые индексы можно быстро проверять нужные индексы;
- организовывать запросы так, чтобы ресурсоёмкость запросов свести к минимуму.
Пример:
Ожидаемая JSON структура входных данных
Пример некорректных данных:
Пример потенциально корректных данных:
Но нам нужно убедиться, что пользователь с ID 1 существует и не заблокирован.
Как проверяем (простой пример с СУБД):
Допустим у нас есть некая таблица в базе данных со следующей структурой:
- нужно подключиться к базе данных;
- проверить, есть ли такой пользователь;
- у этого пользователя флаг is_available равен ли true ;
- у этого пользователя флаг is_removed равен ли true ;
- всё это несложно организовать одним запросом.
Эта простая проверка позволяет не допустить получение доступа к чужой пользовательской информации или выйти за пределы задуманной функциональности приложения.
Выводы
Из своего опыта могу сказать, что подобным списком проверок пользуются небольшое количество знакомых разработчиков, а знаю я достаточно много. Чаще всего разработчики полагаются на некий мифические фреймворки, которые за них проверят и обезопасят приложение. Могу сказать лишь одно: если бы в жизни ИТ всё было так просто, то какого чёрта специалисты имеют заработки выше рынка?
Проверка ввода данных
Рассмотрим пример проверки правильности ввода данных через HTML, с подсветкой CSS, а затем дополнительно сделаем валидацию <input> с помощью JavaScript.
Валидация данных необходима. Пользователь может случайно или специально ввести не корректные символы, что в дальнейшем приведёт к фатальной ошибке или к не желательным результатам.
Пример: для программ типа калькулятора нужно вводить числа, чтобы производить с ними различные вычисления и нужна проверка (валидация) на то, что пользователь ввёл числа, а не буквы или символы.
Для ввода данных служит элемент input, у него и будем проверять валидацию.
Валидация input через HTML происходит с помощью атрибута pattern.
Примеры атрибута «pattern»
<label>
<p>Введите число</p>
<input type=»text» title=»Используйте числовой формат» pattern=»^[0-9]+$»>
</label>
<label>
<p>Введите число от 1 до 9</p>
<input type=»text» title=»Введите число от 1 до 9″ pattern=»[1-9]»>
</label>
<label>
<p>Введите не более 5 цифр</p>
<input type=»text» title=»Разрешено не более 5 цифр» pattern=»[0-9]<1,5>«>
</label>
<label>
<p>Введите от 7 до 9 цифр</p>
<input type=»text» title=»Разрешено от 7 до 9 цифр» pattern=»[0-9]<7,9>«>
</label>
<label>
<p>Только русские слова</p>
<input
type=»text»
title=»Разрешено использовать только пробелы и русские буквы»
pattern=»^[А-Яа-яЁё\s]+$»
/>
</label>
<label>
<p>Только латинские слова</p>
<input
type=»text»
title=»Разрешено использовать только пробелы и латинские буквы»
pattern=»^[a-zA-Z\s]+$»>
</label>
<label>
<p>Только русские или латинские слова с пробелами, не менее 6 символов</p>
<input
type=»text»
title=»Разрешено использовать только пробелы и русские или латинские буквы, не менее 6″
pattern=»^[A-Za-zА-Яа-яЁё\s]<6,>«>
</label>
<label>
<p>Введите телефон в формате: +7 (777) 777-77-77 (Россия)</p>
<input
type=»tel»
title=»Используйте формат: +7 (777) 777-77-77″
pattern=»[+]7\s[\(]\d<3>[\)]\s\d<3>[\-]\d<2>[\-]\d<2>«>
</label>
Атрибут required делает заполнение поля обязательным.
Атрибуты minlength, maxlength задаёт минимальное и (или) максимальное допустимое количество символов. Например minlength=»6″.
Пример валидации на HTML
Использование CSS при валидации
Введите число ОТ 10 ДО 999
Введите что нибудь и щёлкните вне элемента INPUT.
Элемент подсвечивается красным если он пустой или ввод не валидный, в нашем случае в атрибуте pattern задан ввод только чисел, любой символ или буква будет подсвечивать красным, сообщая об ошибке.
Пустое поле подсвечивает красным цветом атрибут required который сигнализирует что поле обязательно для заполнения.
Атрибуты minlength=»2″ maxlength=»3″ следят за тем, чтобы было введено от двух до трёх символов. Один символ, подсветка останется красной. Больше трёх символов ввести не удаться.
При правильном вводе, красная подсветка исчезнет, а введённые числа станут зелёными.
Пример валидации на JavaScript
Проверка заполнения формы
JavaScript
function validateForm() <
var x = document.forms[«myForm»][«fname»].value;
if (x == «») <
alert(«Необходимо ввести имя»);
return false;
>
>
<form name=»myForm» action=»//cwmax.ru/blog/validation.php» onsubmit=»return validateForm()» method=»post»>
Имя: <input type=»text» name=»fname»>
<input type=»submit» value=»Submit»>
</form>
Пример
Проверка ввода чисел
JavaScript
function myFunction() <
var x;
var text = «Все в порядке»;
if (isNaN(x) || x 10) <
text = «Ввод не верен»;
>
document.getElementById(«demo»).innerHTML = text;
>
<p>Введите число от 1 до 10</p>
<input type=»text» required>
<button type=»button» onclick=»myFunction()»>Ввод</button>
<p ></p>
Проверка данных — Java & Spring Validation
Проверка данных класса (bean) в java тема не новая, но актуальная и здесь я объединю различные аспекты: валидацию данных в рамках JSR-303, покажу как это сделать чисто в Java и с использованием Spring, как делать в стандартном приложении и в Web.
Содержание: Валидация данных (JSR-303) в
- стандартном Java приложении
- c использованием Spring
- объединение Java + Spring
- Spring MVC
Validation в стандартном Java приложении
а также можно делать и собственные. И так есть класс (bean)
Здесь в примере Size и @Digits готовые аннотации, а @PersonAgeConstraint собственная. Как сделать собственную:
В message() указываем ключ (value.negative) из файла ресурса (ValidationMessages.properties) для сообщения
и реализацию класса проверки — PersonAgeConstraintValidator.class
Моя собственная аннотация готова, добавляем ее к полю и уже можно проверить, все поля на которых есть аннотации будут проверены соответствующими правилами.
Результат в консоли
Не более 3-х знаков
Отрицательное значение
Сообщения для стандартных аннотаций можно указать в файле сообщений, по правилу:
Поэтапная проверка. Для Class<?>[] groups() можно указывать типы классов по которым можно потом группировать, ограничивать список проверок, т.е. использовать как фильтр. Таким образом проверку можно сделать по этапам, 1) Например разделим проверку лица по состоянию здоровья, 2) а уже затем профессиональные данные. Подготовим две аннотации
HealthConstraint и ProfessionalConstraint и реализации для них. Первым проверим соответствие здоровью а затем если проходит по здоровью, проверим на профессиональные данные.
Пример аннотации HealthConstraint
Пример реализации HealthConstraintValidator
для ProfessionalConstraint все аналогично
Далее проверять так:
Подобные проверки, например нужны когда мы загружаем данные из файла, web service и др. источников.
Validation c использованием Spring
и именно его имплементация выполняет проверку данных. Это уже не декларативный подход, но в нем есть своя гибкость и расширяемость. Для того же бина, сделаю туже проверку возраста.
Переопределив два метода, делаем валидацию
value.negative — так же является ключом в файле сообщений, public boolean supports определяет тип поддерживаемого класса.
Проверка запускается через DataBinder
Будут выполнены все проверки которые имплементировали org.springframework.validation.Validator для класса Person.
Можно добавить так же несколько валидаторов, dataBinder.addValidators, можно сделать композицию правил (вызов из одного правила, другого), пример:
Я почему то ожидал, Spring будет выполнять также проверки указанные в аннотациях, но нет, этот вызов надо делать самостоятельно.
Java & Spring
Очевидно я захочу использовать два подхода в проверки данных — Java и Spring, объединить их можно, а именно добавить в Spring validator вызов javax.validation.Validator.
С помощью spring делаем injection javax.validation.Validator
@Autowired
private Validator validator;
далее на методе public void validate(Object obj, Errors errors)
выполняем декларативные проверки java, а затем выполняем все проверки для класса Person на spring org.springframework.validation.Validator.
Запускаем проверку также через spring
Теперь в коллекции будут проверки от аннотаций java и spring (org.springframework.validation.Validator) для Person
Вывод в консоли
Отрицательное значение (аннотация)
Не более 3-х знаков (аннотация)
Только положительные число (spring)
Spring MVC
Конечно теперь это все можно применить в web приложении.
Добавляем в проект Controller, jsp страницу (тут кстати могут и другие варианты, например генерация страниц с помощью freeMarker, и др.), css стиль, pom зависимость. И так по порядку
1) MVC Controller
Здесь с помощью spring injection подключен PersonValidator
@Autowired
@Qualifier(«personValidator») // spring validator
private Validator personValidator;
устанавливаем PersonValidator в initBinder
@InitBinder
protected void initBinder(WebDataBinder binder) <
binder.setValidator(personValidator);
>
Проверка инициируется с помощью аннотации @Valid
В этом случае выполнится только spring проверка, декларативные проверки будут проигнорированы.
Если убрать из кода
@InitBinder
protected void initBinder(WebDataBinder binder)
то наоборот выполнятся все декларативные проверки, а spring будут проигнорированы.
Что бы выполнить все проверки и декларативные и spring, можно поступить так:
Убрать @InitBinder, оставить injection
@Autowired
@Qualifier(«personValidator») // spring validator
private Validator personValidator;
и добавить вызов spring проверки вручную
// spring validate
personValidator.validate(person, bindingResult);
т.е. в bindingResult будут добавлены еще проверки от spring :-), что и хотелось!
Привязка данных в jsp и модели, осуществляется атрибутом — modelAttribute=»person» В примере подключена SpringMVC’s Form Tag Library.
Проверка вводимых данных в ячейки Excel
Допустим, что в таблице прайс-листа с розничными ценами на товары, в одном из столбцов указана процентная ставка НДС. Как часто бывает в рутинной работе срабатывает человеческий фактор и по ошибке для одной из категорий товаров, вместо ставки НДС 20% была введена старая ставка 19%. Эта небольшая разница в данных – 1% может создать большие проблемы для фирмы с всевозможными последствиями. Чтобы исключить ошибки созданных по причине человеческого фактора, воспользуемся встроенным инструментом Excel для проверки данных, который позволяет контролировать все что вводиться на рабочий лист.
Как в Excel сделать проверку данных в ячейках
Пример прайс-листа с введенными ошибками в процентных ставках НДС:
Чтобы в Excel сделать проверку вводимых данных в ячейки следует выполнить ряд последовательных действий:
- Выделите диапазон ячеек где вводиться формула с процентной ставкой для цен с НДС и выберите инструмент: «ДАННЫЕ»-«Работа с данными»-«Проверка данных».
- В появившемся окне «Проверка вводимых значений» на вкладке «Параметры» из выпадающего списка «Тип данных:» выберите опцию «Список».
- В полю ввода «Источник:» введите значения разных процентных ставок: 0%; 20%; освобождается.
- Перейдите на закладку «Сообщение об ошибке» и заполните текстовое поле «Сообщение:» текстом который будет содержать сообщение при вводе других значений, которые отличаются от указанных значений в списке.
Закладка «Сообщение об ошибке» предоставляет пользователю возможность оформить стиль сообщения об ошибочных вводах значений. Если пользователь вводить в ячейку неправильное значение тогда будет выполнен один из 3-х параметров:
- Останов – данный параметр разрешает вводить только правильное значение, выбранного из выпадающего списка или отменить ввод оставив пустую ячейку.
- Предупреждение – этот параметр предупреждает об вводе ошибочного значения и предоставляет попытку отредактировать или выбрать из выпадающего списка.
- Сообщение – параметр сообщает о неправильном значении и позволяет проигнорировать ошибку.
Закладка «Сообщение для ввода» содержит поля ввода для заголовка и текстовое поле для сообщения в примечании, которое будет появляться перед вводом данных в ячейку. А точнее как только ячейка будет активной сразу высветлиться соответствующее примечание. Данное примечание повышает контроль над ошибками связанных с вводом значений. В нем можно указать правильное значение для данных ячеек.
После заполнения всех параметров в окне «Проверка вводимых значений» нажмите на кнопку ОК.
В результате возле каждой заполняемой ячейки справа появляется стрелка выпадающего списка, в которой указана правильная процентная ставка НДС.
Внимание! Инструмент «Проверка данных» срабатывает только при вводе значений в ячейку. Если же значения буду скопированы из других ячеек, то при вставке этот инструмент удалиться из этих ячеек и не сработает.