Как экранировать символ
Перейти к содержимому

Как экранировать символ

Экранирование (или что нужно знать для работы с текстом в тексте)

SQL инъекции, подделка межсайтовых запросов, поврежденный XML… Страшные, страшные вещи, от которых мы все бы хотели защититься, да вот только знать бы почему это все происходит. Эта статья объясняет фундаментальное понятие, стоящее за всем этим: строки и обработка строк внутри строк.

Основная проблема

Это всего лишь текст. Да, просто текст — вот она основная проблема. Практически все в компьютерной системе представлено текстом (который, в свою очередь, представлен байтами). Разве что одни тексты предназначены для компьютера, а другие — для людей. Но и те, и те, всё же остаются текстом. Чтобы понять, о чем я говорю, приведу небольшой пример:

Не поверите: это — текст. Некоторые люди называют его XML, но это — просто текст. Возможно, он не подойдет для показа учителю английского языка, но это — всё еще просто текст. Вы можете распечатать его на плакате и ходить с ним на митинги, вы можете написать его в письме своей маме… это — текст.

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

Откуда компьютер знает, как сделать это? Ну, потому что мы весьма кстати обернули определенные части текста специальными словами в забавных скобках, как, например, <author> и </author> . Поскольку мы сделали это, мы можем написать программу, которая искала бы эти определенные части, извлекала текст и использовала бы его для какого-нибудь нашего собственного изобретения.

Иными словами, мы использовали определенные правила в нашем тексте, чтобы обозначить некое особое значение, которое кто-то, соблюдая те же правила, мог бы использовать.
Ладно, это всё не так уж и трудно понять. А что если мы хотим использовать эти забавные скобки, имеющие какое-то особое значение, в нашем тексте, но без использования этого самого значения. Примерно так:

Символы «<» и «>» не являются ничем особенным. Они могут законно использоваться где угодно, в любом тексте, как в примере выше. Но как же наша идея о специальных словах, типа <author> ? Значит ли это, что » < n and y > » тоже является каким-то ключевым словом? В XML — возможно да. А возможно нет. Это неоднозначно. Поскольку компьютеры не очень справляются с неоднозначностями, то что-то в итоге может дать непредвиденный результат, если мы не расставим сами все точки над i и не устраним неоднозначности.
Решить эту дилемму можно, заменив неоднозначные символы чем-то однозначным.

Теперь, текст должен стать полностью однозначным. «&lt;» равносильно «<«, а «&gt;» — «>».
Техническое определение этого — экранирование, мы избегаем специальные символы, когда не хотим, чтобы они имели свое особое значение.

Если определенные символы или последовательности символов в тексте имеют особое значение, то должны быть правила, определяющие, как разрешить ситуации, когда эти символы должны использоваться без привлечения своего особого значения. Или, другими словами, экранирование отвечает на вопрос: «Если эти символы такие особенные, то как мне их использовать в своем тексте?».
Как можно было заметить в примере выше, амперсанд (&) — это тоже специальный символ. Но что делать, если мы хотим написать «&lt;», но без интерпретации этого как «<«? В XML, escape-последовательность для &, это — » &amp; «, т.е. мы должны написать: » &amp;lt; «

Другие примеры

XML — не единственный случай «страдания» от специальных символов. Любой исходный код, в любом языке программирования может это продемонстрировать:

Всё просто — обычный текст четко отделяется от «не текста» двойными кавычками. Таким же образом можно использовать и мой текст из курса математического анализа:

Клево! И даже не нужно прибегать к экранированию! Но, подождите, а что, если я хочу процитировать кого-нибудь?

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

«\» делает символ после него не специальным. Но это, опять-таки, значит, что «\» — специальный символ. Для однозначного написания этого символа в тексте, к нему нужно добавить такой же символ, написав: «\\». Забавно, не так ли?

Атака!

Не всё было бы так плохо, если бы просто должны были прибегать к экранированию. Напрягает конечно, но это не так ужасно. Проблемы начинаются, когда одни программы пишут текст для других программ, чтобы те могли его «читать». И нет, это не научная фантастика, это происходит постоянно. Например, на этом сайте, вы, публикуя сообщение, не набираете его в ручную в формате HTML, а пишите лишь только текст, который, в последствие, преобразуется этим сайтом в HTML, после чего, уже браузер, преобразует «сгенерированный» HTML снова в читабельный текст.

Другой распространенный пример и источник многих проблем безопасности — SQL запросы. SQL — язык, предназначенный для упрощения общения с базами данных:

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

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

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

В случае, если Вы просто просматриваете эту статью: Это — анти-пример! Это худшее, что Вы когда-либо могли сделать! Это кошмар безопасности! Каждый раз, когда Вы будете писать что-то подобное, будет погибать один невинный котенок! Ктулху сожрет Вашу душу за это!

А теперь давайте посмотрим, что здесь происходит. $_POST[‘name’] — значение, которое некий случайный пользователь ввел в некую случайную форму на вашем случайно веб-сайте. Ваша программа построит SQL-запрос, использующий это значение в качестве имени пользователя, которого Вы хотели бы найти в БД. Затем это SQL «предложение» отправляется прямиком в БД.

Вроде бы звучит все не так ужасно, да? Давайте попробуем ввести несколько случайных значений, которые можно ввести на вашем случайном веб-сайте и какие запросы из этого получатся:

Joe’; DROP TABLE users; —

Первый запрос выглядит не страшно, а вполне себе мило, правда? Номер 2, кажется, «несколько» повреждает наш синтаксис из-за неоднозначного ‘. Чертов немец! Номер 4 какой-то дурацкий. Кто бы такое написал? Это ведь не имеет смысла…
Но не для БД, обрабатывающей запрос… БД понятия не имеет от куда этот запрос поступил, и что он должен значить. Единственное, что она видит — это два запроса: найти номер пользователя по имени Joe, а затем удалить таблицу users (что сопровождается комментарием ‘), и это будет успешно сделано.

Для вас это не должно быть новостью. Если это так, то, пожалуйста, прочитайте эту статью еще раз, ибо Вы либо новичок в программировании, либо последние 10 лет жили в пещере. Этот пример иллюстрирует основы SQL-инъекций, применяемых во всем мире. для того, чтобы удалить данные, или получить данные, которые не должны быть просто так получены, или войти в систему, не имея на то прав и т.д. А все потому, что БД воспринимает англо-подобный «приговор» слишком буквально.

Впереееееед!

Следующий шаг: XSS атаки. Действуют они аналогично, только применяются к HTML.
Допустим, Вы решили проблемы с БД, получаете данные от пользователя, записываете в базу и выводите их назад на веб-сайт, для доступа пользователям. Это то, что делает типичный форум, система комментариев и т.д. Где-то на вашем сайте есть что-то подобное:

Если ваши пользователи будут хорошими и добрыми, то они будут размещать цитаты старых философов, а сообщения будут иметь примерно следующий вид:

Если пользователи будут умниками, то они, наверное, будут говорить о математике, и сообщения будут такие:

Хм… Опять эти осквернители наших скобок. Ну, с технической точки зрения они могут быть неоднозначными, но браузер простит нам это, правда?

Хорошо, СТОП, что за черт? Какой-то шутник ввел javascript теги на ваш форум? Любой, кто смотрит на это сообщение на вашем сайте, сейчас загружает и выполняет скрипты в контексте вашего сайта, которые могут сделать не весть что. А это не есть хорошо.

Не следует понимать буквально

В вышеупомянутых случаях, мы хотим каким-то образом сообщить нашей БД или браузеру, что это просто текст, ты с ним ничего не делай! Другими словами, мы хотим «удалить» особые значения всех специальных символов и ключевых слов из любой информации, предоставленной пользователем, ибо мы ему не доверяем. Что же делать?

Что? Что говоришь, мальчишка? Ах, ты говоришь, «экранирование»? И ты абсолютно прав, возьми печеньку!
Если мы применим экранирование к пользовательским данным до объединения их с запросом, то проблема решена. Для наших запросов к БД это будет что-то вроде:

Просто одна строка кода, но теперь больше никто не может «взломать» нашу базу данных. Давайте снова посмотрим как будут выглядеть SQL-запросы, в зависимости от ввода пользователя:
Alex

Joe’; DROP TABLE users; —

mysql_real_escape_string без разбора помещает косую черту перед всем, у чего может быть какое-то особое значение.

Далее, заменим наш скрипт на форуме:

Мы применяем функцию htmlspecialchars ко всем пользовательским данным, прежде, чем вывести их. Теперь сообщение вредителя выглядит так:

Обратите внимание, что значения, полученные от пользователи, на самом деле не «повреждены». Любой браузер парсит этот как HTML и выведет на экран все в правильной форме.

Что возвращает нас к.

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

Для полноты картины
  • Validation
    Вы можете проверить, соответствует ли пользовательский ввод некоторой заданной спецификации. Если Вы требуете ввода числа, а пользователь вводит нечто другое, программа должна сообщить ему об этом и отменить ввод. Если все это правильно организовать, то нет никакого риска схватить «DROP TABLE users» там, где, предполагалось, пользователь введет «42». Это не очень практично, для избегания HTML/SQL-инъекций, т.к. часто требуется принять текст свободного формата, который может содержать «подковырки». Обычно валидацию используют в дополнение к другим мерам.
  • Sanitization
    Вы можете так же «втихую» удалить любые символы, которые считаете опасными. Например, просто удалить что-либо похожее на HTML-тег, что избежать добавления на ваш форум. Проблема в том, что вы можете удалить вполне законные части текста.
    Prepared SQL statements
    Есть специальные функции, делающие то, чего мы и добивались: заставляют БД понять различия между самим SQL-запросом и информацией, предоставленной пользователями. В РНР они выглядят примерно так:

При этом отправка происходит в два этапа, четко разграничивая запрос и переменные. БД имеет возможность сначала понять структуру запроса, а потом заполнить его значениями.

Как экранировать спецсимволы, например " \?", в строке URL из Java

Есть строка, содержащая знак вопроса ? , или несколько. Строка передается как параметр в URL. Знак вопроса нужно заменить на %3f . Как это сделать?

Такой код дает ошибку:

Перевод вопроса с enSO: «Replace a question mark (?) with (\?)», не дословный, но очень похоже.

Разобрался сам. Поскольку знак вопроса «?» является спецсимволом в regexp, то экранировать его нужно не одним «\» слешем, а двумя «\\»

Ну а в общем случае для разных спецсимволов у меня получилось вот что:

user avatar

Если кто сюда попал в поисках аналогичного вопроса для JavaSvript, то ответ:

Site design / logo © 2022 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2022.6.10.42345

Нажимая «Принять все файлы cookie», вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Руководство по экранированию символов в регулярном выражении Java

Узнайте, как экранировать специальные символы в регулярных выражениях Java.

  • Автор записи

1. Обзор

API регулярных выражений в Java, java.util.regex широко используется для сопоставления шаблонов. Чтобы узнать больше, вы можете следить за этой статьей .

В этой статье мы сосредоточимся на экранировании символов в регулярном выражении и покажем, как это можно сделать в Java.

2. Специальные символы регулярных выражений

Согласно документации API регулярных выражений Java, в регулярном выражении присутствует набор специальных символов, также известных как метасимволы.

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

Метасимволы, которые нам обычно нужно избежать таким образом, являются:

Давайте рассмотрим простой пример кода, в котором мы сопоставляем входную строку с шаблоном, выраженным в регулярном выражении.

Этот тест показывает, что для заданной входной строки food при совпадении шаблона foo . ( foo , заканчивающегося символом точки), он возвращает значение true , которое указывает на то, что совпадение успешно.

Вы можете задаться вопросом, почему совпадение успешно, если во входной строке нет символа точки (.)?

Ответ прост. Точка (.) является метасимволом – особое значение точки здесь заключается в том, что на ее месте может быть “любой символ”. Таким образом, ясно, как совпадения определили, что совпадение найдено.

Допустим, мы не хотим рассматривать символ точки (.) с его уникальным значением. Вместо этого мы хотим, чтобы он интерпретировался как знак точки. Это означает, что в предыдущем примере мы не хотим позволять шаблону foo. чтобы иметь совпадение во входной строке .

Как бы мы справились с подобной ситуацией? Ответ таков: нам нужно избежать символа точки (.), чтобы его особое значение было проигнорировано.

Давайте рассмотрим это более подробно в следующем разделе.

3. Экранирование Символов

Согласно документации Java API для регулярных выражений, существует два способа, с помощью которых мы можем экранировать символы, имеющие особое значение. Другими словами, заставить относиться к ним как к обычным персонажам.

Давайте посмотрим, что это такое:

  1. Предшествуйте метасимволу с обратной косой чертой (\)
  2. Заключите метасимвол с \Q и \E

Это просто означает, что в примере, который мы видели ранее, если мы хотим избежать символа точки, нам нужно поставить символ обратной косой черты перед символом точки. В качестве альтернативы мы можем поместить символ точки между \Q и \E.

3.1. Экранирование С Помощью Обратной Косой Черты

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

Следовательно, в нашем примере нам нужно изменить регулярное выражение, как показано в этом тесте:

Здесь символ точки экранируется, поэтому совпадения просто рассматривают его как собаку и пытаются найти шаблон, который заканчивается точкой (т. Е. foo. ).

В этом случае он возвращает false , так как во входных данных String для этого шаблона нет совпадения.

3.2. Побег с помощью \Q & \E

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

Это просто означает, что все, что находится между \Q и \E , будет экранировано.

В тесте, показанном здесь, split() класса String выполняет сопоставление с использованием предоставленного ему регулярного выражения.

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

Символ канала-это метасимвол, который необходимо экранировать в регулярном выражении.

Здесь экранирование выполняется путем размещения символа канала между \Q и \E :

4. Метод Pattern.quote(String S)

Метод Pattern.Quote(String S) в классе java.util.regex.Pattern преобразует заданный шаблон регулярного выражения String в литеральный шаблон String. Это означает, что все метасимволы во входной строке | рассматриваются как обычные символы.

Использование этого метода было бы более удобной альтернативой, чем использование \Q & \E , поскольку он обертывает заданную строку с ними.

Давайте посмотрим на этот метод в действии:

В этом быстром тесте метод Pattern.quote() используется для экранирования заданного шаблона регулярного выражения и преобразования его в литерал String . Другими словами, он ускользает от всех метасимволов, присутствующих в шаблоне регулярных выражений для нас. Он выполняет аналогичную работу с \Q & \E .

Символ канала экранируется методом Pattern.quote() , а split() интерпретирует его как String литерал, на который он делит входные данные.

Как мы видим, это гораздо более чистый подход, а также разработчикам не нужно запоминать все escape-последовательности.

Следует отметить, что Следует отметить, что заключает весь блок в единственную escape-последовательность. Если бы мы хотели экранировать символы по отдельности, нам нужно было бы использовать алгоритм замены токенов .

5. Дополнительные Примеры

Давайте посмотрим, как replaceAll() метод java.util.regex.Матчер труды.

Если нам нужно заменить все вхождения данного символа String другим, мы можем использовать этот метод, передав ему регулярное выражение.

Представьте, что у нас есть входные данные с несколькими вхождениями символа $ . Результат, который мы хотим получить, – это та же строка с символом $ , замененным на £.

Этот тест демонстрирует, как шаблон $ передается без экранирования:

Тест утверждает, что $ неправильно заменен на £ .

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

Обратите внимание на \\$ здесь, который делает трюк, экранируя символ $ и успешно сопоставляя шаблон.

6. Заключение

В этой статье мы рассмотрели экранирование символов в регулярных выражениях в Java.

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

Как всегда, исходный код, связанный с этой статьей, можно найти на GitHub .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *