SQLインジェクションとは何ですか?
SQL インジェクションとは、Web アプリケーションがユーザー入力データの正当性を判断しない、または厳密にフィルタリングしないことを意味し、攻撃者は管理者の知らないうちに、Web アプリケーション内の事前定義されたクエリ ステートメントの末尾に追加の SQL ステートメントを追加する可能性があります。特定の条件下で不正な操作を実行することにより、データベース サーバーをだまして不正な任意のクエリを実行し、対応するデータ情報をさらに取得します。
SQLの場合
String sql = "delete from table1 where id = " + "id";
この ID はリクエスト パラメーターから取得されます。パラメーターが
1001 または 1 = 1 に結合される場合、
最も実行されるステートメントは次のようになります。
String sql = "delete from table1 where id = 1001 or 1 = 1";
このとき、データベース内のデータは消去され、非常に深刻な結果が生じます。
Java プロジェクトで SQL インジェクションを防ぐ方法
以下の 4 種類があります。
- PreparedStatement は SQL インジェクションを防止します
- mybatis の #{} が SQL インジェクションを防止する
- リクエストパラメータ内の機密性の高い単語をフィルタリングする
- nginxリバースプロキシがSQLインジェクションを防ぐ
1. PreparedStatement による SQL インジェクションの防止
PreparedStatement にはプリコンパイル機能があり、上記 SQL を例に取ると、
PreparedStatement を使用したプリコンパイル SQL は次のようになります。
delete from table1 where id = ?
現時点では、SQL ステートメントの構造が修正されています。"?" がどのパラメーターに置き換えられても、SQL ステートメントは where の後に条件が 1 つだけあるとのみ考慮します。1001 または 1 = 1 が渡された場合、ステートメントはエラーを報告するため、SQL インジェクションが防止されます。
2. mybatis の #{} 式は SQL インジェクションを防止します。mybatis
の #{} 式は SQL インジェクションを防止します。これは PreparedStatement に似ており、どちらも SQL ステートメントを
プリコンパイルします。
#{}: パラメータのプレースホルダ
${}: 結合置換文字。SQL インジェクションを防ぐことはできません。一般的に使用されます。
- データベース オブジェクト (データベース名、テーブル名など) を渡します。
- order 後の条件は、リクエスト パラメーターの機密性の高い単語をフィルターします。
3. リクエストパラメータ内の機密ワードをフィルタリングする
springboot は次のように記述されます。
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.Enumeration;
@WebFilter(urlPatterns = "/*",filterName = "sqlFilter")
@Configuration
public class SqlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* @description sql注入过滤
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest request = servletRequest;
ServletResponse response = servletResponse;
// 获得所有请求参数名
Enumeration<String> names = request.getParameterNames();
String sql = "";
while (names.hasMoreElements()){
// 得到参数名
String name = names.nextElement().toString();
// 得到参数对应值
String[] values = request.getParameterValues(name);
for (int i = 0; i < values.length; i++) {
sql += values[i];
}
}
if (sqlValidate(sql)) {
//TODO 这里直接抛异常处理,前后端交互项目中,请把错误信息按前后端"数据返回的VO"对象进行封装
throw new IOException("您发送请求中的参数中含有非法字符");
} else {
filterChain.doFilter(request,response);
}
}
/**
* @description 匹配效验
*/
protected static boolean sqlValidate(String str){
// 统一转为小写
String s = str.toLowerCase();
// 过滤掉的sql关键字,特殊字符前面需要加\\进行转义
String badStr =
"select|update|and|or|delete|insert|truncate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute|table|"+
"char|declare|sitename|xp_cmdshell|like|from|grant|use|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|order|by|" +
"'\\*|\\;|\\-|\\--|\\+|\\,|\\//|\\/|\\%|\\#";
//使用正则表达式进行匹配
boolean matches = s.matches(badStr);
return matches;
}
@Override
public void destroy() {
}
}
4. nginx リバース プロキシは SQL インジェクションを防止します。
リバース プロキシに nginx を使用する Web サイトがますます増えています。SQL インジェクションを防止するようにこの層を構成することもできます。
次の Nginx 構成ファイルのコードをサーバー ブロックに配置し、Nginx を再起動します。
.
```sql
if ($request_method !~* GET|POST) {
return 444; }
#使用444错误代码可以更加减轻服务器负载压力。
#防止SQL注入
if ($query_string ~* (\$|'|--|[+|(%20)]union[+|(%20)]|[+|(%20)]insert[+|(%20)]|[+|(%20)]drop[+|(%20)]|[+|(%20)]truncate[+|(%20)]|[+|(%20)]update[+|(%20)]|[+|(%20)]from[+|(%20)]|[+|(%20)]grant[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]where[+|(%20)]|[+|(%20)]select[+|(%20)]|[+|(%20)]and[+|(%20)]|[+|(%20)]or[+|(%20)]|[+|(%20)]count[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]chr[+|(%20)]|[+|(%20)]mid[+|(%20)]|[+|(%20)]like[+|(%20)]|[+|(%20)]iframe[+|(%20)]|[\<|%3c]script[\>|%3e]|javascript|alert|webscan|dbappsecurity|style|confirm\(|innerhtml|innertext)(.*)$) {
return 555; }
if ($uri ~* (/~).*) {
return 501; }
if ($uri ~* (\\x.)) {
return 501; }
#防止SQL注入
if ($query_string ~* "[;'<>].*") {
return 509; }
if ($request_uri ~ " ") {
return 509; }
if ($request_uri ~ (\/\.+)) {
return 509; }
if ($request_uri ~ (\.+\/)) {
return 509; }
#if ($uri ~* (insert|select|delete|update|count|master|truncate|declare|exec|\*|\')(.*)$ ) {
return 503; }
#防止SQL注入
if ($request_uri ~* "(cost\()|(concat\()") {
return 504; }
if ($request_uri ~* "[+|(%20)]union[+|(%20)]") {
return 504; }
if ($request_uri ~* "[+|(%20)]and[+|(%20)]") {
return 504; }
if ($request_uri ~* "[+|(%20)]select[+|(%20)]") {
return 504; }
if ($request_uri ~* "[+|(%20)]or[+|(%20)]") {
return 504; }
if ($request_uri ~* "[+|(%20)]delete[+|(%20)]") {
return 504; }
if ($request_uri ~* "[+|(%20)]update[+|(%20)]") {
return 504; }
if ($request_uri ~* "[+|(%20)]insert[+|(%20)]") {
return 504; }
if ($query_string ~ "(<|%3C).*script.*(>|%3E)") {
return 505; }
if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") {
return 505; }
if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") {
return 505; }
if ($query_string ~ "proc/self/environ") {
return 505; }
if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") {
return 505; }
if ($query_string ~ "base64_(en|de)code\(.*\)") {
return 505; }
if ($query_string ~ "[a-zA-Z0-9_]=http://") {
return 506; }
if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") {
return 506; }
if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") {
return 506; }
if ($query_string ~ "b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)b") {
return 507; }
if ($query_string ~ "b(erections|hoodia|huronriveracres|impotence|levitra|libido)b") {
return 507; }
if ($query_string ~ "b(ambien|bluespill|cialis|cocaine|ejaculation|erectile)b") {
return 507; }
if ($query_string ~ "b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)b") {
return 507; }
#这里大家根据自己情况添加删减上述判断参数,cURL、wget这类的屏蔽有点儿极端了,但要“宁可错杀一千,不可放过一个”。
if ($http_user_agent ~* YisouSpider|ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl|Java|python) {
return 508; }
#同上,大家根据自己站点实际情况来添加删减下面的屏蔽拦截参数。
if ($http_user_agent ~* "Go-Ahead-Got-It") {
return 508; }
if ($http_user_agent ~* "GetWeb!") {
return 508; }
if ($http_user_agent ~* "Go!Zilla") {
return 508; }
if ($http_user_agent ~* "Download Demon") {
return 508; }
if ($http_user_agent ~* "Indy Library") {
return 508; }
if ($http_user_agent ~* "libwww-perl") {
return 508; }
if ($http_user_agent ~* "Nmap Scripting Engine") {
return 508; }
if ($http_user_agent ~* "~17ce.com") {
return 508; }
if ($http_user_agent ~* "WebBench*") {
return 508; }
if ($http_user_agent ~* "spider") {
return 508; } #这个会影响国内某些搜索引擎爬虫,比如:搜狗
#拦截各恶意请求的UA,可以通过分析站点日志文件或者waf日志作为参考配置。
if ($http_referer ~* 17ce.com) {
return 509; }
#拦截17ce.com站点测速节点的请求,所以明月一直都说这些测速网站的数据仅供参考不能当真的。
if ($http_referer ~* WebBench*") {
return 509; }
#拦截WebBench或者类似压力测试工具,其他工具只需要更换名称即可。