Перегрузка функции типом возврата?

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

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

Мой подход:

  • Если вы ожидаете, что ввод будет целым, убедитесь, что он действительно целое число. В языке с переменным типом, таком как PHP, этот очень важен. Вы можете использовать, к примеру, это очень простое, но мощное решение: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
  • Если вы ожидаете чего-то еще от целого шестнадцатеричного значения. Если вы отбросите его, вы полностью избежите ввода. В C / C ++ есть функция, называемая mysql_hex_string() , в PHP вы можете использовать bin2hex() . Не беспокойтесь о том, что экранированная строка будет иметь размер в 2 раза по сравнению с исходной длиной, потому что даже если вы используете mysql_real_escape_string, PHP должен выделять одну и ту же емкость ((2*input_length)+1), что то же самое.
  • hex метод часто используется при передаче двоичных данных, но я не вижу причин, почему бы не использовать его во всех данных для предотвращения атак SQL-инъекций. Обратите внимание, что вам необходимо предварительно добавить данные с помощью 0x или использовать функцию MySQL UNHEX.

Так, например, запрос:

SELECT password FROM users WHERE name = 'root'

Будет:

SELECT password FROM users WHERE name = 0x726f6f74

или

SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex - идеальный выход.

Разница между функцией UNHEX и префиксом 0x

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

Префикс ** 0x ** может использоваться только для столбцов данных, таких как char, varchar, text, block, binary, и т. д. Кроме того, его использование немного сложно, если вы собираетесь вставить пустую строку. Вам придется полностью заменить его на '', или вы получите сообщение об ошибке.

UNHEX () работает в любом столбце; вам не нужно беспокоиться о пустой строке.


Hex-методы часто используются в качестве атак

Обратите внимание, что этот шестиугольный метод часто используется как атака SQL-инъекции, где целые числа точно так же, как и строки mysql_real_escape_string. Тогда вы можете избежать использования кавычек.

Например, если вы просто делаете что-то вроде этого:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

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

SELECT ... WHERE id = -1 union all select table_name from information_schema.tables

и теперь просто извлеките структуру таблицы:

SELECT ... WHERE id = -1 union all select column_name from information_schema.column где table_name = 0x61727469636c65

И тогда просто выберите нужные данные. Разве это не круто?

Но если кодер инъекционного сайта будет шестнадцатеричным, инъекция не будет возможна, потому что запрос будет выглядеть следующим образом: SELECT ... WHERE id = UNHEX('2d312075...3635')

248
задан Kevin Panko 16 June 2010 в 07:14
поделиться

6 ответов

Вопреки то, что другие говорят, перегружаясь типом возврата, возможно, и сделаны некоторыми современными языками. Обычное возражение состоит в том, что в коде как [1 152]

int func();
string func();
int main() { func(); }

Вы не можете сказать, которым func() называется. Это может быть разрешено несколькими способами:

  1. Имеют предсказуемый метод для определения, какая функция вызвана в такой ситуации.
  2. Каждый раз, когда такая ситуация происходит, это - ошибка времени компиляции. Однако имейте синтаксис, который позволяет программисту снимать неоднозначность, например, int main() { (string)func(); }.
  3. не имеют побочных эффектов. Если у Вас нет побочных эффектов, и Вы никогда не используете возвращаемое значение функции, то компилятор может не когда-либо вызывать функцию во-первых.

Два из языков I регулярно ( ab) используют перегрузку типом возврата: Perl и Haskell. Позвольте мне описать то, что они делают.

В [1 179] Perl, существует фундаментальное различие между [1 140] скаляр и список контекст (и другие, но мы притворимся, что существует два). Каждая встроенная функция в Perl может сделать разные вещи в зависимости от контекст , в котором это называют. Например, join оператор вызывает контекст списка (на вещи, к которой присоединяются), в то время как scalar оператор вызывает скалярный контекст, поэтому сравните:

print join " ", localtime(); # printed "58 11 2 14 0 109 3 13 0" for me right now
print scalar localtime(); # printed "Wed Jan 14 02:12:44 2009" for me right now.

Каждый оператор в Perl делает что-то в скалярном контексте и что-то в контексте списка, и они могут отличаться, как проиллюстрировано. (Это не только для случайных операторов как [1 112]. При использовании массива @a в контексте списка это возвращает массив, в то время как в скалярном контексте, это возвращает число элементов. Так, например print @a распечатывает элементы, в то время как print 0+@a печать размер.), Кроме того, каждый оператор может сила контекст, например, дополнение + контекст скаляра сил. Каждая запись в [1 117] документы это. Например, вот часть записи для [1 118]:

В контексте списка, возвраты (возможно пустой) список расширений имени файла на значении [1 119], таких как стандартная оболочка Unix /bin/csh сделал бы. В скалярном контексте шарик выполняет итерации посредством таких расширений имени файла, возвращаясь undef, когда список исчерпывается.

Теперь, каково отношение между списком и скалярным контекстом? Ну, man perlfunc говорит

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

, таким образом, это не простой вопрос наличия единственной функции, и затем Вы делаете простое преобразование в конце. На самом деле я выбрал localtime пример по этой причине.

Это не просто созданные-ins, которые имеют это поведение. Любой пользователь может определить такую функцию с помощью [1 123], который позволяет Вам различать список, скаляр и пустой контекст. Так, например, можно решить ничего не сделать, если Вас звонят в пустом контексте.

Теперь, можно жаловаться, что это не верно перегрузка возвращаемым значением, потому что у Вас только есть одна функция, которой говорят контекст, это призвано и затем действует на ту информацию. Однако это явно эквивалентно (и аналогично тому, как Perl не позволяет обычную перегрузку буквально, но функция может просто исследовать свои аргументы). Кроме того, это приятно разрешает неоднозначную ситуацию, упомянутую в начале этого ответа. Perl не жалуется, что не знает который метод звонить; это просто называет его. Все, что это должно сделать, выяснить, какой контекст функция была призвана, который всегда возможен:

sub func {
    if( not defined wantarray ) {
        print "void\n";
    } elsif( wantarray ) {
        print "list\n";
    } else {
        print "scalar\n";
    }
}

func(); # prints "void"
() = func(); # prints "list"
0+func(); # prints "scalar"

(Примечание: Я могу иногда говорить оператор Perl, когда я имею в виду функцию. Это не крайне важно для этого обсуждения.)

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

main = do n <- readLn
          print (sqrt n) -- note that this is aligned below the n, if you care to run this

Этот код читает число с плавающей точкой из стандартного входа и печатает его квадратный корень. Но что удивительно об этом? Ну, тип [1 124] readLn :: Read a => IO a. То, что это означает, - то, что для любого типа, который может быть Read (официально, каждый тип, который является экземпляром Read класс типа), readLn может считать его. Как Haskell знал, что я хотел считать число с плавающей точкой? Ну, тип [1 129] sqrt :: Floating a => a -> a, который по существу означает, что sqrt может только принять числа с плавающей точкой как исходные данные, и таким образом, Haskell вывел то, что я хотел.

, Что происходит, когда Haskell не может вывести то, что я хочу? Ну, там несколько возможностей. Если я не буду использовать возвращаемое значение вообще, Haskell просто не вызовет функцию во-первых. Однако, если я делаю , используют возвращаемое значение, тогда Haskell будет жаловаться, что оно не может вывести тип:

main = do n <- readLn
          print n
-- this program results in a compile-time error "Unresolved top-level overloading"

я могу разрешить неоднозначность путем определения типа, который я хочу:

main = do n <- readLn
          print (n::Int)
-- this compiles (and does what I want)

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

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

Ada: "Могло бы казаться, что самое простое правило разрешения перегрузки состоит в том, чтобы использовать все - всю информацию от максимально широкого контекста - для разрешения перегруженной ссылки. Это правило может быть простым, но это не полезно. Это требует, чтобы читатель просканировал произвольно большие части текста и сделал произвольно сложные выводы (такие как (g) выше). Мы полагаем, что лучшее правило является тем, которое делает явным задача, которую должны выполнить читатель или компилятор, и это делает эту задачу максимально естественной для читателя".

C++ (подраздел 7.4.1of Bjarne Stroustrup "Язык Программирования на C++"): "Возвратитесь, типы не рассматривают в разрешении перегрузки. Причина состоит в том, чтобы сохранить разрешение для отдельного оператора или вызова функции независимым от контекста. Рассмотрите:

float sqrt(float);
double sqrt(double);

void f(double da, float fla)
{
    float fl = sqrt(da);     // call sqrt(double)
    double d = sqrt(da); // call sqrt(double)
    fl = sqrt(fla);            // call sqrt(float)
    d = sqrt(fla);             // call sqrt(float)
}

, Если бы тип возврата был принят во внимание, больше не было бы возможно посмотреть на вызов [1 132] в изоляции и определить, какая функция была вызвана". (Обратите внимание для сравнения, что в Haskell нет никаких неявны преобразования.)

Java ( Спецификация языка 9.4.1 Java): "Один из унаследованных методов должен, должен быть тип возврата, substitutable для любого другого унаследованного метода; иначе ошибка времени компиляции происходит". (Да, я знаю, что это не дает объяснение. Я уверен, что объяснение дано Gosling на "Языке программирования Java". Возможно, у кого-то есть копия? Я держал пари, что это - "принцип наименьшего количества удивления" в сущности.) Однако забавный факт о Java: JVM позволяет перегрузка возвращаемым значением! Это используется, например, в [1 136] Scala, и может быть получено доступ непосредственно через Java также путем игры вокруг с внутренностями.

пз. Как заключительное примечание, на самом деле возможно перегрузиться возвращаемым значением в C++ с приемом. Свидетель:

struct func {
    operator string() { return "1";}
    operator int() { return 2; }
};

int main( ) {
    int x    = func(); // calls int version
    string y = func(); // calls string version
    double d = func(); // calls int version
    cout << func() << endl; // calls int version
    func(); // calls neither
}
517
ответ дан Community 23 November 2019 в 02:59
поделиться

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

-2
ответ дан Charles Graham 23 November 2019 в 02:59
поделиться

В haskell это возможно даже при том, что это не имеет перегрузки функции. Haskell использует классы типа. В программе Вы видели:

class Example a where
    example :: Integer -> a

instance Example Integer where  -- example is now implemented for Integer
    example :: Integer -> Integer
    example i = i * 10

сама Перегрузка функции не так популярна. Главным образом языки, которые я видел с ним, являются C++, возможно, Java и/или C#. На всех динамических языках это - стенография для:

define example:i
  ↑i type route:
    Integer = [↑i & 0xff]
    String = [↑i upper]


def example(i):
    if isinstance(i, int):
        return i & 0xff
    elif isinstance(i, str):
        return i.upper()

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

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

Вы видите, что существует бесконечно много других лучших более легких к реализации функций для реализации на язык, включая:

  • Динамический контроль типов
  • Внутренняя поддержка списков, словарей и строк unicode
  • Оптимизация (JIT, вывод типа, компилируя)
  • Интегрированные инструменты развертывания
  • поддержка Библиотеки
  • общественная поддержка и места сбора
  • библиотеки стандарта Rich
  • Хороший синтаксис
  • цикл печати оценки Read
  • Поддержка отражающего программирования
5
ответ дан Cheery 23 November 2019 в 02:59
поделиться

Украсть C++, конкретный ответ от другого очень похожего вопроса (простофиля?):

<час>

Функциональные типы возврата не играют роли в разрешении перегрузки просто, потому что Stroustrup (я принимаю с входом от других архитекторов C++), требуемое разрешение перегрузки, чтобы быть 'независимым контекстом'. См. 7.4.1 - "Перегрузка и Тип Возврата" с "Языка Программирования на C++, Третий Выпуск".

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

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

И, Господь знает, разрешение перегрузки C++ достаточно сложно как есть...

18
ответ дан Community 23 November 2019 в 02:59
поделиться

На таком языке, как был бы Вы разрешать следующее:

f(g(x))

, если f имел перегрузки void f(int) и void f(string) и g, имел перегрузки int g(int) и string g(int)? Вам был бы нужен некоторый disambiguator.

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

27
ответ дан Greg Hewgill 23 November 2019 в 02:59
поделиться

Если функции были перегружены типом возврата, и у Вас были эти две перегрузки

int func();
string func();

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

void main() 
{
    func();
}

поэтому разработчики языка часто запрещают перегрузке возвращаемого значения.

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

37
ответ дан Frederick The Fool 23 November 2019 в 02:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: