টি এল; ডিআর
mysql_real_escape_string()
যদি কোনও সুরক্ষা সরবরাহ না করে (এবং তদ্ব্যতীত আপনার ডেটাটি ছত্রভঙ্গ করতে পারে) যদি:
মাইএসকিউএল এর NO_BACKSLASH_ESCAPES
এসকিউএল মোড সক্ষম করা হয়েছে (যা এটি হতে পারে, যতক্ষণ না আপনি স্পষ্টভাবে প্রতিবার সংযোগ করার সময় অন্য এসকিউএল মোড নির্বাচন করেন ); এবং
আপনার এসকিউএল স্ট্রিং লিটারেলগুলি ডাবল-উদ্ধৃতি "
অক্ষর ব্যবহার করে উদ্ধৃত করা হয়েছে ।
এটি বাগ # 72458 হিসাবে দায়ের করা হয়েছিল এবং এটি মাইএসকিউএল v5.7.6 এ ঠিক করা হয়েছে ( নীচে " দ্য সেভিং গ্রেস " শীর্ষক বিভাগটি দেখুন)।
এটি আর একটি, (সম্ভবত কম?) অস্পষ্ট এজ এজ CASE !!!
@ আর্কম্যাক্সেলের দুর্দান্ত উত্তরে শ্রদ্ধা জানাতে (সত্যই, এটি চাটুকারিতা বলে মনে করা হয় এবং চুরির কথা নয়!), আমি তার ফর্ম্যাটটি গ্রহণ করব:
আক্রমণ
একটি বিক্ষোভ দিয়ে শুরু হচ্ছে ...
mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- ');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');
এটি test
টেবিল থেকে সমস্ত রেকর্ড ফিরিয়ে দেবে । একটি বিচ্ছেদ:
একটি এসকিউএল মোড নির্বাচন করা হচ্ছে
mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');
স্ট্রিং লিটারালসের অধীনে নথিভুক্ত হিসাবে :
স্ট্রিংয়ের মধ্যে উদ্ধৃতি অক্ষর অন্তর্ভুক্ত করার বিভিন্ন উপায় রয়েছে:
একটি " '
" সাথে উদ্ধৃত একটি স্ট্রিংয়ের ভিতরে " '
" হিসাবে লেখা যেতে পারে ''
।
একটি " "
" সাথে উদ্ধৃত একটি স্ট্রিংয়ের ভিতরে " "
" হিসাবে লেখা যেতে পারে ""
।
একটি অব্যাহতি অক্ষর দ্বারা উদ্ধৃতি অক্ষর আগে (" \
")।
" '
" দিয়ে উদ্ধৃত একটি স্ট্রিংয়ের ভিতরে একটি " "
" বিশেষ চিকিত্সা প্রয়োজন এবং দ্বিগুণ বা পালাতে হবে না। একইভাবে, " "
" দিয়ে উদ্ধৃত একটি স্ট্রিংয়ের অভ্যন্তরে '
কোনও বিশেষ চিকিত্সার প্রয়োজন নেই।
যদি সার্ভারের এসকিউএল মোড অন্তর্ভুক্ত থাকে NO_BACKSLASH_ESCAPES
, তবে এই বিকল্পগুলির মধ্যে তৃতীয়টি — যা সাধারণ পদ্ধতি অবলম্বন করে mysql_real_escape_string()
—এটি উপলভ্য নয়: পরিবর্তে প্রথম দুটি বিকল্পের মধ্যে একটি ব্যবহার করা আবশ্যক। নোট করুন যে চতুর্থ বুলেটটির প্রভাবটি হ'ল যে কারও ডেটা মংগিং এড়ানোর জন্য আক্ষরিক উদ্ধৃতি হিসাবে ব্যবহৃত হবে এমন চরিত্রটি অবশ্যই জানা উচিত।
পে লোড
" OR 1=1 --
পে-লোড "
চরিত্রের সাথে এই ইনজেকশনটি বেশ আক্ষরিক অর্থে শুরু করে । কোনও নির্দিষ্ট এনকোডিং নেই। কোনও বিশেষ অক্ষর নেই। কোনও অদ্ভুত বাইট নেই।
mysql_real_escape_string ()
$var = mysql_real_escape_string('" OR 1=1 -- ');
ভাগ্যক্রমে, mysql_real_escape_string()
এসকিউএল মোডটি পরীক্ষা করে এবং সে অনুযায়ী তার আচরণটি সামঞ্জস্য করে। দেখুন libmysql.c
:
ulong STDCALL
mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
ulong length)
{
if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
return escape_string_for_mysql(mysql->charset, to, 0, from, length);
}
সুতরাং এসকিউএল মোড ব্যবহার escape_quotes_for_mysql()
করা হয়, একটি ভিন্ন অন্তর্নিহিত ফাংশন,, ডাকে NO_BACKSLASH_ESCAPES
। উপরে উল্লিখিত হিসাবে, এই জাতীয় ক্রিয়াকলাপটি এটি জানতে হবে যে অন্য বর্ণনামূলক অক্ষরকে আক্ষরিকভাবে পুনরাবৃত্তি না করেই পুনরুক্ত করতে কোন অক্ষরটি আক্ষরিক উদ্ধৃতিতে ব্যবহৃত হবে।
তবে এই ফাংশনটি নির্বিচারে ধরে নিয়েছে যে একক-উদ্ধৃতি '
অক্ষর ব্যবহার করে স্ট্রিংটি উদ্ধৃত করা হবে । দেখুন charset.c
:
/*
Escape apostrophes by doubling them up
// [ deletia 839-845 ]
DESCRIPTION
This escapes the contents of a string by doubling up any apostrophes that
it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
effect on the server.
// [ deletia 852-858 ]
*/
size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
char *to, size_t to_length,
const char *from, size_t length)
{
// [ deletia 865-892 ]
if (*from == '\'')
{
if (to + 2 > to_end)
{
overflow= TRUE;
break;
}
*to++= '\'';
*to++= '\'';
}
সুতরাং, এটি আক্ষরিক উদ্ধৃতিতে ব্যবহৃত প্রকৃত চরিত্র নির্বিশেষে ডাবল-কোট "
অক্ষরগুলি (এবং সমস্ত একক-উদ্ধৃতি '
অক্ষরের দ্বিগুণ ) ছেড়ে দেয় ! আমাদের যদি $var
দেহাবশেষ যুক্তি হল যে প্রদান করা হয় ঠিক একই mysql_real_escape_string()
-এটা যেন কোন পলায়নের জায়গা নিয়েছে এ সব ।
প্রশ্ন
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');
আনুষ্ঠানিকতার কিছু, রেন্ডার্ড ক্যোয়ারীটি হ'ল:
SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1
আমার শিক্ষিত বন্ধু যেমনটি বলেছে: অভিনন্দন, আপনি কেবলমাত্র সফলভাবে একটি প্রোগ্রাম ব্যবহার করে আক্রমণ mysql_real_escape_string()
...
খারাপ জন
mysql_set_charset()
সাহায্য করতে পারে না, কারণ চরিত্রের সেটগুলির সাথে এর কোনও যোগসূত্র নেই; না পারে mysqli::real_escape_string()
, যেহেতু এটি এই একই ফাংশনটির চারপাশে কেবল একটি আলাদা র্যাপার।
সমস্যাটি যদি ইতিমধ্যে সুস্পষ্ট না হয় তবে তা হ'ল আক্ষরিক অর্থে কোন চরিত্রটি উদ্ধৃত হবে mysql_real_escape_string()
তা কলটি জানতে পারে না , কারণ এটি পরবর্তী সময়ে সিদ্ধান্ত নিতে বিকাশকারীকে রেখে গেছে left সুতরাং, NO_BACKSLASH_ESCAPES
মোডে, আক্ষরিকভাবে কোনও উপায় নেই যে এই ফাংশনটি নিরাপদে উদ্ধৃতি দিয়ে ব্যবহারের জন্য প্রতিটি ইনপুট নিরাপদে রক্ষা করতে পারে (কমপক্ষে, দ্বিগুণ হওয়া অক্ষরগুলির দ্বিগুণ না করে এবং এইভাবে আপনার ডেটা মংগ করা ছাড়াই নয়)।
কুৎসিত
এটা খুব খারাপ হচ্ছে. NO_BACKSLASH_ESCAPES
স্ট্যান্ডার্ড এসকিউএল এর সাথে সামঞ্জস্যের জন্য প্রয়োজনীয়তার প্রয়োজনীয়তার কারণে বন্যের মধ্যে এটি অসাধারণ কিছু নাও হতে পারে (যেমন এসকিউএল -২২ স্পেসিফিকেশনের অংশ 5.3 দেখুন , যথা <quote symbol> ::= <quote><quote>
ব্যাকরণ উত্পাদন এবং ব্যাকস্ল্যাশকে দেওয়া কোনও বিশেষ অর্থের অভাব)। তদ্ব্যতীত, এর ব্যবহারটি স্পষ্টভাবে ওয়ার্কআউন্ড হিসাবে সুপারিশ করা হয়েছিল (দীর্ঘকাল থেকে স্থির) বাগ যা আইর্কমেক্সেলের পোস্ট বর্ণনা করে। কে জানে, কিছু ডিবিএ এমনকি ডিফল্টরূপে এটির মতো ভুল পালানোর পদ্ধতির ব্যবহারকে নিরুৎসাহিত করার জন্য কনফিগারও করতে পারে addslashes()
।
এছাড়াও, একটি নতুন সংযোগের এসকিউএল মোডটি সার্ভারের দ্বারা এটির কনফিগারেশন অনুসারে সেট করা হয়েছে (যা কোনও SUPER
ব্যবহারকারী যে কোনও সময় পরিবর্তন করতে পারে); সুতরাং, সার্ভারের আচরণ সম্পর্কে নিশ্চিত হওয়ার জন্য আপনাকে সংযুক্ত হওয়ার পরে সর্বদা স্পষ্টভাবে আপনার কাঙ্ক্ষিত মোডটি নির্দিষ্ট করতে হবে ।
সেভিং গ্রেস
এতক্ষণ আপনি যতক্ষণ না স্পষ্টভাবে এসকিউএল মোডটি অন্তর্ভুক্ত না করে NO_BACKSLASH_ESCAPES
বা একক-উদ্ধৃতি অক্ষর ব্যবহার করে মাইএসকিউএল স্ট্রিং লিটারেলগুলি উদ্ধৃত করেন, এই বাগটি তার কুরুচিপূর্ণ মাথাটি পিছন করতে পারে না: যথাক্রমে escape_quotes_for_mysql()
ব্যবহার করা হবে না, বা কোট অক্ষরের পুনরাবৃত্তি প্রয়োজন হবে এমন ধারণা সঠিক হতে
এই কারণে, আমি প্রস্তাব দিচ্ছি যে যে কেউ ব্যবহার করে NO_BACKSLASH_ESCAPES
তা ANSI_QUOTES
মোড সক্ষম করে, কারণ এটি একক-উদ্ধৃত স্ট্রিং লিটারালগুলির অভ্যাসগত ব্যবহারকে বাধ্য করবে। মনে রাখবেন যে ডাবল-কোটড লিটারেলগুলি ব্যবহৃত হওয়ার ঘটনায় এটি এসকিউএল ইঞ্জেকশনটিকে বাধা দেয় না — এটি কেবল ঘটনার সম্ভাবনা হ্রাস করে (কারণ স্বাভাবিক, দূষিত প্রশ্নগুলি ব্যর্থ হবে)।
পিডিওতে, এটির সমতুল্য কার্যকারিতা PDO::quote()
এবং এর প্রস্তুত বিবৃতি এমুলেটর উভয়ই mysql_handle_quoter()
- যা ঠিক এটি করে: এটি নিশ্চিত করে যে পালানো আক্ষরিকটি একক-কোটায় উদ্ধৃত হয়েছে, সুতরাং আপনি নিশ্চিত হতে পারেন যে পিডিও সর্বদা এই বাগ থেকে প্রতিরোধক থাকে।
মাইএসকিউএল ভি 5.7.6 হিসাবে, এই বাগটি ঠিক করা হয়েছে। পরিবর্তন লগ দেখুন :
কার্যকারিতা যুক্ত বা পরিবর্তিত
নিরাপদ উদাহরণ
আইক্রিমেক্সেল দ্বারা ব্যাখ্যা করা বাগের সাথে একসাথে নেওয়া, নিম্নলিখিত উদাহরণগুলি সম্পূর্ণ নিরাপদ (ধরে নিলেন যে কেউ হয় 4.1.20, 5.0.22, 5.1.11 এর পরে মাইএসকিউএল ব্যবহার করছে; বা এটি কোনও জিবিকে / বিগ 5 সংযোগ এনকোডিং ব্যবহার করছে না) :
mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');
... কারণ আমরা স্পষ্টভাবে একটি এসকিউএল মোড নির্বাচন করেছি যা এতে অন্তর্ভুক্ত নয় NO_BACKSLASH_ESCAPES
।
mysql_set_charset($charset);
$var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
... কারণ আমরা একক উদ্ধৃতি দিয়ে আমাদের স্ট্রিংকে আক্ষরিক উদ্ধৃতি দিচ্ছি।
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);
... কারণ পিডিও-র তৈরি বিবৃতিগুলি এই দুর্বলতা থেকে রক্ষা পেয়েছে (এবং আইক্রিমেক্সেলেরও এটি সরবরাহ করা হয়েছে যে আপনি PHP≥5.3.6 ব্যবহার করছেন এবং অক্ষর সেটটি ডিএসএন-তে সঠিকভাবে সেট করা হয়েছে; বা প্রস্তুত বিবৃতি ইমুলেশন অক্ষম করা হয়েছে) ।
$var = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");
... কারণ PDO এর quote()
কার্যকারিতা কেবল আক্ষরিক থেকে অব্যাহতি পায় না, তবে এটি উদ্ধৃত করে (একক-উদ্ধৃতি '
অক্ষরে); নোট করুন যে এক্ষেত্রে আর্কম্যাক্সেলের বাগ এড়াতে আপনার অবশ্যই পিএইচপি -5.3.6 ব্যবহার করা উচিত এবং ডিএসএন -তে অক্ষর সেটটি সঠিকভাবে সেট করা উচিত ।
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();
... কারণ মাইএসকিউএলআই প্রস্তুত বিবৃতি নিরাপদ।
মোড়ক উম্মচন
আপনি যদি:
- নেটিভ প্রস্তুত বিবৃতি ব্যবহার করুন
অথবা
- MySQL v5.7.6 বা তার পরে ব্যবহার করুন use
অথবা
... তাহলে আপনার পুরোপুরি নিরাপদ হওয়া উচিত (স্ট্রিংয়ের পরিধি বাইরে দূরে থাকা দুর্বলতা)।