엄범


SQL Escape method

mysqli::real_escape_string

이스케이프 문자 목록
```php
'   "   \   \x00(NUL)   \x1a(EOF)   \n   \r
```

아래는 모두 동일한 함수.
```php
string mysqli::escape_string ( string $escapestr ) // alias
string mysqli::real_escape_string ( string $escapestr )
string mysqli_real_escape_string ( mysqli $link , string $escapestr )
```

addslashes

이스케이프 문자 목록
```php
'   "   \   \x00(NUL)
```
DB에서 지원해주는 함수가 정 없다면 addslashes() 를 사용한다.

magic_quotes_gpc    ( REMOVED as of PHP 5.4.0. )

gpc(GET, POST, COOKIE)에 자동으로 ``php addslashes()``를 적용해서 이스케이프한다.
비활성화하고 GPC+α에 일관성 있게 DB 고유의 이스케이프 함수를 사용하는 것이 좋다.

magic_quotes_runtime    ( REMOVED as of PHP 5.4.0. )

external source(e.g., db, text file)에서 데이터를 가져오는 함수의 결과 데이터가 quotes를 포함하는 경우 backslash로 이스케이프한다.


bypass

숫자형 데이터로 넘기기

query에서 ``php $id``를 ``php ''``로 감싸지 않았을 때,

단순히 ``php mysql_real_escape_strig($id)``만 수행하면 숫자가 넘어올 경우 이를 숫자형 데이터로 간주해 우회된다.

따라서 다음과 같은 방법으로 Filtering / Escape 하며, query 내의 변수는 ``php '$id'``형태로 꼭 감싸주어 문자열로 인식할 수 있도록 한다.

```php
$id = $_GET['id'];
$id = stripslashes($id);
$id = mysqli_real_escape_string($id);

if (is_numeric($id)) {
    ...
```

multibyte encoding

중간에 charset을 변경하거나, escape를 다른 charset으로 지정하고 수행하는 경우, 
multibyte를 나타내는`` 0x??``과 `` \(0x5c)``를 묶어 한 문자로 인식하도록 만들어 escape하면서 붙게되는 `` \``를 제거할 수 있다. 이를 이용해 `` \``나 `` '``같은 escape 대상 문자를 그냥 입력하는 것이 가능하다.
EUC-KR → UTF-8 변환인 경우, EUC-KR이 2 bytes를 나타내는데 `` \xa1`` 부터 사용하므로 `` \xa1~\xfe``를 사용하면 된다.
`` \xa1\x5c``는 EUC-KR에 없는 문자이지만, 한 문자로 인식되며 UTF-8로 변환 후에도 한 문자로 인식된다. 
```php
$id = $mysqli->real_escape_string($_GET['id']);
$id = mb_convert_encoding($id, 'UTF-8', 'EUC-KR');
$result = $mysqli->query("select * from testtbl where id='$id'");
```
URL에 `` %a1~%fe``를 던져도 되고, python으로 진행해도 된다.
```py
url = "http://127.0.0.1/mb_bypass.php"
payload = {'id':"p2\xa1' or 1#"}
r = requests.get(url, params=payload)
```
```php
after escape : p2�\' or 1#
after mb_convert : p2?' or 1#

ALL RECODES LEAK!

```


Note ) 또 다른 charset 변환 함수 ``php iconv()``에서는 없는 문자가 들어오면 에러가 발생한다.`` \xa1\xa0`` 부터 에러 발생.

```php

$id = iconv('EUC-KR', 'UTF-8', $id);

iconv(): Detected an illegal character in input string

```

그러나 `` //IGNORE``를 지정해주는 경우, bypass 가능하다. ( `` //TRANSLIT``도 지정 가능하지만 이건 에러가 발생한다. )

```php

$id = iconv('EUC-KR', 'UTF-8//IGNORE', $id);

```

```php

after escape : p2�\' or 1#

after mb_convert : p2' or 1#

ALL RECODES LEAK!

```

따라서 user input을 변환해야 한다면 이를 옵션 지정하지 말고 사용하는 편이 좋다.


Indirect SQL Injection

input data만 escape하고, DB에서 불러오는 데이터는 escape하지 않을 때 발생한다.

파라미터로 `` guest'``를 전송하면 서버 단에서 다음과 같이 escape되지만, 결과적으로 DB에 저장되는 것은 `` guest'``다.
```
url : ?id=guest'
php : guest\'
query : insert into member where value('guest\'');
DB : guest'
```
이러한 상황에서 DB에서 불러온 `` guest'``를 escape하지 않고 그대로 쿼리에 집어넣는 코드가 있다면 `` guest'``가 쿼리에 영향을 미치게 된다.

즉, user input이든 DB든 외부 source에서 온 데이터는 무조건 필터링과 escape 과정을 거쳐야한다.

* `` magic_quotes_runtime=off``일 때만 적용되나, PHP 5.4부터 제거된 옵션이다.


이렇게 Bypass가 가능하니 preparedStatement를 사용하는 것이 좋다.

SQL injection Mitigation



'Languages & Frameworks > PHP' 카테고리의 다른 글

[PHP] File Upload  (0) 2017.11.11
[PHP] File IO  (0) 2017.11.11
[PHP] SQL Escape & Bypass  (0) 2017.10.23
[PHP] mysqli, PDO / password_*  (0) 2017.06.24
[PHP] 함수, 클래스, 객체, 상속, 트레이트  (0) 2017.06.10
[PHP] form tag, GET POST / cookie, session  (0) 2017.06.10