๐Ÿ•น๏ธ 3. ์ธ์ ์…˜

๐Ÿ•น๏ธ ๋ชจ์˜ํ•ดํ‚น ์ฒดํ—˜ ๋„์ „

๐Ÿ•น๏ธ ๋ชจ์˜ํ•ดํ‚น ์ฒดํ—˜ ๋”ฐ๋ผํ•˜๊ธฐ โ–ถSTEP_1) test ๊ณ„์ • ๋กœ๊ทธ์ธ

STEP_2) ๊ด€๋ฆฌ์ž ์•„์ด๋””(admin) ์ •๋ณด ํ™•์ธ

STEP_3) SQL ์ธ์ ์…˜ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ด€๋ฆฌ์ž ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ ์‹œ๋„

STEP_4) ๊ด€๋ฆฌ๋ผ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, FLAG ๊ฐ’ ํ™•์ธ ๊ฐ€๋Šฅ


[LDAP ์ธ์ ์…˜]โ–ถ

O ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’์— ๋น„์ •์ƒ์ ์ธ ์ฟผ๋ฆฌ๋ฅผ ํฌํ•จํ•œ ํ›„ ์ž…๋ ฅ์„ ์‹œ๋„ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ทจ์•ฝ์ 


O ๊ทผ๊ฑฐ ์ž๋ฃŒ 

โ˜ž ์ฃผ์š”์ •๋ณดํ†ต์‹ ๊ธฐ๋ฐ˜์‹œ์„ค ๊ธฐ์ˆ ์  ์ทจ์•ฝ์  ๋ถ„์„ ํ‰๊ฐ€ ์ƒ์„ธ ๊ฐ€์ด๋“œ(p.657)

โ˜ž OWASP TOP10 2021


O ํŒ๋‹จ ๊ธฐ์ค€

์–‘ํ˜ธ์ž„์˜์˜ LDAP ์ฟผ๋ฆฌ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ ธ ๋ณ€์กฐ๋œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ
์ทจ์•ฝ์ž„์˜์˜ LDAP ์ฟผ๋ฆฌ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•„ ๋ณ€์กฐ๋œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ


O ์ ๊ฒ€ ๋ฐฉ๋ฒ•

-  ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’ ๋‚ด์— LDAP์—์„œ ์™€์ผ๋“œ ๋ฌธ์ž๋กœ ์‚ฌ์šฉ๋˜๋Š” * ๊ธฐํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ๋ฐ˜ํ™˜๋˜๋Š” ๋ฐ์ดํ„ฐ ๋Ÿ‰์˜ ๋ณ€ํ™”๋ฅผ ํ™•์ธํ•œ๋‹ค.

-  [*);cn;], [*));cn;], [*)));cn;], [*)));cn;]์˜ ๋ฐฉ๋ฒ•์œผ๋กœ ) ๊ธฐํ˜ธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.


O ์กฐ์น˜ ๋ฐฉ๋ฒ•

- ์ž…๋ ฅ์ด LDAP ์ฟผ๋ฆฌ์— ํฌํ•จ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด, ์ž…๋ ฅํ•œ ๊ฐ’์€ ํ—ˆ์šฉ๋œ ๋ฌธ์ž๋กœ๋งŒ ๊ฒ€์ฆ์ด ๋˜์–ด์•ผ ํ•˜๊ณ , LDAP์ฟผ๋ฆฌ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ํŠน์ˆ˜๋ฌธ์ž ์ž…๋ ฅ ์ œํ•œ

- ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ &lsquo=', '+', '<', '>' ๋“ฑ์˜ ํŠน์ˆ˜ ๋ฌธ์ž์˜ ๊ฒฝ์šฐ ์‹คํ–‰ ๋ช…๋ น์ด ์•„๋‹Œ ์ผ๋ฐ˜ ๋ฌธ์ž๋กœ ์ธ์‹๋˜๋„๋ก ์ฒ˜๋ฆฌ

# ํ•„ํ„ฐ๋ง ๋Œ€์ƒ

'"--#(
)=*//*+
<>
user_tables
user_table_columns
user_table_columns
column_name
syscolumns
union
select
insert
drop
update
and
or
if
join
substring
from
where
declare
substr
openrowset
xpsysobjectdeclare
*
from
&
|


O ์‹œํ์–ด ์ฝ”๋”ฉ(Secure Coding) ์˜ˆ์‹œ

# JAVA โ–ถ 
private void searchRecord(String userSN, String userPassword) throwsย NamingException {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    
    try {
        DirContext dctx = new InitialDirContext(env);
        SearchControls sc = new SearchControls();
        String[] attributeFilter = {"cn", "mail" };
        sc.setReturningAttributes(attributeFilter);
        sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
        String base = "dc=example,dc=com";
        
ย  ย  ย  ย  // userSN๊ณผ userPassword ๊ฐ’์—์„œ LDAP ํ•„ํ„ฐ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž์—ด์„ ์ œ๊ฑฐํ•˜๊ณ  ์‚ฌ์šฉ
        if (!userSN.matches("[๏ฟฆ๏ฟฆw๏ฟฆ๏ฟฆs]*") || !userPassword.matches("[๏ฟฆ๏ฟฆw]*")) {
            throw new IllegalArgumentException("Invalid input");
        }
        
        String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))";
        NamingEnumeration<!--?--> results = dctx.search(base, filter, sc);

        while (results.hasMore()) {
            SearchResult sr = (SearchResult) results.next();
            Attributes attrs = sr.getAttributes();
            Attribute attr = attrs.get("cn");
            ......
        }

        dctx.close();
    } catch (NamingException e) {
ย  ย  ย  ย  // ์˜ˆ์™ธ ์ฒ˜๋ฆฌ 
    }
}


# C# โ–ถ 
static void SearchRecord(string userSN, string userPW){
    try {
            DirectoryEntry oDE;
            oDE = new DirectoryEntry(GetStrPath(), userSN, userPW);
            // userSN๊ณผ userPW ๋กœ ์ธ์ฆ ํ›„์— LDAP ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰
            foreach(DirectoryEntry objChildDE om oDE.Children) {
ย  ย  ย  ย  ย  ย  ย  ย  .....
            }
ย  ย  } catch (NamingException e){
ย  ย  ย  ย  // ์˜ˆ์™ธ ์ฒ˜๋ฆฌ 
    }
}


# C โ–ถ 
void LDAPInjection() {
    char *filter = getenv(“Filter”);
    int error_code; int i;
    LDAP *ld = NULL;
    LDAPMessage *result;
    
    // ์ •๋ณด๋ฅผ ์•Œ๊ณ  ์‹ถ์€ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์„ ๊ณ ์ • ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
    for(i = 0; *(filter + i) != 0; i++) {
        // ๊ณต๊ฒฉ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด ๊ฒ€์‚ฌ
        switch(*(filter + i)) {
            case '*':
            case '(':
            case ')':
            .....
                return;
        }
    }
    error_code = ldap_search_ext_s(ld, FIND_DN, LDAP_SCOPE_BASE, filter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result);
}



[SQL ์ธ์ ์…˜]โ–ถ 

O ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๋™๋œ ์›น ์‘์šฉํ”„๋กœ๊ทธ๋žจ์—์„œ ์ž…๋ ฅ๋œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์„ ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ๊ณต๊ฒฉ์ž๊ฐ€ ์ž…๋ ฅ ํผ ๋ฐ URL ์ž…๋ ฅ๋ž€์— SQL๋ฌธ์„ ์‚ฝ์ž…ํ•˜์—ฌ DB๋กœ๋ถ€ํ„ฐ ์ •๋ณด๋ฅผ ์—ด๋žŒํ•˜๊ฑฐ๋‚˜ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ทจ์•ฝ์ 


O ๊ทผ๊ฑฐ ์ž๋ฃŒ

โ˜ž ์ฃผ์š”์ •๋ณดํ†ต์‹ ๊ธฐ๋ฐ˜์‹œ์„ค ๊ธฐ์ˆ ์  ์ทจ์•ฝ์  ๋ถ„์„ ํ‰๊ฐ€ ์ƒ์„ธ ๊ฐ€์ด๋“œ(p.661)

โ˜ž OWASP TOP10 2021


O ํŒ๋‹จ ๊ธฐ์ค€

์–‘ํ˜ธ์ž„์˜์˜ SQL ์ฟผ๋ฆฌ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ ธ ์ž„์˜์˜ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ
์ทจ์•ฝ์ž„์˜์˜ SQL ์ฟผ๋ฆฌ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ


O ์ ๊ฒ€ ๋ฐฉ๋ฒ•

- ๋ณ€์ˆ˜ ๊ฐ’์— ํฐ๋”ฐ์˜ดํ‘œ(“), ์ž‘์€๋”ฐ์˜ดํ‘œ(‘), ์„ธ๋ฏธ์ฝœ๋ก (;) ๋“ฑ์„ ์ž…๋ ฅํ•œ ํ›„, DB error๊ฐ€ ์ผ์–ด๋‚˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

- ['and'a&rsquo='a, 'and'a&rsquo='b], [and 1=1, and 1=2]์˜ ์„ธํŠธ์˜ ๊ฐ’์„ ๊ฐ๊ฐ ์‚ฝ์ž…ํ•˜์—ฌ ์ •์ƒ ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ณด๋‚ธ ํ™”๋ฉด๊ณผ [๋™์ผํ™”๋ฉด, ๋‹ค๋ฅธํ™”๋ฉด]์˜ ๋ฐ˜์‘์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

- ๋กœ๊ทธ์ธ ํ™”๋ฉด์˜ ๊ฒฝ์šฐ ์•„์ด๋””๋‚˜ ํŒจ์Šค์›Œ๋“œ ๋ณ€์ˆ˜ ๊ฐ’์— ['or 1=1 –], ['or&rdquo='] ๋“ฑ์˜ SQL ์ฟผ๋ฆฌ๋ฌธ์„ ์ž…๋ ฅํ•œ ํ›„ ๋กœ๊ทธ์ธ์ด ๋˜๋Š”์ง€ ์‹œ๋„ํ•œ๋‹ค.


O ์กฐ์น˜ ๋ฐฉ๋ฒ•

๋ฌธ์ž์—ด ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋กœ์ง ๊ตฌํ˜„

# ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’ ์ œํ•œ ํŠน์ˆ˜ ๋ฌธ์ž (DB์— ๋”ฐ๋ผ ๋ฌธ์ž๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ)

๋ฌธ์ž์„ค๋ช…
'๋ฌธ์ž ๋ฐ์ดํ„ฐ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ
;์ฟผ๋ฆฌ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ
--, #ํ•ด๋‹น๋ž‘์ธ ์ฃผ์„ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ
/*,  *//*์™€ */ ์‚ฌ์ด ๊ตฌ๋ฌธ ์ฃผ์„

- Dynamic SQL ๊ตฌ๋ฌธ ์‚ฌ์šฉ ๊ธˆ์ง€ ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ํ•„์ˆ˜ ์ ์šฉ

- ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

: ๊ณต๊ฒฉ์ž์—๊ฒŒ ์‹œ์Šคํ…œ ์ •๋ณด ๋“ฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ํ†ตํ•œ ์ •๋ณด ๋…ธ์ถœ ์ตœ์†Œํ™”

: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฐ DBMS์—์„œ ์ œ๊ณตํ•˜๋Š” ์—๋Ÿฌ ์ฝ”๋“œ๊ฐ€ ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

์›น ๋ฐฉํ™”๋ฒฝ์„ ํ†ตํ•œ Injection ๊ณต๊ฒฉ ์ฐจ๋‹จ ์ •์ฑ… ์ ์šฉ

ํ•„ํ„ฐ๋ง ๋“ฑ ์ž…๋ ฅ ๊ฐ’ ๊ฒ€์ฆ ํ”„๋กœ์„ธ์Šค๋Š” ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์Šคํฌ๋ฆฝํŠธ๋กœ ๊ตฌํ˜„


O ์‹œํ์–ด ์ฝ”๋”ฉ(Secure Coding) ์˜ˆ์‹œ 

# JAVA โ–ถ 
import java.util.regex.Matcher;
import java.util.regex.Pattern;

final Pattern SpecialChars = Pattern.compile("['\"\\-#,-()@=*/+]");
// ์ธ์ ์…˜์— ์‚ฌ์šฉ๋˜๋Š” ํŠน์ˆ˜๋ฌธ์ž ํ•„ํ„ฐ๋ง
UserInput = SpecialChars.matcher(UserInput).replaceAll("");

// ์ธ์ ์…˜์— ์‚ฌ์šฉ๋˜๋Š” SQL ๋ช…๋ น์–ด ํ•„ํ„ฐ๋ง
final String regex = "(union|select|from|where|case|then|dual|having|group by)";ย 
final Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
final Matcher matcher = pattern.matcher(UserInput);

if(matcher.find()){
    out.println("");
}


# PHP โ–ถ 
// ์ธ์ ์…˜์— ์‚ฌ์šฉ๋˜๋Š” ํŠน์ˆ˜๋ฌธ์ž ํ•„ํ„ฐ๋ง
$UserInput = preg_replace("/[\r\n\s\t\'\;\"\=\-\-\#\/*]+/","", $UserInput);

// ์ธ์ ์…˜์— ์‚ฌ์šฉ๋˜๋Š” SQL ๋ช…๋ น์–ด ํ•„ํ„ฐ๋ง
if(preg_match('/(union|select|from|where)/i', $UserInput)) {
    $this–>Error_popup('No SQL-Injection');
}


# php.ini ์„ค์ • ๋ณ€๊ฒฝ โ–ถ 

- php.ini ์„ค์ • ์ค‘ magic_quotes_gpc ๊ฐ’์„ On์œผ๋กœ ์„ค์ •

Magic quotes
// Magic quotes for incoming GET/POST/Cookie data
// off์—์„œ on์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
magic_quotes_gpc = ON ;
// Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off
// Use Sybase-style magic quotes (escape ' with " instead of \').
magic_quotes_sybase = Off



[SSI ์ธ์ ์…˜]โ–ถ 

O SSI(Server-Side Includes)๋Š” HTML๋ฌธ์„œ ๋‚ด ๋ณ€์ˆ˜ ๊ฐ’์œผ๋กœ ์ž…๋ ฅ๋œ ํ›„, ์ด๋ฅผ ์„œ๋ฒ„๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ๋•Œ ์ธ์ ์…˜ ๋ช…๋ น๋ฌธ์ด ์ˆ˜ํ–‰๋˜์–ด ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ ์ •๋ณด๊ฐ€ ๋ˆ„์ถœ๋˜๋Š” ์ทจ์•ฝ์ 

* SSI(Server-Side Includes) : CGI ํ”„๋กœ๊ทธ๋žจ์„ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ํ˜น์€ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์–ธ์–ด๋กœ, ์›น ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์ „์— ๊ตฌ๋ฌธ์„ ํ•ด์„ํ•˜๋„๋ก ์ง€์‹œํ•˜๋Š” ์—ญํ• 


O ๊ทผ๊ฑฐ ์ž๋ฃŒ

โ˜ž ์ฃผ์š”์ •๋ณดํ†ต์‹ ๊ธฐ๋ฐ˜์‹œ์„ค ๊ธฐ์ˆ ์  ์ทจ์•ฝ์  ๋ถ„์„ ํ‰๊ฐ€ ์ƒ์„ธ ๊ฐ€์ด๋“œ(p. 667)

โ˜ž OWASP TOP10 2021


O ํŒ๋‹จ ๊ธฐ์ค€

์–‘ํ˜ธ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒฝ์šฐ
์ทจ์•ฝ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ


O ์ ๊ฒ€ ๋ฐฉ๋ฒ•

๋ณ€์ˆ˜ ๊ฐ’์— <!–#echo var="DOCUMENT_ROOT" –>๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ํ‘œ์‹œ ํŽ˜์ด์ง€์— ์‚ฌ์ดํŠธ์˜ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

- ๋ณ€์ˆ˜ ๊ฐ’์— <!–#exec cmd="ls -al" –>๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ํ‘œ์‹œ ํŽ˜์ด์ง€์— ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ๊ฐ€ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.


O ์กฐ์น˜ ๋ฐฉ๋ฒ•

์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ์ด ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž๋“ค์„ ์ œํ•œํ•˜์—ฌ ์ •ํ•ด์ง„ ๋ฌธ์ž๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ๋ชจ๋“  ๋ฌธ์ž์— ๋Œ€ํ•˜์—ฌ ํ•„ํ„ฐ๋ง ์ˆ˜ํ–‰

: ํ•„ํ„ฐ๋ง ๋Œ€์ƒ์€ GET ์งˆ์˜ ๋ฌธ์ž์—ด, POST ๋ฐ์ดํ„ฐ, ์ฟ ํ‚ค, URL, ๊ทธ๋ฆฌ๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์™€ ์›น ์„œ๋ฒ„๊ฐ€ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จ

# ํŠน์ˆ˜๋ฌธ์ž์— ๋Œ€ํ•œ Entity ํ˜•ํƒœ ์˜ˆ์‹œ

๋ณ€๊ฒฝ ์ „<>"()$&
๋ณ€๊ฒฝ ํ›„&lt;&gt;&quot;&#40;&#41;&#35;&amp



[XPath ์ธ์ ์…˜]โ–ถ 

O XML ๋ฌธ์„œ๋ฅผ ์กฐํšŒํ•  ๊ฒฝ์šฐ ์ž…๋ ฅ ๊ฐ’ ์กฐ์ž‘์„ ํ†ตํ•ด XQuery๋‚˜ XPath์™€ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฌธ์˜ ๊ตฌ์กฐ๋ฅผ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ํ—ˆ๊ฐ€๋˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ์ธ์ฆ์ ˆ์ฐจ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ์ทจ์•ฝ์ 


O ๊ทผ๊ฑฐ ์ž๋ฃŒ

โ˜ž ์ฃผ์š”์ •๋ณดํ†ต์‹ ๊ธฐ๋ฐ˜์‹œ์„ค ๊ธฐ์ˆ ์  ์ทจ์•ฝ์  ๋ถ„์„ ํ‰๊ฐ€ ์ƒ์„ธ ๊ฐ€์ด๋“œ(p.669)

โ˜ž OWASP TOP10 2021


O ํŒ๋‹จ ๊ธฐ์ค€

์–‘ํ˜ธ์ฟผ๋ฆฌ ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•ด ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒฝ์šฐ
์ทจ์•ฝ์ฟผ๋ฆฌ ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ


O ์ ๊ฒ€ ๋ฐฉ๋ฒ•

- ['and' a'='a, 'and' a'='b], [ and 1=1,  and 1=2]์˜ ์„ธํŠธ์˜ ๊ฐ’์„ ๊ฐ๊ฐ ์‚ฝ์ž…ํ•˜์—ฌ ์ •์ƒ ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ณด๋‚ธ ํ™”๋ฉด๊ณผ [๋™์ผํ™”๋ฉด, ๋‹ค๋ฅธํ™”๋ฉด]์˜ ๋ฐ˜์‘์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

- ๋‹ค์Œ ๊ฐ’์„ ์ž…๋ ฅํ•ด์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

: 'or count(parent::*[position()=1])=0 or 'a'='b

: 'or count(parent::*[position()=1])>0 or 'a'='b

: 1 or count(parent::*[position()=1])=0

: 1 or count(parent::*[position()=1])>0


O ์กฐ์น˜ ๋ฐฉ๋ฒ•

XPath ์ฟผ๋ฆฌ์— ์ž…๋ ฅ ๊ฐ’์ด ์ž…๋ ฅ๋˜๋Š” ๊ฒฝ์šฐ, ( ) = ' [ ] : , * / ๋“ฑ ํŠน์ˆ˜๋ฌธ์ž ๋ฐ ์ฟผ๋ฆฌ ์˜ˆ์•ฝ์–ด ํ•„ํ„ฐ๋ง

- ํŠน์ • ํŠน์ˆ˜๋ฌธ์ž ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ—ˆ์šฉ๋œ ๋ฌธ์ž ์ด์™ธ์˜ ๋ชจ๋“  ์ž…๋ ฅ ์ œํ•œ


O ์‹œํ์–ด ์ฝ”๋”ฉ(Secure Coding) ์˜ˆ์‹œ

# JAVA (์‚ฌ์ „ XQuery ๊ณจ๊ฒฉ ์ƒ์„ฑ) โ–ถ 
[login.xq ํŒŒ์ผ]
declare variable $loginID as xs:string external; 
declare variable $password as xs:string external;

// users/user[@loginID=$loginID and @password=$password]
// XQuery๋ฅผ ์ด์šฉํ•œ XPath Injection ๋ฐฉ์ง€
String nm = props.getProperty("name");
String pw = props.getProperty("password");
Document doc = new Builder().build("users.xml");

// ํŒŒ๋ผ๋ฏธํ„ฐํ™” ๋œ ์ฟผ๋ฆฌ๊ฐ€ ๋‹ด๊ฒจ์žˆ๋Š” login.xq๋ฅผ ์ฝ์–ด์™€์„œ ํŒŒ๋ผ๋ฏธํ„ฐํ™” ๋œ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
XQuery xquery = new XQueryFactory().createXQuery(new File("login.xq"));
Map vars = new HashMap();

// ๊ฒ€์ฆ๋˜์ง€ ์•Š์€ ์™ธ๋ถ€ ๊ฐ’์ธ nm, pw๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐํ™” ๋œ ์ฟผ๋ฆฌ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์„ค์ •ํ•œ๋‹ค.
vars.put("loginID", nm);
vars.put("password", pw);

// ํŒŒ๋ผ๋ฏธํ„ฐํ™” ๋œ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฏ€๋กœ ์™ธ๋ถ€ ๊ฐ’์„ ๊ฒ€์ฆ์—†์ด ์‚ฌ์šฉํ•˜์—ฌ๋„ ์•ˆ์ „ํ•˜๋‹ค.
Nodes results = xquery.execute(doc, null, vars).toNodes();
for (int i=0; i<results.size(); i++){
    system.out.println(results.get(i).toXML());
}


# JAVA โ–ถ 
// XPath ์‚ฝ์ž…์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž๋“ค์„ ์ž…๋ ฅ ๊ฐ’์—์„œ ์ œ๊ฑฐ
public String XPathFilter(String input) {
    if (input != null) 
        return input.replaceAll("[',๏ฟฆ๏ฟฆ[]", ""); 
    } else { 
        return "";
    }
}

......

// ์™ธ๋ถ€ ์ž…๋ ฅ ๊ฐ’์— ์‚ฌ์šฉ
String nm = XPathFilter(props.getProperty("name")); 
String pw = XPathFilter(props.getProperty("password"));

......

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

......

// ์™ธ๋ถ€ ์ž…๋ ฅ ๊ฐ’์ธ nm, pw๋ฅผ ๊ฒ€์ฆํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฌธ์„ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์•ˆ์ „ํ•˜๋‹ค.
XPathExpression expr = xpath.compile("//users/user[login/text()='"+nm+"' and password/text()='"+pw+"']/home_dir/text()");