[Linq2db] InsertWithIdentity и Sql Injection
От: elcolex  
Дата: 04.01.16 09:51
Оценка: :)
Всем привет.
Давно хотел попробовать эту замечательную библиотеку в реальном проекте. И тут как раз подвернулась такая возможность. Использую БД Оракл. Посмотрел примеры. Все очень просто и круто.

Сделал класс доступа к данным, добавил туда метод для вставки записи через linq2db. На таблицах используется триггер insert before, который из сиквенса добавляет ид и возвращает его. В самом коде используется метод InsertWithIdentity.
Разобрался с логированием sql, чтобы в файлик писался код, сгенерированный библиотекой.
Все, так как описано в блоге: http://blog.linq2db.com/2015/05/linq-crud-operations.html

using (var db = new DataConnection())
{
    object identity = db.GetTable<TestTable>()
        .InsertWithIdentity(() => new TestTable
        {
            Name      = "Crazy Frog",
            CreatedOn = Sql.CurrentTimestamp
        });
}

DECLARE @IDENTITY_PARAMETER Decimal
SET     @IDENTITY_PARAMETER = NULL
 
INSERT INTO TestTable
(
    Name,
    CreatedOn
)
VALUES
(
    'Crazy Frog',
    CURRENT_TIMESTAMP
)
RETURNING
    ID INTO :IDENTITY_PARAMETER


Написал, все работает. Потом присмотрелся... И думаю: а какого фига не используются bind-переменные?!.. Это был для меня прям удар в спину. Тут же ведь значения из полей модели прямо вставляются в sql. Вот это косяк! И что интересно, метод Insert генерит SQL с bind-переменными! Но он не очень удобный, потому что не возвращает ид записи. Пришлось его все-таки использовать (сначала sequence дергать, потом ид передавать).. Но я в шоке: почему бинд-переменные не используются??! Да, там есть эскейпинг значений, но гарантируется ли отсутствие SQL injection? Или я напрасно переживаю?.. К тому же, насколько я знаю, бинд-переменные используются ещё для кэширования SQL оракулом... Просто у меня волосы на голове шевельнулись, когда я увидел, что значения не заданы в переменных, а вставляются прямо в текст! Это же ведь такой лоховский косяк.
Что скажете? Или никто не использует InsertWithIdentity?

Вот здесь же все в порядке:
DECLARE @Name Varchar2 -- String
SET     @Name = 'Crazy Frog'
DECLARE @Description Varchar2 -- String
SET     @Description = NULL
DECLARE @CreatedOn TimeStamp -- DateTime
SET     @CreatedOn = NULL
 
INSERT INTO TestTable
(
    Name,
    Description,
    CreatedOn
)
VALUES
(
    :Name,
    :Description,
    :CreatedOn
)
я не разделяю ваших галлюцинаций...
Re: [Linq2db] InsertWithIdentity и Sql Injection
От: rameel https://github.com/rsdn/CodeJam
Дата: 04.01.16 13:07
Оценка:
Здравствуйте, elcolex, Вы писали:

E>Написал, все работает. Потом присмотрелся... И думаю: а какого фига не используются bind-переменные?!..


Потому что используешь константы. Используй переменные и будут тебе параметры.

Для информации, инлайнингом параметров при использовании переменных можно управлять через свойство InlineParameters у DataConnection и DataContext.
var name = "John";
// Какой именно запрос будет сгенерирован зависит от значения свойства InlineParameters
db.Persons.Insert(() => new Person {Name = name});


E> Это был для меня прям удар в спину. Тут же ведь значения из полей модели прямо вставляются в sql. Вот это косяк!


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

E>И что интересно, метод Insert генерит SQL с bind-переменными!


Вот эти методы, вызываемые напрямую у DataConnection и DataContext генерируют параметризованный sql.
db.Insert(new Person {Name = "John"});
db.InsertWithIdentity(new Person {Name = "John"});


Остальные от того, что ты используещь: константы или переменные (в последнем еще зависит от свойства InlineParameters).

E>Но он не очень удобный, потому что не возвращает ид записи.


Есть метод:
db.InsertWithIdentity(new Person {Name = "John"});
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[2]: [Linq2db] InsertWithIdentity и Sql Injection
От: elcolex  
Дата: 04.01.16 18:31
Оценка:
Здравствуйте, rameel, Вы писали:

R>Здравствуйте, elcolex, Вы писали:


E>>Написал, все работает. Потом присмотрелся... И думаю: а какого фига не используются bind-переменные?!..


R>Потому что используешь константы. Используй переменные и будут тебе параметры.


R>Для информации, инлайнингом параметров при использовании переменных можно управлять через свойство InlineParameters у DataConnection и DataContext.

R>
R>var name = "John";
R>// Какой именно запрос будет сгенерирован зависит от значения свойства InlineParameters
R>db.Persons.Insert(() => new Person {Name = name});
R>


E>> Это был для меня прям удар в спину. Тут же ведь значения из полей модели прямо вставляются в sql. Вот это косяк!


R>Тебе только кажется. Все безопасно, так как строковые параметры при необходимости эскейпятся.


E>>И что интересно, метод Insert генерит SQL с bind-переменными!


R>Вот эти методы, вызываемые напрямую у DataConnection и DataContext генерируют параметризованный sql.

R>
R>db.Insert(new Person {Name = "John"});
R>db.InsertWithIdentity(new Person {Name = "John"});
R>


R>Остальные от того, что ты используещь: константы или переменные (в последнем еще зависит от свойства InlineParameters).


E>>Но он не очень удобный, потому что не возвращает ид записи.


R>Есть метод:

R>
R>db.InsertWithIdentity(new Person {Name = "John"});
R>


Про свойство InlineParameters я знаю. Как раз в дебаггере смотрел, оно по-умолчанию false. И использую я переменные (значения приходят с формы ввода): в linq2db есть такой способ прикольный — db.Value().Value().Value().InsertWithIdentity(). Это чтобы не все поля таблицы инсертить.
Но я не совсем понял, а для чего такие сложности? Константы это или не константы. Всегда использовать бинд-переменные да и не волноваться — а правильно ли мы заэскейпили?
Вот, реальный пример: в поле ввода введено значение (без ковычек) "---юЩУЯ----"
И у меня при инсерте выдаёт оракловую ошибку "ORA-01008 not all variables bound". И я не понимаю что произошло. Т.е. это значение не вставилось в таблицу.

И потом не объяснишь разработчику, что вот этот InsertWithIdentity не используй, потому что он не совсем безопасный, а вот этот используй.
Эх, такая классная библиотека. Это ж надо...
я не разделяю ваших галлюцинаций...
Re[3]: [Linq2db] InsertWithIdentity и Sql Injection
От: Jack128  
Дата: 04.01.16 19:02
Оценка:
Здравствуйте, elcolex, Вы писали:

E>Вот, реальный пример: в поле ввода введено значение (без ковычек) "---юЩУЯ----"

E>И у меня при инсерте выдаёт оракловую ошибку "ORA-01008 not all variables bound". И я не понимаю что произошло. Т.е. это значение не вставилось в таблицу.

Хм, ты не знаешь что произошло, но уже обвиняешь Linq2db ??

E>И потом не объяснишь разработчику, что вот этот InsertWithIdentity не используй, потому что он не совсем безопасный, а вот этот используй.

E>Эх, такая классная библиотека. Это ж надо...
Re[3]: [Linq2db] InsertWithIdentity и Sql Injection
От: rameel https://github.com/rsdn/CodeJam
Дата: 04.01.16 19:58
Оценка:
Здравствуйте, elcolex, Вы писали:

E>И использую я переменные (значения приходят с формы ввода): в linq2db есть такой способ прикольный — db.Value().Value().Value().InsertWithIdentity(). Это чтобы не все поля таблицы инсертить.


Есть разница как использовать
db.Persons.Value(p => p.Name, () => name).Insert();
db.Persons.Value(p => p.Name, name).Insert();


E>Но я не совсем понял, а для чего такие сложности? Константы это или не константы. Всегда использовать бинд-переменные да и не волноваться — а правильно ли мы заэскейпили?


Это зависит от многих факторов, в том числе и от используемой БД и от его версии. Где-то константы могут приводить к перекомпиляции запроса и переполнении планов, но в то же время план запроса будет построен под конкретные значения, в отличие от.
Соответственно, библиотека не навязывает свой способ работы с используемой БД и не сужает выбор до "единственно правильного", а дает возможность использовать разные стратегии для разных случаев с максимальной эффективностью. И я считаю, это правильно.

E>Вот, реальный пример: в поле ввода введено значение (без ковычек) "---юЩУЯ----"

E>И у меня при инсерте выдаёт оракловую ошибку "ORA-01008 not all variables bound". И я не понимаю что произошло. Т.е. это значение не вставилось в таблицу.

Не понимаешь, но виновата библиотека?

E>И потом не объяснишь разработчику, что вот этот InsertWithIdentity не используй, потому что он не совсем безопасный, а вот этот используй.


Нет такого, Insert и InsertWithIdentity работают совершенно одинаково.

E>Эх, такая классная библиотека. Это ж надо...


Предлагаю разобраться, а не кидаться эээ беспочвенными обвинениями
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[3]: [Linq2db] InsertWithIdentity и Sql Injection
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.01.16 15:27
Оценка:
Здравствуйте, elcolex, Вы писали:

E>Вот, реальный пример: в поле ввода введено значение (без ковычек) "---юЩУЯ----"

E>И у меня при инсерте выдаёт оракловую ошибку "ORA-01008 not all variables bound". И я не понимаю что произошло. Т.е. это значение не вставилось в таблицу.

Так разберитесь что произошло. Посмотрите какой запрос сгенерировался, есть ли реально проблема в запросе по вине linq2db. И отпишите сюда, пожалуйста.
Re[4]: [Linq2db] InsertWithIdentity и Sql Injection
От: elcolex  
Дата: 05.01.16 22:20
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Здравствуйте, elcolex, Вы писали:


E>>Вот, реальный пример: в поле ввода введено значение (без ковычек) "---юЩУЯ----"

E>>И у меня при инсерте выдаёт оракловую ошибку "ORA-01008 not all variables bound". И я не понимаю что произошло. Т.е. это значение не вставилось в таблицу.

MC>Так разберитесь что произошло. Посмотрите какой запрос сгенерировался, есть ли реально проблема в запросе по вине linq2db. И отпишите сюда, пожалуйста.


У меня сейчас по рукой нет оракла, после праздников поподробнее разберусь. Но по памяти, как и писал. Запрос сгенерировался такой же как я писал в первом посте. Там вместо "Crazy Frog" поставить "---юЩУЯ----". Вот такой:
DECLARE @IDENTITY_PARAMETER Decimal
SET     @IDENTITY_PARAMETER = NULL
 
INSERT INTO TestTable
(
    Name,
    CreatedOn
)
VALUES
(
    '---юЩУЯ----',
    CURRENT_TIMESTAMP
)
RETURNING
    ID INTO :IDENTITY_PARAMETER

Такой текст я видел в логах. После него запись с ошибкой ORA-01008 и стектрейсом.
Потом взял переписал вместо InsertWithIdentity на просто Insert. В логах прописались бинд-переменные как надо. Ну и запись вставилась успешно.
я не разделяю ваших галлюцинаций...
Re[4]: [Linq2db] InsertWithIdentity и Sql Injection
От: elcolex  
Дата: 05.01.16 22:53
Оценка: 1 (1)
Здравствуйте, rameel, Вы писали:

R>Здравствуйте, elcolex, Вы писали:


E>>И использую я переменные (значения приходят с формы ввода): в linq2db есть такой способ прикольный — db.Value().Value().Value().InsertWithIdentity(). Это чтобы не все поля таблицы инсертить.


R>Есть разница как использовать

R>
R>db.Persons.Value(p => p.Name, () => name).Insert();
R>db.Persons.Value(p => p.Name, name).Insert();
R>


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

E>>Но я не совсем понял, а для чего такие сложности? Константы это или не константы. Всегда использовать бинд-переменные да и не волноваться — а правильно ли мы заэскейпили?


R>Это зависит от многих факторов, в том числе и от используемой БД и от его версии. Где-то константы могут приводить к перекомпиляции запроса и переполнении планов, но в то же время план запроса будет построен под конкретные значения, в отличие от.

R>Соответственно, библиотека не навязывает свой способ работы с используемой БД и не сужает выбор до "единственно правильного", а дает возможность использовать разные стратегии для разных случаев с максимальной эффективностью. И я считаю, это правильно.

E>>Вот, реальный пример: в поле ввода введено значение (без ковычек) "---юЩУЯ----"

E>>И у меня при инсерте выдаёт оракловую ошибку "ORA-01008 not all variables bound". И я не понимаю что произошло. Т.е. это значение не вставилось в таблицу.

R>Не понимаешь, но виновата библиотека?


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

E>>И потом не объяснишь разработчику, что вот этот InsertWithIdentity не используй, потому что он не совсем безопасный, а вот этот используй.


R>Нет такого, Insert и InsertWithIdentity работают совершенно одинаково.

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

E>>Эх, такая классная библиотека. Это ж надо...


R>Предлагаю разобраться, а не кидаться эээ беспочвенными обвинениями


Да я не обвиняю библиотеку. Был у меня сюрприз просто перед самым новым годом. Неожиданный, надо сказать.. Разработчикам библиотеки большой респект. Давно уже смотрю что делают (начиная с bltoolkit). Грамотные простые решения, без этих всяких новомодных приблуд.
я не разделяю ваших галлюцинаций...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.