2013-03-19

SQL injection

Энэ бичсэн зүйлээ сайжруулъя гэж бодсоор хэдэн жил болчихож. Хэзээ нэгэн цагт илүү чадвартай болохоороо өөр маягаар илүү хялбар ойлгогдохоор нийтлэл бичнээ.

SQL injection бол програм болон вэбийг өгөгдлийн сантай холбогдох үед гардаг хамгаалалтын сул талыг ашиглан програм болон вэб сайтад халдах арга юм. Энэхүү халдлагыг хийхдээ хэрэглэгчийн гараас оруулах өгөгдөлд мөр (string) төрлийн тусгай тэмдэгтүүд агуулсан SQL команд (statement) оруулдаг. Энэ нь програмчлалын хэл буюу скрипт хэлийг хамтад нь ашиглах үед үүсдэг.

string утга нь програмчлалын хэл бүр дээр өөр боловч .net c# хэлний хувьд “” болно. Жишээлбэл, “Сайн байцгаана уу!”

Жишээ 1.
Ойлгомжтой тайлбарлах үүднээс дараах жишээн дээр хэрхэн ажилладгийг харуулъя. Дараах програмын кодын мөр нь халдлага хийх боломж бүхий сул талтай.

statement := "SELECT * FROM users WHERE name = '" + userName + "';"

Дээрх SQL код нь хэрэглэгчийн (users) хүснэгтээс тухайн хэрэглэгчийн нэр бүхий мэдээлэл байгаа эсэхийг шалгах зорилготой. Гэвч “userName” хувьсагчийг буруу байдлаар ашиглах тохиолдолд энэхүү SQL командыг ашиглан уг кодыг зохиогчийнбодсоноос өөр аргаар ашиглаж болно. Жишээлбэл “userName” хувьсагчийг:

‘a' or 't'='t’

гэж өөрчилбөл өмнөх SQL команд дараах хэлбэртэй болно:

SELECT * FROM users WHERE name = 'a' or 't'='t';

Энэ кодыг програмд нэвтрэхэд ашиглавал 't'='t' үргэлж үнэн байх тул хүссэн үедээ хүссэн хэрэглэгчийн нэрээр нэвтрэх боломжтой болно гэсэн үг.

MS SQL сервер зэрэг зарим SQL серверийн хувьд алдаагүй SQL командыг иймэрхүү аргаар болон хэд хэдэн statement-ийг нийлүүлэх замаар оруулж болно. Доорх жишээнд хэрэглэгчийн хүснэгтийг хэрхэн устгаж болохыг харж болно. Мэдээжхэрэглэгч бүрийн мэдээллийг харуулах боломжтой.

a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '%

Дараах оруулсан өгөгдөлийн дагуу гүйцэтгэгдэх SQL statement харуулбал:

SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '%';

Бусад SQL серверүүд аюулгүй байдлын үүднээс тухайн нэг SQL query-д нэгэн зэрэг хэд хэдэн SQL командыг биелүүлэх боломжийг хаасан байдаг. Гэвч энэ нь ялгаатай query-нүүдийг оруулж өгөхөөс сэргийлэх боловч тухайн query-г өөрчлөхөөс хамгаалж чадахгүй.

Жишээ 2.
SQL statement-д тоон талбар ашиглагдах үед програм зохиогч хэрэглэгчээс оруулж өгсөн өгөгдөл тоо эсэхийг шалгах шалгалт хийхгүй бол дараах нөхцөл байдал үүснэ.

statement := "SELECT * FROM data WHERE id = " + a_variable + ";"

Үүнээс харахад програм зохиогч a_variable “id” талбарт харгалзах тоо болгон ашигласан болохыг харж болно. Гэвч “string” бичигдэж болж байвал тусгай тэмдэгтүүд ашиглахгүйгээр дараах байдлаар a_variable-ийг өөрчилбөл:

1;DROP TABLE users

Үр дүнд нь “users” хүснэгтийг устгах бөгөөд SQL маань дараах хэлбэртэй болно:

SELECT * FROM data WHERE id = 1;DROP TABLE users;

Програм болон вэб дотроосоо SQL injection халдлагаас хэрхэн хамгаалах вэ?
.NET C# хэлний хувьд ADO.NET SqlCommand (Микрософт SQL серверийн хувьд) эсвэл OracleCommand (Ораклийн өгөгдлийн сангийн серверийн хувьд) обьектын хувьд дараах кодыг засварлан SQL injection халдлагаас сэргийлж болно.

using( SqlConnection con = (acquire connection) ) {
    con.Open();
    using( SqlCommand cmd = new SqlCommand("SELECT * FROM users WHERE name = '" + userName + "'", con) ) {
       using( SqlDataReader rdr = cmd.ExecuteReader() ){
           ...
       }
    }     
}

Дээрх кодыг ашиглахын оронд дараах кодыг ашиглах хэрэгтэй. Үүнд:

using( SqlConnection con = (acquire connection) ) {
    con.Open();
    using( SqlCommand cmd = new SqlCommand("SELECT * FROM users WHERE name = @userName", con) ) {
       cmd.Parameters.AddWithValue("@userName", userName); 
       using( SqlDataReader rdr = cmd.ExecuteReader() ){
           ...
       }
    }     
}

Java-гийн хувьд:

String selectStatement = "SELECT * FROM User WHERE userId = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
prepStmt.setString(1, userId);
ResultSet rs = prepStmt.executeQuery();

гэсэн бичлэгийг ашиглавал халдлагад өртөх аюул харицангуй багасна.

Өгөгдлийн сангийн талаас халдлагаас хэрхэн хамгаалах вэ?
Аюулгүй байдлын эрх
Энэ бол өгөгдлийн сан дээр аюулгүй байдлын эрхийг тохируулж өгөх тохиолдолдхийсэн байх ёстой хамгийн наад захын зүйл юм. Хэдхэн програмын хувьд тухайнпрограм хүснэгт болон өгөгдлийн санг устгах эрх олгох шаардлагатай байдаг бөгөөд шаардлагагүй програмд энэ эрхийг хасч өгөх хэрэгтэй.

Үүнийг ашиглан SQL injection халдлагын асуудлыг шийдэхгүй боловч бодит аюулыг багасгах боломжтой.

Stored procedures
Ихэнх өгөгдлийн сангууд stored procedure-ийг дэмждэг. SQL-ийг програмын хэсэгт динамикаар үүсгэж ашиглахаасаа илүүтэйгээр stored procedure ашиглах нь аюул багатай. Жишээлбэл, оруулж байгаа өгөгдлийг параметрчилж өгөх, төрлийг хатуу мөрддөг (шалгадаг) болгосноор хэрэглэгчийн оруулах өгөгдлийг маш сайн шүүхээс(filter хийхээс) гадна програм stored procedure руу хандах эрх байхаас биш үндсэн өгөгдлийн сан руу хандах эрх байдаггүй. Үүний тусламжтайгаар програм stored procedure-д тусгаснаас илүү үйлдэл хийх боломжийг хаана.

Гэвч stored procedure ашиглах нь code injection асуудлыг шийдвэрлэхгүй. Учир нь хэрэв хэрэглэгчийн гараас оруулах өгөгдөл параметрчилагдаагүй, эсвэл filter хийгдээгүй тохиолдолд дараах байдлаар халдаж болно. Үүнд,

GET_PASSWORD(userName) ба GET_USER(userName, password) гэсэн 2 stored procedure өгөгдсөн гэж үзвэлтохиолдолд халдагч GET_USER рүү доорх аргыг ашиглан халдлага хийх боломжтой. Үүнд: password-ийг зөвхөн тусгай тэмдэгтээс хамгаалаагүй

GET_USER('admin', '' || GET_PASSWORD('admin') || '').

Multi-statement халдлагаас сэргийлэх
MySQL С клиент сангийн стандарт query аргачлал нэг оролтонд нэгээс олон query биелэгдэхээс сэргийлдэг болохыг дахин сануулъя. Гэвч ердийн хэрэглэгчийн оруулсан тусгай тэмдэгт (данхаалт гэх мэт) агуулсан өгөгдлөөс болж алдаатай бичигдсэн SQL синтаксын дагуу програмд дараах алдаа, зарим халдлага гарах боломжтой. Жишээлбэл, вэб сайт дээр байгаа username-тэй холбоотой мэдээллийг гаргадаг жагсаалт байна гэж үзье. Биелэгдэх query нь:

SELECT * from items where username='$username';

Энэ тохиолдолд тусгай янзалсан username-ийг ашиглан бүх хэрэглэгчийн талаарх бүх мэдээллийг гаргая гэвэл:

' or username is not null or username='

Дээрх кодыг ашиглан дараах SQL statement үүснэ:

SELECT * from items where username='' or username is not null or username='';

Дээрх хаалтыг хассанаар SQL injection халдлагын аюулаас бүрэн сэргийлж чадахгүй. Таны query дараах хэлбэртэй байна гэж үзвэл:

SELECT * from items where userid=$userid;

$userid-ийг тоон утга гэж тооцсон боловч шалгаагүй явсан гэж үзвэл энэ тусгай бэлдсэн userid дахин бүх хэрэглэгчийн талаарх мэдээллийг гаргана:

33 or userid is not null or userid=44

Таны харж байгаагаар нэг ч хаалт байхгүй байсан ч дараах query үүснэ:

SELECT * from items where userid=33 or userid is not null or userid=44;

Хамгийн шилдэг хамгаалалт бол мэдэгдэж байгаа аюултай өгөгдөл оруулалтыг хар жагсаалтанд оруулжалтын “цэвэр” жагсаалтыг гаргах хэрэгтэй. Жишээлбэл хэрэв та өмнөх халдлагаас хамгаалахыг хүсвэл userid хувьсагчийн тоо агуулсан эсэхийг шалгах хэрэгтэй: сэргийлэхээсээ илүү зөвхөн мэдэгдэж байгаа аюулгүй өгөгдөл оруул

if(!ctype_digit($userid)){ die("Invalid characters in userid."); }

Литералын хүчингүй болгох
SQL injection аюулаас өгөгдлийн сангийн engine ‘литералыг хүчингүй болгох’ хэрэгсэлтэй байх тохиолдолд бүрэн сэргийлж болно. Литералыг хүчингүй болгоно гэдэг нь текст болон тоон литерал SQL statement-ийн нэг хэсэг болохыг зөвшөөрөхгүй байх өгөгдлийн сангийн engine-ийн төлөв байх ба зөвхөн placeholder зөвшөөрөгдөнө. Иймээс дараах төрлийн statement:

SELECT * FROM USER WHERE NAME='Smith'

SELECT * FROM ITEMS WHERE USERID=2

өмнө дурдсан төлөвт зөвшөөрөгдөхгүй (өгөгдлийн сангийн engine алдааны мэдээлэл буцаана). Query дараах хэлбэртэй бичигдэнэ:

SELECT * FROM USER WHERE NAME=?

SELECT * FROM ITEMS WHERE USERID=?

Литералыг хүчингүй болгосноор placeholder-ийг ашиглахаас өөр аргагүй болно. (Placeholder гэдэг бол математикт олонлогийн ямар ч элементийн нэрээр солигдож болохыг илэрхийлэх дүрс юм)

Үүнээс болж өмнө дурдсан SQL injection халдлагын төрлүүд энэ төлөвт боломжгүй болно.

Одоогийн байдлаар зөвхөн нэг өгөгдлийн сангийн engine (H2 өгөгдлийн сангийн engine) литерал хүчингүй болгохыг дэмжиж байгаа бөгөөд технологи ньпатентлагдаагүй байна. Програмын SQL injection халдлагаас сэргийлэхийн тулд одоогоор бэлэн болоогүй байгаа энэ хэрэгслийг ашиглах шаардлагагүй болов уу.Үүний оронд уг хэрэгслийг дэмждэг өгөгдлийн санг уг төлөвт оруулан нэгжийн тест хийхэд хангалттай.

SQL Injection халдлагаас хамгаалах хамгийн сайн арга
Гараас оруулах өгөгдлийг шалгах нь SQL Injection халдлагаас хамгаалах хамгийн чухал хэсэг юм. Сайн загварын тусламжтайгаар бүх шинэ програмдаа гараас оруулах өгөгдөл шалгах ажлыг хийж өгнө. Сайн загвар гэдэг нь query дэх өгөгдөлд хялбар, “аюулгүй” зам тодорхойлохыг хэлдэг. Мөн өмнөх кодоо шалгах хэрэгтэй. Мэдээж серверээ тогтмол шалгаж байвал зохино.
  • Өгөгдлийн сантай ажиллахдаа stored procedure ашиглах
  • Параметрчилэгдсэн API-гаар stored procedure дуудах
  • Оруулах бүх өгөгдлийг тусгай арга замаар шалгах
  • “Бага эрх” зарчмыг баримтлах буюу query төрөл тус бүрт эрх тодорхойлох
  • Оруулах өгөгдлийг шалгах
  • “Зөвхөн сайныг зөвшөөрөх” шүүлтүүр ашиглах буюу оруулах өгөгдөл нь тообайгаа тохиолдолд хадгалахын тулд тоон хувьсагчийг кодондоо ашиглах
  • Програмд алдаа гаргаж болохоор өгөгдлийг орхиж, өөрчлөхийн оронд тухайнөгөгдлөөс татгалзах
  • “Програм зохиогч бүрийн мэдэх аюултай зүйлсээс хамгаалах” шүүлтүүрийг хэрэгжүүлэх. Жишээлбэл: “select”, “insert”, “update”, “shutdown”, “delete”, “drop”, “--”, “” эдгээрийг програмаас хүлээн авахгүй байх
  • Серверээ шалгах
1. Өгөгдлийн санг үргэлж бага эрхтэй хэрэглэгчээр ажиллуулах
2. Ашиглагдахгүй байгаа stored procedure болон функцыг устгах,администратор хандах эрхтэй байх
3. Системийн обьектуудын “public” хандалтыг өөрчлөх, хасах
4. Бүх хэрэглэгчийн эрхийн хувьд нууц үгийн чадлыг шалгах
5. Өмнө нэвтэрч холбогдсон серверүүдийг хасах
6. Ашиглагдаагүй сүлжээний протоколуудыг идэвхгүй болгох
7. Серверт firewall хийж, зөвхөн итгэлтэй үйлчлүүлэгчид ханддаг болох

l SQL Injection илрүүлэх, сануулах
            SQL injection оролдлогуудад дараах хариу үйлдэл хийж болно.
l Хадлага хийх оролдлогуудыг тэмдэглэх
l Цахим шуудангаар аюулын мэдээ хүлээн авах
l Халдлага үйлдсэн IP хаягийг блоклох
l Хариу сануулсан алдааны мессеж буцаах. Жишээ нь, “Анхааруулга: Програм буруугаар ашиглах гэсэн оролдлогыг илрүүллээ. Халдлага хийх болзошгүйг тогтоолоо. Хуулийн дагуу арга хэмжээ авах болно” гэх мэт.

Эдгээр хамгаалалтуудыг хийсэн тохиолдолд таны програм болон вэб SQL injectionхалдлагаас тодорхой түвшинд хамгаалагдах болно.

Энэ бол вэб сайт, програмуудад байдаг хамгийн түгээмэл сул тал. Энэ нь өгөгдлийн сан болон вэб серверийн асуудал биш, харин вэб болон програм хөгжүүлэхтэй холбоотой гардаг алдаа юм. Харамсалтай нь ихэнх програм зохиогчид үүнд анхаарал хандуулдаггүй. Дээр нь интернэтэд тавьсан вэб болон програмын шийдлүүд нь дээрх төрлийн халдлагаас хангалттай сэргийлж чаддаггүй. Дэлхийн нийт вэб болон програмын 60 хувь нь SQL injection халдлагаас сэргийлэгдээгүй байдаг гэсэн судалгааны дүн бий.

No comments:

Post a Comment