sql注入を効果的に防止する方法の実証


前言
SQL注入攻撃はハッカーがデータベースを攻撃する際によく使われる手段の一つであり、B/Sモードのアプリケーション開発の発展に伴い、このようなモードを使ってアプリケーションを作成するプログラマーも増えてきました。しかし、プログラマのレベルや経験がまちまちなため、かなりの一部のプログラマーがコードを作成する際に、ユーザーがデータを入力する合法性を判断していないため、アプリケーションにセキュリティ上の問題があります。ユーザはデータベースクエリコードの一部を提出することができ、プログラムの結果によって、彼が取得したいデータを得ることができます。これはSQL Injectionと呼ばれるSQL注入です。
一つの背景
もしある大学がネット授業システムを開発したら、学生に授業を選んでから学習を完成させるように要求します。データベースの中に表courseがあります。この表には各学生の選択情報と完成状況が保存されています。具体的な設計は以下の通りです。

データは以下の通りです

このシステムはmysqlをデータベースにして、Jdbcを使ってデータベースの関連操作を行います。システムは、次のコードの学生のコースの完了状況を調べるための機能を提供します。

@RestController
public class Controller {
  
  @Autowired
  SqlInject sqlInject;
  
  @GetMapping("list")
  public List<Course> courseList(@RequestParam("studentId") String studentId){
    List<Course> orders = sqlInject.orderList(studentId);
    return orders;
  }
}

@Service
public class SqlInject {

  @Autowired
  private JdbcTemplate jdbcTemplate;
  
  public List<Course> orderList(String studentId){
    String sql = "select id,course_id,student_id,status from course where student_id = "+ studentId;
    return jdbcTemplate.query(sql,new BeanPropertyRowMapper(Course.class));
  }
}
二注入攻撃のデモ
1.通常の状況で、学生の選択した授業と完成状況を調べるには、student_に入る必要があります。IDなら、関連データを調べることができます。

応答の結果によって、私達はすぐに対応するsqlを書くことができます。

select id,course_id,student_id,status 
from course 
where student_id = 4
2.この表のすべてのデータを取得するには、上記のsqlのwhere条件が恒真であればいいです。

select id,course_id,student_id,status 
from course 
where student_id = 4 or 1 = 1 
インターフェースを要求する時、studedIdを4 or 1=1に設定します。このsqlのwhere条件は恒真です。sqlは次のようなものです。

select id,course_id,student_id,status 
from course 
要求の結果は以下の通りです。私たちはこの表のすべてのデータを手に入れました。

3.mysqlバージョン番号を調べ、unionでsqlを接続する。

union select 1,1,version(),1

4.クエリーデータベース名

union select 1,1,database(),1

5.mysql現在のユーザーのすべてのライブラリを調べます。

union select 1,1, (SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata) schemaName,1

これらのプレゼンテーションを見てから、怖くなりましたか?あなたのすべてのデータの配置はすべて完全に暴露して、この以外、また多くの操作を完成することができて、データを更新して、削除して、時計などを削除します。
三どのようにsql注入を防止しますか?
1.コード層がsql注入攻撃を防止する一番いい方法はsqlプリコンパイルです。

public List<Course> orderList(String studentId){
  String sql = "select id,course_id,student_id,status from course where student_id = ?";
  return jdbcTemplate.query(sql,new Object[]{studentId},new BeanPropertyRowMapper(Course.class));
}
このようにして私たちが送ってきたパラメータ4 or 1 = 1student_idとして扱われますので、sql注入は発生しません。
2.各データの種類を確認します。たとえば、数字であれば、データベースはintタイプを使って記憶しなければなりません。
3.データ長を規定し、ある程度sql注入を防ぐことができます。
4.データベースの権限を厳しく制限し、sql注入の危害を最大程度減らすことができる。
5.sql異常情報に直接応答しないでください。sqlに異常が発生したら、カスタム異常に応答します。
6.フィルタパラメータに含まれるいくつかのデータベースのキーワード

@Component
public class SqlInjectionFilter implements Filter {
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req=(HttpServletRequest)servletRequest;
    HttpServletRequest res=(HttpServletRequest)servletResponse;
    //         
    Enumeration params = req.getParameterNames();
    String sql = "";
    while (params.hasMoreElements()) {
      //      
      String name = params.nextElement().toString();
      //        
      String[] value = req.getParameterValues(name);
      for (int i = 0; i < value.length; i++) {
        sql = sql + value[i];
      }
    }
    if (sqlValidate(sql)) {
      throw new IOException("                ");
    } else {
      chain.doFilter(servletRequest,servletResponse);
    }
  }

  /**
   *      
   * @param str
   * @return
   */
  protected static boolean sqlValidate(String str) {
    //       
    str = str.toLowerCase();
    //     sql   ,      
    String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|*|%|chr|mid|master|truncate|" +
        "char|declare|sitename|net user|xp_cmdshell|;|or|-|+|,|like'|and|exec|execute|insert|create|drop|" +
        "table|from|grant|use|group_concat|column_name|" +
        "information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|*|" +
        "chr|mid|master|truncate|char|declare|or|;|-|--|+|,|like|//|/|%|#";
    String[] badStrs = badStr.split("\\|");
    for (int i = 0; i < badStrs.length; i++) {
      if (str.indexOf(badStrs[i]) >= 0) {
        return true;
      }
    }
    return false;
  }
}
締め括りをつける
ここでsql注入を防止する効果的な文章について紹介します。これまでの文章を検索したり、次の関連記事を見たりしてください。これからもよろしくお願いします。