সংক্ষিপ্ত উত্তর হ'ল না , পিডিও প্রস্তুত সমস্ত সম্ভাব্য এসকিউএল-ইনজেকশন আক্রমণ থেকে আপনাকে রক্ষা করবে না। কিছু অস্পষ্ট প্রান্ত-ক্ষেত্রে জন্য।
আমি PDO সম্পর্কে কথা বলার জন্য এই উত্তরটি রূপান্তর করছি ...
দীর্ঘ উত্তর এত সহজ নয়। এটি এখানে প্রদর্শিত একটি আক্রমণ ভিত্তিক ।
আক্রমণ
সুতরাং, আসুন আক্রমণ দেখিয়ে শুরু করা যাক ...
$pdo->query('SET NAMES gbk');
$var = "\xbf\x27 OR 1=1 /*";
$query = 'SELECT * FROM test WHERE name = ? LIMIT 1';
$stmt = $pdo->prepare($query);
$stmt->execute(array($var));
নির্দিষ্ট পরিস্থিতিতে, এটি 1 সারির বেশি ফিরে আসবে। আসুন এখানে কী চলছে তা ছড়িয়ে দিন:
একটি অক্ষর সেট নির্বাচন করা হচ্ছে
$pdo->query('SET NAMES gbk');
কাজ এই আক্রমণ, আমরা এনকোডিং প্রয়োজন যে সার্ভারের উভয় সঙ্কেতাক্ষরে লিখা সংযোগে আশা '
হওয়া ASCII অর্থাত হিসেবে 0x27
এবং কিছু চরিত্র যার চূড়ান্ত বাইট একটি ASCII হয় আছে \
অর্থাত 0x5c
। এটি সক্রিয় আউট হিসাবে, 5 ধরনের ডিফল্টরূপে মাইএসকিউএল 5.6 সমর্থিত এনকোডিং আছেন: big5
, cp932
, gb2312
, gbk
এবং sjis
। আমরা gbk
এখানে নির্বাচন করব ।
এখন, SET NAMES
এখানে ব্যবহার নোট করা খুব গুরুত্বপূর্ণ । এই অক্ষর সেট সেট করে সার্ভারে । এটি করার আরও একটি উপায় আছে তবে আমরা খুব শীঘ্রই সেখানে পৌঁছে যাব।
পে লোড
আমরা এই ইঞ্জেকশনের জন্য যে পেডলোডটি ব্যবহার করতে যাচ্ছি তা বাইট সিকোয়েন্স দিয়ে শুরু হয় 0xbf27
। ইন gbk
, এটি একটি অবৈধ মাল্টিবাইট চরিত্র; ইন latin1
, এটি স্ট্রিং ¿'
। মনে রাখবেন যে, মধ্যে latin1
এবং gbk
, 0x27
তার নিজের একটি আক্ষরিক হয় '
অক্ষর।
আমরা এই পে-লোডটি বেছে নিয়েছি কারণ, যদি আমরা addslashes()
এটির আহ্বান করি তবে আমরা একটি এএসসিআইআই \
অর্থাত অক্ষরের 0x5c
আগে sert োকাতাম '
। সুতরাং আমরা সাথে বয়ে যাব 0xbf5c27
, যা gbk
একটি দুটি চরিত্রের ক্রম: এর 0xbf5c
পরে 0x27
। বা অন্য কথায়, একটি অবৈধ অক্ষর অনুসরণ করে একটি বৈধ অক্ষর '
। কিন্তু আমরা ব্যবহার করছি না addslashes()
। পরবর্তী পদক্ষেপে তাই ...
$ Stmt-> চালানো ()
এখানে উপলব্ধি করা গুরুত্বপূর্ণ বিষয়টি হ'ল পিডিও ডিফল্টরূপে সত্য প্রস্তুত বিবৃতি দেয় না । এটি তাদের অনুকরণ করে (মাইএসকিউএল জন্য)। অতএব, পিডিও অভ্যন্তরীণভাবে mysql_real_escape_string()
প্রতিটি বাউন্ড স্ট্রিংয়ের মানটিতে (মাইএসকিউএল সি এপিআই ফাংশন) কল করে কোয়েরি স্ট্রিং তৈরি করে ।
সি এপিআই কলটি এর চেয়ে mysql_real_escape_string()
পৃথক addslashes()
হয় যা এটি সংযোগের অক্ষর সেটটি জানে। সুতরাং এটি সার্ভারের দ্বারা প্রত্যাশিত অক্ষর সেটটি ঠিকঠাকভাবে সম্পাদন করতে পারে। যাইহোক, এই মুহূর্ত পর্যন্ত, ক্লায়েন্ট মনে করে যে আমরা এখনও latin1
সংযোগের জন্য ব্যবহার করছি , কারণ আমরা এটি অন্যথায় কখনও বলিনি। আমরা যে সার্ভারটি ব্যবহার করছি তা আমরা জানিয়েছি gbk
, তবে ক্লায়েন্ট এখনও মনে করে এটি এটি latin1
।
অতএব mysql_real_escape_string()
ব্যাকস্ল্যাশ সন্নিবেশ করানোর জন্য কলটি , এবং '
আমাদের "পালানো" সামগ্রীতে আমাদের একটি বিনামূল্যে ঝুলন্ত চরিত্র রয়েছে! বস্তুত, যদি আমরা তাকান ছিল $var
মধ্যে gbk
অক্ষর সেট, আমরা দেখতে চাই:
OR 'বা 1 = 1 / *
আক্রমণটি ঠিক কী প্রয়োজন।
প্রশ্ন
এই অংশটি কেবল একটি আনুষ্ঠানিকতা, তবে এখানে রেন্ডার্ড কোয়েরি রয়েছে:
SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1
অভিনন্দন, আপনি কেবলমাত্র সাফল্যের সাথে পিডিও প্রস্তুত বিবৃতি ব্যবহার করে একটি প্রোগ্রাম আক্রমণ করেছেন ...
সিম্পল ফিক্স
এখন, এটি লক্ষণীয় যে আপনি অনুকরণীয় প্রস্তুত বিবৃতি অক্ষম করে এটি প্রতিরোধ করতে পারেন:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
এটি সাধারণত একটি সত্য প্রস্তুত বিবৃতিতে (যেমন কোয়েরি থেকে পৃথক প্যাকেটে ডেটা প্রেরণ করা হবে) ফলাফল করে। যাইহোক, সচেতন থাকবেন PDO চুপটি হবে ফলব্যাক বিবৃতি এমুলেট যে মাইএসকিউএল স্থানীয়ভাবে প্রস্তুত পারে না: ঐ যে এটা করছে পারেন তালিকাভুক্ত ম্যানুয়াল কিন্তু উপযুক্ত সার্ভার সংস্করণ নির্বাচন করুন করার জন্য) হুঁশিয়ার।
সঠিক ফিক্স
এখানে সমস্যাটি হ'ল আমরা এর mysql_set_charset()
পরিবর্তে সিপিআই'র কল করি নি SET NAMES
। আমরা যদি তা করি তবে আমরা ভাল থাকব যদি আমরা 2006 থেকে একটি মাইএসকিউএল রিলিজ ব্যবহার করি।
আপনি আগের মাইএসকিউএল রিলিজ, তারপর একটি ব্যবহার করেন, তাহলে বাগ মধ্যে mysql_real_escape_string()
অর্থ ছিল যেমন আমাদের পে লোড ঐ অবৈধ multibyte অক্ষর উদ্দেশ্যে পলায়নের জন্য একক বাইট পরিণত হওয়ার উপক্রম হয়েছিল এমনকি যদি ক্লায়েন্ট সঠিকভাবে সংযোগ এনকোডিং অবগত করা হয়েছে এবং তাই এই হামলা would এখনও সফল। বাগটি MySQL 4.1.20 , 5.0.22 এবং 5.1.11 এ স্থির করা হয়েছিল ।
তবে সবচেয়ে খারাপ দিকটি হ'ল 5.3.6 পর্যন্ত সিআইপি PDO
প্রকাশ করা হয়নি mysql_set_charset()
, সুতরাং পূর্ববর্তী সংস্করণগুলিতে এটি প্রতিটি সম্ভাব্য কমান্ডের জন্য এই আক্রমণটিকে আটকাতে পারে না ! এটি এখন ডিএসএন প্যারামিটার হিসাবে প্রকাশিত হয়েছে , যা পরিবর্তে ব্যবহার করা উচিত SET NAMES
...
সেভিং গ্রেস
যেমনটি আমরা শুরুতে বলেছি, এই আক্রমণটির জন্য ডাটাবেস সংযোগটি দুর্বল অক্ষর সেটটি ব্যবহার করে এনকোড করা আবশ্যক। utf8mb4
হয় না প্রবন এখনো সমর্থন করতে পারে না এবং যে যাতে আপনি মাইএসকিউএল 5.5.3 থেকে উপলব্ধ পরিবর্তে-কিন্তু এটি শুধুমাত্র হয়েছে ব্যবহার করতে নির্বাচিত পারে: ইউনিকোড অক্ষর। একটি বিকল্প হ'ল utf8
এটিও ঝুঁকিপূর্ণ নয় এবং পুরো ইউনিকোড বেসিক বহুভাষিক সমতলটিকে সমর্থন করতে পারে ।
বিকল্পভাবে, আপনি NO_BACKSLASH_ESCAPES
এসকিউএল মোড সক্ষম করতে পারেন , যা (অন্যান্য জিনিসগুলির মধ্যে) এর ক্রিয়াকলাপকে পরিবর্তন করে mysql_real_escape_string()
। এই মোডে সক্ষম থাকলে, 0x27
সঙ্গে প্রতিস্থাপন করা হবে 0x2727
বরং 0x5c27
পলায়নের প্রক্রিয়া এবং এইভাবে করতে পারবে না প্রবন এনকোডিং যেখানে তারা পূর্বে উপস্থিত করেনি কোনো বৈধ অক্ষর তৈরি (যেমন 0xbf27
এখনও 0xbf27
ইত্যাদি।) - সার্ভার তাই এখনও অবৈধ হিসাবে স্ট্রিং প্রত্যাখ্যান করবে । তবে, এই এসকিউএল মোড (PDO সহ না হলেও) ব্যবহার করে উত্থাপিত হতে পারে এমন একটি ভিন্ন দুর্বলতার জন্য @ eggyal এর উত্তর দেখুন ।
নিরাপদ উদাহরণ
নিম্নলিখিত উদাহরণগুলি নিরাপদ:
mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
কারণ সার্ভারের প্রত্যাশা utf8
...
mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
কারণ আমরা ঠিকমতো অক্ষর সেট করে রেখেছি তাই ক্লায়েন্ট এবং সার্ভারের মিল রয়েছে।
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
কারণ আমরা অনুকরণীয় প্রস্তুত বিবৃতি বন্ধ করেছি।
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
কারণ আমরা চরিত্রটি সঠিকভাবে সেট করেছি।
$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();
কারণ মাইএসকিউএলআই সমস্ত সময় সত্য প্রস্তুত বিবৃতি দেয়।
মোড়ক উম্মচন
আপনি যদি:
- মাইএসকিউএল এর আধুনিক সংস্করণগুলি (5.1, 5.6, ইত্যাদির শেষের দিকে 5.1) এবং পিডিওর ডিএসএন চরসেট প্যারামিটার (পিএইচপি ≥ 5.3.6 এ) ব্যবহার করুন
অথবা
- সংযোগ এনকোডিংয়ের জন্য দুর্বল অক্ষর সেট ব্যবহার করবেন না (আপনি কেবল
utf8
/ latin1
/ ascii
/ ইত্যাদি ব্যবহার করেন )
অথবা
NO_BACKSLASH_ESCAPES
এসকিউএল মোড সক্ষম করুন
আপনি 100% নিরাপদ
অন্যথায়, আপনি PDO প্রস্তুত বিবৃতি ব্যবহার করা সত্ত্বেও আপনি দুর্বল হন ...
অভিযোজ্য বস্তু
পিএইচপি-র ভবিষ্যতের সংস্করণের প্রস্তুতি না নেওয়ার জন্য আমি ডিফল্টটি পরিবর্তন করতে ধীরে ধীরে প্যাচ নিয়ে কাজ করছি। আমি যে সমস্যাটি চালাচ্ছি তা হ'ল আমি যখন এটি করি তখন প্রচুর পরীক্ষাগুলি ভেঙে যায়। একটি সমস্যা হ'ল এমুলেটেড প্রিপারেসগুলি কেবল সম্পাদনের ক্ষেত্রে সিনট্যাক্স ত্রুটি নিক্ষেপ করবে তবে সত্য প্রস্তুতি প্রস্তুতির ক্ষেত্রে ত্রুটি ফেলে দেবে। সুতরাং এটি সমস্যার কারণ হতে পারে (এবং পরীক্ষাগুলি উদ্বোধনের কারণগুলির একটি অংশ)।