Я разобрал кое-какую архитектуру Java и материалы интервью (микросервисы, кластеры, распределенное, промежуточное ПО и т.д.), а друзья, кому нужно, могут обратить внимание на официальный аккаунт [Внутренние дела Программиста], и получить самостоятельно без всяких рутин
более предпочтительно
- Интервьюер немного смутился, сказав 9 распределенных методов генерации ID на одном дыхании.
- Что делать, если в интервью всегда спрашивают о подбазе данных и подтаблице? Вы можете ударить его так
- Резюме на 30 000 слов, суть оптимизации Mysql
- Чтобы не копировать и не вставлять, я был вынужден изучить краулер JAVA
- Технологический отдел неожиданно объявил, что все JAVA-разработчики должны знать фреймворк автоматизации тестирования интерфейсов.
- 5 структур данных Redis и соответствующие им сценарии использования, дополнительные баллы за пленарные интервью
введение
MyBatis
Является структурой уровня сохраняемости, междуJDBC
а такжеHibernate
между. Благодаря MyBatis снижается сложность рукописных операторов SQL, и пользователи могут гибко использовать операторы SQL и поддерживать расширенное сопоставление. Но запуск MyBatis связан не только с вопросами безопасности, многие разработчики думают, что SQL-инъекций при использовании MyBatis не будет, так ли это на самом деле?
Не будет ли SQL-инъекций при использовании MyBatis?Ответ, очевидно, НЕТ. MyBatis — это всего лишь фреймворк уровня сохраняемости, он не решает за вас проблемы безопасности. Конечно, если вы сможете следовать спецификациям и разрабатывать в соответствии с методом, рекомендованным фреймворком, вы, естественно, избежите проблемы SQL-инъекций. В этой статье MyBatis и SQL будут внедрены в эти обиды и недовольства. (Обратите внимание, что MyBatis, упомянутый в этой статье, по умолчанию относится к Mybatis3)
техническое образование
Источник написания этой статьи в основном связан с SQL-инъекцией, найденной в интрасети. Мы нашли запрос из интранетаkeyword
В параметрах есть SQL-инъекция, и кратко представляем предысторию требований.
По сути, этот интерфейс предназначен для реализации нечеткого запроса ключевых слов с несколькими полями, что должно быть относительно распространенным требованием. Просто здесь есть несколько условий запроса. После недолгих поисков мы обнаружили, что суть проблемы кроется в следующем коде:
public Criteria addKeywordTo(String keyword) {
StringBuilder sb = new StringBuilder();
sb.append("(display_name like '%" + keyword + "%' or ");
sb.append("org like '" + keyword + "%' or ");
sb.append("status like '%" + keyword + "%' or ");
sb.append("id like '" + keyword + "%') ");
addCriterion(sb.toString());
return (Criteria) this;
}
Ясно, что потребность должна быть удовлетворенаdiaplay_name
,org
,status
так же какid
, но разработчик создал его здесь самaddKeywordTo
метод, с помощью которого создается нечеткое условие запроса, включающее несколько полей.
Существует интересное явление: большинство точек внедрения SQL-инъекций, обнаруженных в интранете, в основном模糊查询
Место. Возможно, многие разработчики часто думают, что с нечеткими запросами нет проблем с SQL-инъекциями.
Проанализируйте, почему эта разработка написана таким образом, когда он не понимает, что в этом способе написания есть проблема с SQL-инъекцией, он может подумать, что этот способ написания самый беспроблемный, и тогда он может напрямую написать условия запроса. Приведенный выше код является ядром проблемы, давайте посмотрим на соответствующий файл xml:
<sql id="Example_Where_Clause" >
<where >
<foreach collection="oredCriteria" item="criteria" separator="or" >
<if test="criteria.valid" >
<trim prefix="(" suffix=")" prefixOverrides="and" >
<foreach collection="criteria.criteria" item="criterion" >
<choose >
<when test="criterion.noValue" >
and ${criterion.condition}
</when>
<when test="criterion.singleValue" >
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue" >
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue" >
and ${criterion.condition}
<foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<select id="selectByExample" resultMap="BaseResultMap" parameterType="com.doctor.mybatisdemo.domain.userExample" >
select
<if test="distinct" >
distinct
</if>
<include refid="Base_Column_List" />
from user
<if test="_parameter != null" >
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null" >
order by ${orderByClause}
</if>
</select>
Вернемся и посмотрим на вершинуJAVA
в кодеaddCriterion
метод, этот методMyBatis generator
Сгенерировано.
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
здесьaddCriterion
Метод передает только строковый параметр, по факту здесь используется перегрузка, есть и другиеaddCriterion
Количество параметров, передаваемых в метод, отличается. Используемый здесь метод передает только один параметр, который понимается какcondition
, поэтому просто добавьтеcondition
изCriterion
. Теперь посмотрите на xml вExample_Where_Clause
, при обходеcriteria
Когда критерий имеет только условие и не имеет значения, будет введено только условие.criterion.noValue
, поэтому формирование всей SQL-инъекции очень ясно.
<when test="criterion.noValue" >
and ${criterion.condition}
</when>
Правильное написание
Поскольку приведенное выше неверно, каким должно быть правильное написание?
Во-первых, мы можем использовать очень простой и прямой метод, т.addKeywordTo
метод внутриkeword
Фильтрация, которая фактически позволяет избежать SQL-инъекций. путем регулярного сопоставленияkeyword
Все небуквенные или числовые символы в нем заменяются пустыми строками, поэтому, естественно, отсутствует возможность SQL-инъекций.
keyword = keyword.replaceAll("[^a-zA-Z0-9\s+]", "");
Но этот способ письма не является научным способом письма.У этого способа письма есть недостаток, то есть если выkeyword
Что мне делать, если мне нужно включить символы, то нужно ли рассматривать больше ситуаций, нужно ли добавлять больше логических суждений или есть возможность обхода? Итак, каким должно быть правильное написание? фактическиmybatis 官网
уже даноComple Queries
Пример:
TestTableExample example = new TestTableExample();
example.or()
.andField1EqualTo(5)
.andField2IsNull();
example.or()
.andField3NotEqualTo(9)
.andField4IsNotNull();
List<Integer> field5Values = new ArrayList<Integer>();
field5Values.add(8);
field5Values.add(11);
field5Values.add(14);
field5Values.add(22);
example.or()
.andField5In(field5Values);
example.or()
.andField6Between(3, 7);
Эквивалентный оператор SQL выше:
where (field1 = 5 and field2 is null)
or (field3 <> 9 and field4 is not null)
or (field5 in (8, 11, 14, 22))
or (field6 between 3 and 7)
Теперь давайте начнем сaddKeywordTo
метод преобразования:
public void addKeywordTo(String keyword, UserExample userExample) {
userExample.or().andDisplayNameLike("%" + keyword + "%");
userExample.or().andOrgLike(keyword + "%");
userExample.or().andStatusLike("%" + keyword + "%");
userExample.or().andIdLike(keyword + "%");
}
Этот способ письма является относительно стандартным способом письма.or()
метод создаст новыйCriteria
объект, добавленный вoredCriteria
, и вернуть этоCriteria
объект, который можно связать, добавивCriterion
. добавлено вот такCriteria
состоит в том, чтобы содержатьcondition
так же какvalue
Да, при выполнении условного запроса вы будете вводитьcriterion.singleValue
, то параметр ключевого слова будет передан толькоvalue
в, покаvalue
через#{}
входящий.
<when test="criterion.singleValue" >
and ${criterion.condition} #{criterion.value}
</when>
Подводя итог, причина этой SQL инъекции в том, что разработка была написана не в соответствии со спецификацией, и я сделал колесо и написал метод для выполнения нечеткого запроса, но я не знал, что это приносит уязвимость SQL инъекции. фактически,Mybatis generator
Для каждого поля были сгенерированы расширенные методы, при условии их разумного использования проблем с внедрением SQL можно избежать.
Может ли использование #{} избежать внедрения SQL?
Если вы вдруг увидите этот вопрос, вы можете почувствовать себя колеблющимся? использовать#{}
Можно ли полностью исключить внедрение SQL? Но если вы внимательно его проанализируете, то обнаружите, что ответ положительный. Расскажу о конкретных причинах.
Прежде всего, нам нужно выяснить, что находится в MyBatis.#{}
как заявлено. когда параметр передается#{}
объявлены, параметры будут переданы черезPreparedStatement
Выполнить, то есть выполнить предварительно скомпилированным образом. Вы должны быть знакомы с предварительной компиляцией, потому что вJDBC
Уже есть предварительно скомпилированные интерфейсы в .
Это также соответствует тому, о чем мы упоминали в начале статьи: Mybatis — это не ядро, которое может решить SQL-инъекцию, а прекомпиляция. Прекомпиляция может не только избежать операторов SQL, чтобы избежать внедрения SQL, но и повысить эффективность выполнения. Нижний уровень Mybatis реализован через JDBC. Взяв в качестве примера MyBatis 3.3.1, SqlRunner в jdbc предназначен для реализации определенных операторов SQL.
Возьмем для примера способ обновления, видно что он через JAVAPreparedStatement
Для достижения предварительной компиляции операторов sql.
public int update(String sql, Object... args) throws SQLException {
PreparedStatement ps = this.connection.prepareStatement(sql);
int var4;
try {
this.setParameters(ps, args);
var4 = ps.executeUpdate();
} finally {
try {
ps.close();
} catch (SQLException var11) {
;
}
}
return var4;
}
Стоит отметить, что здесьPreparedStatement
Строго не эквивалентно предварительной компиляции. На самом деле предварительная компиляция делится на предварительную компиляцию на стороне клиента и прекомпиляцию на стороне сервера.MySql на стороне сервера после 4.1 уже поддерживает функцию предварительной компиляции.
многие основные持久层框架
(MyBatis
,Hibernate
) На самом деле никакой реальной пользы от прекомпиляции нет.Предварительная компиляция требует, чтобы мы настроили ее в списке параметров.Если мы не включим ее вручную, прекомпиляция по умолчанию драйвера JDBC версии 5.0.5 и выше выключен.
Его необходимо включить через параметры конфигурации:
jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true&cachePrepStmts=true
Выполнение SQL базы данных состоит из нескольких этапов, как показано на рисунке ниже, но наша предварительная компиляция операторов SQL на стороне клиента выполняется перед их отправкой на сервер. Основным соображением на стороне сервера является производительность, которая не рассматривается в этой статье.
Конечно, могут быть некоторые различия в том, как каждая реализация базы данных прекомпилируется. Но для предотвращения SQL-инъекций в MyBatis просто используйте#{}
Это нормально, потому что это реализует параметризацию операторов SQL и позволяет избежать прямого введения вредоносных операторов SQL и их выполнения.
Использование генератора MyBatis
Для использованияMyBatis
,MyBatis generator
Определенно важный инструмент для использования. MyBatis — это инструмент генерации кода для MyBatis и iBATIS, поддерживающий все версии MyBatis и iBATIS 2.2.0 и выше.
Потому что в реальной бизнес-разработке обязательно будет задействовано много таблиц, и написать соответствующие документы разработчикам самостоятельно невозможно. С помощью генератора MyBatis вы можете сгенерировать соответствующийPOJO 文件
,SQL Map XML
файл и дополнительный клиентский код JAVA.
Обычный способ использования генератора MyBatis — напрямую использовать Maven.mybatis-generator-maven-plugin
Плагины, пока файлы конфигурации и информация, связанная с базой данных, подготовлены, соответствующий код может быть сгенерирован с помощью этого плагина.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="MysqlTables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="false" />
<property name="suppressDate" value="false" />
</commentGenerator>
<!-- 数据库链接URL、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybaits_test"
userId="xxx"
password="xxx">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="true" />
</javaTypeResolver>
<javaModelGenerator targetPackage="com.doctor.mybatisdemo.domain" targetProject="src/main/java/">
<property name="constructorBased" value="false" />
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="myBatisGeneratorDemoConfig" targetProject="src/main/resources">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.doctor.mybatisdemo.dao" targetProject="src/main/java/">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 要生成那些表(更改tableName和domainObjectName就可以) -->
<table tableName="user" domainObjectName="user"/>
</context>
</generatorConfiguration>
Здесь я хочу подчеркнуть настройку ключевого параметра, а именноtargetRuntime
параметр. Для этого параметра есть два элемента конфигурации, а именноMyBatis3
а такжеMyBatis3Simple
, MyBatis3 является элементом конфигурации по умолчанию.MyBatis3Simple
будет генерировать только базовый CRUD, аMyBatis3
Будут сгенерированы условные добавления, удаления и изменения, и все условия инкапсулированы в XXXexample.
При использовании MyBatis3,enableSelectByExample
,enableDeleteByExample
,enableCountByExample
так же какenableUpdateByExample
Когда эти свойства имеют значение true, генерируется соответствующий динамический оператор. Вот почему генерируется наш Example_Where_Clause выше.
Если используется элемент конфигурации MyBatis3Simple, сгенерированный XML-файл карты SQL будет очень простым, включая только некоторые основные методы, и не будет генерировать вышеуказанные динамические методы. Можно сказать, что при использовании MyBatis3Simple дополнительной модификации нет, т.к. все переменные в нем передаются через#{}
Введено, невозможно иметь проблему инъекции SQL.
Однако в реальном бизнесе часто возникают сложные условия запросов, а исходные файлы конфигурации обычно используются для разработки, поэтому использование MyBatis3 или MyBatis3Simple по-прежнему требует определенных проблем и особых соображений. Однако, если вы используете конфигурацию по умолчанию, вам нужно быть осторожным: помните, что параметры, переданные извне, скорее всего, будут небезопасными и не могут быть напрямую введены для обработки. Имея это в виду, вы можете в основном избежать проблемы SQL-инъекций.
Суммировать
В этой статье рассматривается использование MyBatis, вызванное уязвимостью SQL-инъекции в интрасети.#{}
как это работает иMybatis generator
Далее было рассмотрено использование нескольких аспектов.
Можно резюмировать следующие пункты:
- Не используйте сплайсинг без использования сплайсинга, которого также следует избегать
SQL 注入
основные принципы - В использовании
${}
При передаче переменных обязательно обратите внимание на введение и фильтрацию переменных и избегайте передачи внешних переменных напрямую через ${} - не себя
造轮子
, особенно с точки зрения безопасности, на самом деле фреймворк уже обеспечивает стандартный подход в этом вопросе. Если он разработан в соответствии со спецификацией, он не вызовет проблем с внедрением SQL. - Обратите внимание, что в MyBatis
targetRuntime
конфигурации, если вам не нужен сложный условный запрос, рекомендуется использовать его напрямуюMyBatis3Simple
. Это может лучше напрямую устранить риск, потому что, как только есть точка риска, существует вероятность проблемы.
Автор: madneal@Ping Группа безопасности банковских приложений,Посмотреть исходный текст
Сегодня так много нужно сказать, если эта статья была вам полезна, я надеюсь получить от вас лайк 👍
Ваше одобрение является движущей силой для моего письма!