엄범


SQL Escape method

DB에 데이터를 보내야 하는 경우 PDO의 prepared statement를 사용하는 편이 좋다.
PDO를 사용하지 않는 경우 DB에 보낼 데이터를 이스케이프하는 함수로는 데이터베이스에서 지원해주는 함수를 사용하는 것이 좋으며, 이마저도 사용할 수 없을 때 ``php addslashes()``를 고려한다.

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부터 제거된 옵션이다.



'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