در حال ویرایش تزریق SQL

از Secure Coding

هشدار: شما وارد نشده‌اید. نشانی آی‌پی شما برای عموم قابل مشاهده خواهد بود اگر هر تغییری ایجاد کنید. اگر وارد شوید یا یک حساب کاربری بسازید، ویرایش‌هایتان به نام کاربری‌تان نسبت داده خواهد شد، همراه با مزایای دیگر.

این ویرایش را می‌توان خنثی کرد. لطفاً تفاوت زیر را بررسی کنید تا تأیید کنید که این چیزی است که می‌خواهید انجام دهید، سپس تغییرات زیر را ذخیره کنید تا خنثی‌سازی ویرایش را به پایان ببرید.

نسخهٔ فعلی متن شما
سطر ۱: سطر ۱:
 
در این نوع از آسیب پذیری های مهاجم می تواند payload خود را در  قسمت های مختلف برنامه تزریق کند.
 
در این نوع از آسیب پذیری های مهاجم می تواند payload خود را در  قسمت های مختلف برنامه تزریق کند.
 
از انواع آسیب پذیری های injection می توان به تزریق sql و یا تزریق command اشاره کرد.
 
از انواع آسیب پذیری های injection می توان به تزریق sql و یا تزریق command اشاره کرد.
 +
==1-1- تزریق sql==
 
زبان پرس و جو sql راهی برای مدیریت داده های ذخیره شده در پایگاه داده های رابطه ای است.
 
زبان پرس و جو sql راهی برای مدیریت داده های ذخیره شده در پایگاه داده های رابطه ای است.
 
به این معنا که می توانیم در هر بستری از جمله وب داده هارا ذخیره، ویرایش و یا تغییر دهیم.
 
به این معنا که می توانیم در هر بستری از جمله وب داده هارا ذخیره، ویرایش و یا تغییر دهیم.
 
گاهی اوقات احراز هویت کاربران، نمایش محتوای وبسایت و یا عکس های کاربران در پایگاه داده ذخیره می شود و توسط sql در مسیر های مختلف به کار برده می شود.
 
گاهی اوقات احراز هویت کاربران، نمایش محتوای وبسایت و یا عکس های کاربران در پایگاه داده ذخیره می شود و توسط sql در مسیر های مختلف به کار برده می شود.
 
حال فرض کنید attacker در پرس و جوی sql، دستکاری و یا Inject کند.
 
حال فرض کنید attacker در پرس و جوی sql، دستکاری و یا Inject کند.
در اینجا می گوییم sql injection رخ داده است.
+
در اینجا می گوییم sql injection رخ داده است.[[پرونده:Sql.png]]
 
 
[[پرونده:Sql.png]]
 
 
 
 
برای مثال در تصویر بالا attacker به جای مقدار دسته بندی محصولات(Gift)این payload را وارد می کند.
 
برای مثال در تصویر بالا attacker به جای مقدار دسته بندی محصولات(Gift)این payload را وارد می کند.
 
+
<syntaxhighlight lang="Python" line start="55">
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
 
' UNION SELECT username, password FROM users--
 
' UNION SELECT username, password FROM users--
</pre>
+
</syntaxhighlight>
</div>
 
 
 
در نتیحه علاوه بر اطلاعات محصول دسته بندی Gifts تمام username و password های جدول users را هم در خروجی نمایش می دهد.
 
حملات sql injection نوع های مختلفی دارد. چون به ازای هر query نوشته شده ممکن است حمله جدیدی رخ دهد.
 
با این حال به صورت کلی 3 دسته بندی زیر را می توان نام برد.
 
== بر مبنای خطا یا Error Based ==
 
 
 
مثال معروفی برای این روش وجود دارد.
 
صفحه ای از وبسایتی برای نمایش جزییات خبر از id آن خبر استفاده می کند؛
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
https://web.tld/news?id=100
 
</pre>
 
</div>
 
 
 
با توجه به مقدار id، داده های دیگری از پایگاه داده خوانده و نمایش داده می شود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
SELECT title,context FROM tbl_news WHERE id ='$id'
 
</pre>
 
</div>
 
 
 
=== بر مبنای اجتماع یا Union Based ===
 
 
 
در این روش علاوه بر اطلاعات دریافت شده توسط پرس و جوی توسعه دهنده با دستور Union اطلاعات دیگری از پایگاه داده استخراج می شود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
http://fakesite.com/report.php?id=23
 
</pre>
 
</div>
 
 
 
برای مثال فرض کنید صفحه ای وجود دارد که براساس id موجودیت های سیستم گزارشی از وضیعت آن موجودیت چاپ می کند.
 
 
 
اگر مقدار id را به همراه دستور زیر وارد کنیم، می توانیم تعداد ستون های جدولی که با توجه به id، اطلاعات از آن خوانده می شود را بدست آوریم.(ممکن است مقدار 5 به ازای هر جدول متفاوت باشد)
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
http://fakesite.com/report.php?id=23 order by 5--+
 
</pre>
 
</div>
 
 
 
بعد از دانستن تعداد ستون های جدول با دستور Union، ستونی که از آن اطلاعات خوانده می شود و در صفحه چاپ می شود را بدست می آوریم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
http://fakesite.com/report.php?id=-23 union select 'hello1','hello2','hello3','hello4','hello5'--+
 
</pre>
 
</div>
 
 
 
هر کدام از hello ها که در صفحه چاپ شود به این معنی است که می توانیم به جای آن hello پرس و جوی خود را بنویسیم.
 
 
 
برای مثال نام دیتابیس را با database() بدست می آوریم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
http://fakesite.com/report.php?id=-23 union select 1,2,database(),4,5--+
 
</pre>
 
</div>
 
 
 
== بر مبنای تاریکی Blind Based ==
 
 
 
شاید اسم این روش عجیب باشد ولی با توجه به الگو "بر مبنای" تاریکی کلمه مناسب و هکر پسندانه تری است!
 
 
 
در این روش با استفاده از and و دستورات زمانی مانند sleep در بارگذاری اطلاعات از پایگاه داده تاخییر و یا تغییر ایجاد می کنیم.
 
 
 
برای مثال می دانیم 2=1 نیست پس آن را در مقدار id قرار می دهیم تا شرط False شود و بدین ترتیب اطلاعات به درستی از پایگاه داده استخراج نشود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
http://www.shop.local/item.php?id=34 and 1=2
 
</pre>
 
</div>
 
 
 
در مرحله بعد شرط True می کنیم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
http://www.shop.local/item.php?id=34 and 1=1
 
</pre>
 
</div>
 
 
 
حال با توجه به روش های بالا به سراغ روش های جلوگیری از این حمله می رویم.
 
 
 
== روش های جلوگیری ==
 
 
 
 
 
=== روش های جلوگیری در زبان PHP ===
 
 
 
 
 
مثال 1)
 
 
 
تکه کد زیر دو مقدار username و password را با درخواست از نوع POST دریافت می کند و سپس در پرس و جو با رکورد های موجود در پایگاه داده مقایسه می کند.
 
 
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
<?php
 
$userName=$_POST['userName'];
 
$password=$_POST['password'];
 
$sqlQuery="SELECT * FROM users WHERE user_name='".$username."' AND user_password='".$password"';"
 
?>
 
</pre>
 
</div>
 
 
 
اگر password، مقدار ‘ or ‘a’=’a ‘or’ داشته باشد؛ شرط WHERE به ازای کاربر با کلمه عبور نامرتبط درست خواهد بود.
 
 
 
مثال 2)
 
 
 
اطلاعات کاربر را با توجه به id او توسط پرس و جو sql دریافت می کنیم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
$expected_data = 1;
 
$query = "SELECT * FROM users where id=$expected_data"
 
</pre>
 
</div>
 
 
 
اگر id، مقدار1; DROP TABLE users; داشته باشد علاوه بر نمایش اطلاعات کاربر جدول کاربران هم پاک می شود!
 
و پرس و جو در نهایت این شکلی می شود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
SELECT * FROM users where id=1; DROP TABLE users;
 
</pre>
 
</div>
 
 
 
برای جلوگیری دو روش Block Malicious و Prepared Statements را بررسی می کنیم.
 
 
 
راه حل 1)
 
 
 
یکی از بهترین روش ها برای جلوگیری از آسیب پذیری، تشخص کاراکتر های غیر مجاز درون رشته و تبدیل آن ها به کاراکتر های مجاز است.
 
 
 
با این توضیح ابتدا تابعی تعریف می کنیم تا ورودی کاربر را بگیرد و کاراکتر های برهم زننده رشته پرس و جو را به کاراکتر های مجاز در رشته در قالب آرایه تبدیل کند.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
<?php
 
function BlockSQLInjection($str)
 
{
 
return str_replace(array("'",""","'",'"'),array("'","""'",""",$str));
 
}
 
?>
 
</pre>
 
</div>
 
 
 
حال درخواست های ارسال شده را به تابع پاس دهیم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
<?php
 
$userName=BlockSQLInjection($_POST['userName']);
 
$password=BlockSQLInjection($_POST['password']);
 
?>
 
</pre>
 
</div>
 
 
 
راه حل 2)
 
 
 
یکی از روش های بسیار خوب استفاده از Prepared Statements ها در زبان های مختلف است.
 
 
 
برای مثال در زبان php می توان از PDO برای کار با قسمت های مختلف پایگاه داده در قالب api استفاده کرد.
 
 
 
نمونه کد برای کار pdo به شکل زیر است.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
<?php
 
$stmt=$conn->prepare(INSERT INTO MyGuests(firstname,lastname,email)VALUES(?,?,?)");
 
$stmt->bind_param("sss",$firstname,$lastname,$email);
 
//set paramters and execute
 
$firstname="John";
 
$lastname="Doe";
 
 
$stmt->execute();
 
$firstname="Mary";
 
$lastname="Moe";
 
 
$stmt->execute();
 
</pre>
 
</div>
 
 
 
=== روش های جلوگیری در ASP.NET ===
 
 
 
مثال 1)
 
کد زیر را در نظر بگیرید.
 
 
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
protected void Button1_Click(object sender, EventArgs e)
 
{
 
  string connect = "MyConnString"
 
    string query = "Select Count(*) From Users Where Username = '" + UserName.Text + "' And Password = '" + Password.Text + "'"
 
      int result = 0;
 
        using (var conn = new SqlConnection(connect))
 
          {
 
              using (var cmd = new SqlCommand(query, conn))
 
                  {
 
      conn.Open();
 
      result = (int)cmd.ExecuteScalar();
 
      }
 
      }
 
  if (result > 0)
 
  {
 
    Response.Redirect("LoggedIn.aspx");
 
  }
 
  else
 
  {
 
    Literal1.Text = "Invalid credentials"
 
}
 
</pre>
 
</div>
 
 
 
همانطور که در رشته پرس و جو مشاهده می کنید، هیچ کنترلی بر روی درخواست ارسال شده نمی شود.
 
 
 
 
 
اگر مقدار ' or '1' = '1 ارسال شود شرط WHERE درست می شود.
 
 
 
مثال 2)
 
 
 
در کد زیر هم درخواست ارسال شده بدون هیچ کنترلی در رشته پرس و جو استفاده شده است.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
string connect = "MyConnString"
 
string query = "Select * From Products Where ProductID = " + Request["ID"];
 
 
 
using (var conn = new SqlConnection(connect))
 
{
 
  using (var cmd = new SqlCommand(query, conn))
 
  {
 
    conn.Open();
 
    //Process results
 
  }
 
}
 
</pre>
 
</div>
 
 
 
بنابر این اگر مقدار ;Drop Table Admin-- ارسال شود علاوه بر پرس و جوی قبل پرس و جوی ارسال شده اجرا می شود.
 
برای جلوگیری دو روش زیر را بررسی می کنم.
 
 
 
راه حل 1)
 
 
 
با توجه به نوع ستون جدول در پایگاه داده، ورودی کاربر را تبدیل به آن نوع می کنم.
 
 
 
برای مثال id محصول از نوع عدد و int است پس درخواست ارسالی را تبدیل به عدد می کنم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
protected void Page_Load(object sender, EventArgs e)
 
{
 
  var connect = ConfigurationManager.ConnectionStrings["NorthWind"].ToString();
 
  var query = "Select * From Products Where ProductID = @ProductID"
 
  using (var conn = new SqlConnection(connect))
 
  {
 
    using (var cmd = new SqlCommand(query, conn))
 
    {
 
      cmd.Parameters.Add("@ProductID", SqlDbType.Int);
 
      cmd.Parameters["@ProductID"].Value = Convert.ToInt32(Request["ProductID"]);
 
      conn.Open();
 
      //Process results
 
    }
 
  }
 
}
 
</pre>
 
</div>
 
 
 
این روش را می توان به صورت زیر هم استفاده کرد.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
protected void Page_Load(object sender, EventArgs e)
 
{
 
  var connect = ConfigurationManager.ConnectionStrings["NorthWind"].ToString();
 
  var query = "Select * From Products Where ProductID = @ProductID"
 
  using (var conn = new SqlConnection(connect))
 
  {
 
    using (var cmd = new SqlCommand(query, conn))
 
    {
 
      cmd.Parameters.AddWithValue("@ProductID", Convert.ToInt32(Request["ProductID"]);
 
 
 
      conn.Open();
 
      //Process results
 
    }
 
  }
 
}
 
</pre>
 
</div>
 
 
 
راه حل 2)
 
 
 
روش دیگر استفاده از Store Procedure است.
 
 
 
مزیت این روش نسبت به روش قبل این است که query حتما با توجه به نوع ستون اجرا می شود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
var connect = ConfigurationManager.ConnectionStrings["NorthWind"].ToString();
 
var query = "GetProductByID"
 
 
 
using (var conn = new SqlConnection(connect))
 
{
 
  using (var cmd = new SqlCommand(query, conn))
 
  {
 
    cmd.CommandType = CommandType.StoredProcedure;
 
    cmd.Parameters.Add("@ProductID", SqlDbType.Int).Value = Convert.ToInt32(Request["ProductID"]);
 
    conn.Open();
 
    //Process results
 
  }
 
}
 
</pre>
 
</div>
 
 
 
=== روش های جلوگیری در زبان Java ===
 
 
 
مثال 1)
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
protected void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
 
        response.setContentType('text/html;charset=UTF-8');
 
        PrintWriter out = response.getWriter();
 
        try {
 
            String user = request.getParameter('user');
 
            Connection conn = null;
 
            String url = 'jdbc:mysql://192.168.2.128:3306/';
 
            String dbName = 'anvayaV2';
 
            String driver = 'com.mysql.jdbc.Driver';
 
            String userName = 'root';
 
            String password = '';
 
            try {
 
                Class.forName(driver).newInstance();
 
                conn = DriverManager.getConnection(url + dbName, userName, password);
 
                Statement st = conn.createStatement();
 
                String query = 'SELECT * FROM  User where userId='' + user + ''';
 
                out.println('Query : ' + query);
 
                System.out.printf(query);
 
                ResultSet res = st.executeQuery(query);
 
 
                out.println('Results');
 
                while (res.next()) {
 
                    String s = res.getString('username');
 
                    out.println('\t\t' + s);
 
                }
 
                conn.close();
 
            } catch (Exception e) {
 
                e.printStackTrace();
 
            }
 
        } finally {
 
            out.close();
 
        }
 
</pre>
 
</div>
 
 
 
با توجه به کد بالا اگر در userId مقدار admin’ or ‘1’=’1 وارد کنیم رشته پرس و جو به شکل زیر می شود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
SELECT * FROM User where userId ='admin' or '1'='1'
 
</pre>
 
</div>
 
 
 
راه حل 1)
 
 
 
با استفاده Prepared Statement نوع درخواست ارسال شده را تعیین می کنیم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
PreparedStatement  preparedStatement=conn.prepareStatement('SELECT * FROM  usercheck where username=?') ;
 
preparedStatement.setString(1, user);
 
</pre>
 
</div>
 
 
 
راه حل 2)
 
 
 
با استفاده از NamedQuery در جاوا می توانیم درخواست را به صورت Prepared Statement دریافت و پرس و جو مرتبط ایجاد کنیم.
 
 
 
در حالت عادی query به شکل زیر ساخته می شود.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
String q='SELECT r FROM  User r where r.userId=''+user+''';
 
Query query=em.createQuery(q);
 
List users=query.getResultList();
 
</pre>
 
</div>
 
 
 
اما با NamedQury می توانیم پرس و جو را با توجه به مدل های پایگاه داده خودکار تولید کنیم.
 
 
 
<div lang="en" dir="ltr" class="mw-content-ltr">
 
<pre>
 
Query query=em.createNamedQuery('User.findByUserId');
 
query.setParameter('userId', user);
 
List users=query.getResultList();
 
</pre>
 
</div>
 
 
 
=== توابع php ===
 
 
 
از توابعی که می تواند در بروز این آسیب پذیری موثر باشد می توان به
 
 
 
* mysql_query
 
* mysql_db_query
 
* mysql_unbuffered_query
 
* pg_query
 
* pg_send_query
 
* mssql_query
 
* mysqli_query and mysqli::query
 
* mysqli_real_query and mysqli::real_query
 
* mysqli_multi_query and mysqli::multi_query
 
* mysqli_send_query and mysqli::send_query
 
* PDO::query
 
* PDO::exec
 
* PDO::prepar
 
  
اشاره نمود.
 
  
  
 
[[category:تزریق(Injection)]]
 
[[category:تزریق(Injection)]]

لطفاً توجه داشته‌باشید که همهٔ مشارکت‌ها در Secure Coding ممکن است توسط دیگر مشارکت‌کنندگان تغییر یابند، ویرایش یا حذف شوند. اگر نمی‌خواهید نوشته‌هایتان بی‌رحمانه ویرایش شوند؛ بنابراین، آنها را اینجا ارائه نکنید.
شما همچنین به ما تعهد می‌کنید که خودتان این را نوشته‌اید یا آن را از یک منبع با مالکیت عمومی یا مشابه آزاد آن برداشته‌اید (Secure Coding:حق تکثیر را برای جزئیات بیشتر ببینید). کارهای دارای حق تکثیر را بدون اجازه ارائه نکنید!

لغو راهنمای ویرایش‌کردن (در پنجرهٔ تازه باز می‌شود)