---
# System prepended metadata

title: SQL Injection

---

# SQL Injection

## 概述

使用者透過介面輸入非法的字串，導致 __SQL__ 被重新組合成非預期的 __SQL__，達到不被允許的目的。



## 情境

查詢訂單

### code

``` java
public Order find(String id,Long userId){
    String sql = "select * from order where id ='"+id+"' and user_id = "+userId;
    ResultSet resultSet =  connection.createStatement().executeQuery(sql);
}
```


### 正常輸入

id = "xxxx0001";

```sql
select * from `order` where id = 'xxxx0001' and user_id = 1;
```

### 不正常輸入

id = "' or 0=0 -- ";

```sql
select * from `order` where id = '' or 0=0 -- ' and user_id = 1;
```


## 原理

利用字串拼接組合 __SQL__ 的程式碼，在字串值中，使用 __'__ 特殊符號結束字串並注入新的指令。


## 解決方法

### 錯誤

* 將特殊符號取代
     * 譬如將 __'__ 取代為 __"__，則文字 __I'am__ 就會變成 __I"am__ ，汙染使用者原始輸入的資料。
     
    [剛好看到血淋淋的例子](https://ithelp.ithome.com.tw/articles/10189201)
    
* 遇到特殊符號返回錯誤
    * 等於告訴人家，我不懂 __SQL Injection__。


### 正確

* 使用 __PreparedStatement__

    但是實作原理與下方一樣，利用 __StringUtils.escapeBytes__ 讓特殊符號失效。

    ``` java
    public Order find(String id,Long userId){
        String sql = "select * from `order` where id =? and user_id = ?";
        PreparedStatement ps =  connection.prepareStatement(sql);
        ps.setString(1,id);
        ps.setLong(2,userId);
        // (id,userId)
    }
    ```
    
    實際上，在沒有設定 __cachePrepStmts__ 跟 __useServerPrepStmts__ 時，__SQL__ 會在本地組裝好，才發送給 __DB__
    
    ```sql
    select * from `order` where id = 'xxxx0001' and user_id = 1;
    ```
    
    
* 使 __'__ 符號失效

    ```java
    String id = "' or 0=0 -- ";
    id = id.replaceAll("'","''"); // 變為 "'' or 0=0 -- "

    // 使用跳脫字元
    id = id.replaceAll("'","\\'"); // 變為 "\' or 0=0 -- "
    ```
    
    __'__ 則會被判定為純字串
    ```sql
    select * from `order` where id = ''' or 0=0 -- ' and user_id = 1;
    select * from `order` where id = '\' or 0=0 -- ' and user_id = 1;
    
    select  '\' or 0=0 -- ' ;
    select  ''' or 0=0 -- ' ;
    ```
    
    __出處__
    
    參考 mysql-connector-j

    [StringUtils.escapeBytes](https://github.com/mysql/mysql-connector-j/blob/805f872a57875f311cb82487efcfb070411a3fa0/src/main/core-api/java/com/mysql/cj/util/StringUtils.java#L1822)
    
    
    
## 總結

* 官方源碼有教利用 __\\__ 跳脫字元，讓特殊符號失效。
* 替換時機點，應在執行 __SQL__ 前，否則可能因為流程混亂，導致資料被汙染。
* __PreparedStatement__ 職責不是處理 __SQL Injection__，另有大用。
    * https://freedocs.mi.hdm-stuttgart.de/sectPreparedStatements.html