ли все данные в базе данных доступны для всех пользователей, или вы только позволяют каждому пользователю получить доступ к подмножество данных? Если последнее, просто ограничение доступа к базе данных для доступа только для чтения недостаточно для защиты ваших данных.
В качестве тривиального примера пользователь может скомпрометировать ваш интерфейс, чтобы отправить запрос SELECT password, salt FROM users WHERE login = 'admin'
, захватить ответ, чтобы получить необработанные данные, и перебор пароля администратора. По мере того как популярность приложения растет, пул злонамеренных пользователей растет более чем линейно, до тех пор, пока их коллективный интеллект не станет больше, чем у вашей команды; вы не должны ставить себя в ситуации, когда успех будет вашим падением.
Вы можете взять SQL-запрос, отправленный клиентским приложением, и попытаться разобрать его на стороне сервера, чтобы применить соответствующие ограничения к запросу, чтобы, таким образом, запереть пользователя. Но для этого вам потребуется написать мини-синтаксический анализатор SQL в коде сервера, и кто хочет сделать все это? Гораздо проще написать код, который может писать SQL, чем писать код, который может его прочитать.
Моя команда решить аналогичную задачу для интерфейса отчетности в довольно сложной веб-приложения, и наш подход пошел что-то вроде этого:
Так как вы уже планируете использовать графический интерфейс для построения запроса, это будет довольно просто превратить необработанные данные из элементов интерфейса в структуру данных, которая представляет вход пользователя (и, в свою очередь, запрос). Например, пользователь может указать, используя ваш интерфейс, условие, что они хотят, чтобы результаты были ограничены теми, которые были собраны 5 мая 2010 года всеми, кроме Джона. (Предположим, что Джон UserID
: 3.) Использование вариант формата JSON используется моя команда, вы бы просто разорвать, что данные из пользовательского интерфейса во что-то вроде:
{ "ConditionType": "AND",
"Clauses": [
{ "Operator": "Equals",
"Operands": [
{ "Column": "CollectedDate" },
{ "Value": "2010-05-05" }
]
},
{ "Operator": "NotEquals",
"Operands": [
{ "Column": "CollectedByUserID" },
{ "Value": 3 }
]
}
]
}
На стороне клиента, создавая этот вид данных структура в значительной степени изоморфна задаче создания SQL-запроса и, возможно, несколько проще, так как вам не нужно беспокоиться о синтаксисе SQL.
Здесь есть тонкости, которые я затушевываю. Это только часть WHERE-части запроса и будет вынуждена жить в более крупном объекте ({ "Select": ..., "From": ..., "Where": ..., "OrderBy": ... }
). Возможны более сложные сценарии.Например, если вам требуется, чтобы пользователь мог указать несколько таблиц и как они объединяются вместе, вы должны быть более конкретными при указании столбца в качестве операнда в предложении WHERE. Но опять же, все это - работа, которую вам все равно придется делать, чтобы напрямую строить запрос.
Затем сервер будет десериализовать эту структуру. (Стоит отметить, что имена столбцов, предоставленные пользователем, не должны быть грязными - мы сопоставили их с списком разрешенных столбцов в нашем приложении, если столбец не был в списке, десериализация потерпела неудачу, и пользователь получил сообщение об ошибке.) С простой структурой объектов, с которой можно работать, внесение изменений в запрос почти тривиально. Приложение-сервер может изменить список предложений WHERE, чтобы применить соответствующие ограничения доступа к данным. Например, вы можете сказать (в псевдокоде) Query.WhereClauses.Add(new WhereClause(Operator: 'Equals', Operands: { 'User.UserID', LoggedInUser.UserID }))
.
Код сервера затем передает объект в относительно простой построитель запросов, который перемещает объект и разбивает строку запроса SQL. Это проще, чем кажется, но убедитесь, что все предоставленные пользователем параметры передаются в чистоте. Не дезинфицируйте - используйте параметризованные запросы.
Этот подход в конечном итоге сработало очень хорошо для нас, по нескольким причинам:
- Это позволило разбить сложность составления запроса при помощи графического интерфейса.
- Это гарантирует, что пользовательские запросы никогда не выполнялись грязными.
- Это позволило нам добавить произвольные предложения к запросам на различные виды ограничений доступа.
- Это было достаточно доступно для того, чтобы мы могли делать отличные вещи, например, позволяя пользователям искать в пользовательских полях.
На первый взгляд это может показаться сложным решением, но моя команда обнаружила, что преимуществ было много, а реализация была чистой и ремонтопригодной.
+2 за хороший ответ, -1 за поощрение использования синтаксиса JS в JSON, хотя :). – svens
@svens Спасибо! Однако я не заметил ошибку, которую вы указали; насколько я могу судить, мой пример JSON соответствует опубликованному стандарту. Не могли бы Вы уточнить? – WCWedin
@WCWedin Запустите его через jsonlint.com, имена участников должны быть указаны. Честно говоря, я не уверен, где это указано в стандарте, я просто знаю, что некоторые декодеры JSON собираются сдаться. – svens