SQL Injection কী এবং আপনি এটি কীভাবে প্রতিরোধ করবেন?
SQL injection আক্রমণ, বাস্তব উদাহরণ এবং উৎপাদন কোডে তাদের বন্ধ করার কংক্রিট সমাধানের একটি ব্যবহারিক বিশ্লেষণ।
SQL injection 1990 এর দশকের শেষ থেকে রয়েছে এবং এটি এখনও ওয়েব অ্যাপ্লিকেশন হ্যাক হওয়ার সবচেয়ে সাধারণ উপায়গুলির মধ্যে একটি। বাগটি সহজ: একজন আক্রমণকারী এমন ইনপুট পাঠায় যা সাধারণ ডেটার পরিবর্তে SQL কোড হিসাবে ব্যাখ্যা করা হয়, এবং ডাটাবেস এমন কিছু করে যা এটি কখনই করার কথা ছিল না।
আক্রমণটি আসলে কীভাবে কাজ করে
কল্পনা করুন একটি লগইন ফর্ম যা PHP-তে এইভাবে একটি query তৈরি করে:
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
যদি অ্যাপটি ইনপুট sanitize না করে, একজন আক্রমণকারী username ফিল্ডে admin' -- টাইপ করে। query হয়ে ওঠে:
SELECT * FROM users WHERE username = 'admin' --' AND password = ''
-- লাইনের বাকি অংশকে comment করে দেয়, তাই password চেক কখনও ঘটে না। এটি একটি ক্লাসিক auth bypass। আক্রমণকারীরা অন্যান্য টেবিল থেকে ডেটা টানতে UNION SELECT ব্যবহার করে, অথবা semicolon দিয়ে query স্ট্যাক করে DROP TABLE users; এর মতো ধ্বংসাত্মক কিছু চালাতে যদি ড্রাইভার একাধিক statement অনুমতি দেয়।
এছাড়াও blind SQL injection আছে, যেখানে অ্যাপটি query ফলাফল সরাসরি দেখায় না। আক্রমণকারীরা timing এর মাধ্যমে তথ্য অনুমান করে (MySQL এ SLEEP(5)) অথবা boolean প্রতিক্রিয়া (true বনাম false অবস্থার জন্য পৃষ্ঠা ভিন্নভাবে আচরণ করে কি)। sqlmap এর মতো সরঞ্জামগুলি একটি দুর্বল প্যারামিটার খুঁজে পাওয়ার পরে এই ধরনের extraction স্বয়ংক্রিয় করে।
কেন string concatenation মূল সমস্যা
এই আক্রমণের প্রতিটি variant একটি জিনিসে ফিরে আসে: একই string এ code এবং data মিশ্রিত করা। ডাটাবেস একটি legitimate মূল্য এবং injected syntax এর মধ্যে পার্থক্য বলতে পারে না কারণ তারা একই channel এ আসে। quote escaping করা কিছু ক্ষেত্রে সাহায্য করে কিন্তু এটি দুর্বল — বিভিন্ন encoding, second-order injection (ডেটা একবার সংরক্ষিত, তারপরে পরে অনিরাপদভাবে পুনরায় ব্যবহৃত), এবং ড্রাইভার-নির্দিষ্ট quirks সবই bypass পথ তৈরি করে। Escaping একটি প্যাচ, একটি fix নয়।
Parameterized queries আসল fix
fix হল SQL code কে user data থেকে ড্রাইভার স্তরে আলাদা করা, parameterized queries (prepared statements নামেও পরিচিত) ব্যবহার করে। ডাটাবেস প্রথমে query structure পায়, তারপরে পরে মূল্য বাঁধায়, তাই user input কখনও query এর অর্থ পরিবর্তন করতে পারে না।
psycopg2 সহ Python এ:
cur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
mysql2 সহ Node.js এ:
connection.execute('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);
JDBC সহ Java এ:
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);
pattern লক্ষ্য করুন: placeholders (%s, ?) data এর জন্য স্পট ধরে, এবং actual মূল্যগুলি আলাদাভাবে pass হয়। কোন string concatenation নেই, কোন manual escaping প্রয়োজন নেই। এটি একটি সাধারণ অ্যাপ্লিকেশনে আপনি যা লিখবেন তার বিশাল সংখ্যাগরিষ্ঠতার জন্য কাজ করে।
গতিশীল টেবিল বা column নামের বিষয়ে কী?
Parameterization মূল্য পরিচালনা করে কিন্তু identifier নয় — আপনি একটি টেবিল নাম একটি parameter হিসাবে bind করতে পারবেন না। যদি আপনার অ্যাপকে গতিশীলভাবে একটি টেবিল নির্বাচন করতে হয় (বিরল, এবং প্রায়শই একটি design smell), user input থেকে সরাসরি বিশ্বাস করার পরিবর্তে hardcoded list এর বিরুদ্ধে অনুমতিপ্রাপ্ত মূল্যগুলি whitelist করুন:
allowed_tables = {'orders', 'invoices', 'customers'}
if table_name not in allowed_tables:
raise ValueError("Invalid table")
কখনও identifier নাম user input থেকে string formatting এর মাধ্যমে তৈরি করবেন না, escaping সহ এমনকি।
Query এর নিজের বাইরে স্তরীয় প্রতিরক্ষা
Parameterized queries প্রাথমিক নিয়ন্ত্রণ, কিন্তু আরও কয়েকটি জিনিস গুরুত্বপূর্ণ:
- ডাটাবেস অ্যাকাউন্টে least privilege। অ্যাপের DB ব্যবহারকারীর
DROP,ALTER, বা সম্পর্কহীন schemas এ অ্যাক্সেস থাকা উচিত নয়। যদি একটি injection slip through করে, সীমিত privilege ক্ষতি cap করে। - ORMs ডিফল্টভাবে সাহায্য করে। Django এর ORM, SQLAlchemy, এবং Hibernate সব তাদের standard query-building methods ব্যবহার করার সময় স্বয়ংক্রিয়ভাবে query parameterize করে। ঝুঁকি পুনরায় প্রদর্শিত হয় যখন developers raw SQL এ ড্রপ করে বা string interpolation সহ
.extra()/text()calls ব্যবহার করে — তাই সেই স্থানগুলি বিশেষভাবে audit করুন। - Input validation একটি secondary layer, একটি প্রতিস্থাপন নয়। একটি email ফিল্ড একটি email এর মতো দেখায় তা পরীক্ষা করা ভালো অনুশীলন, কিন্তু এটি নিজেই injection বন্ধ করে না — আক্রমণকারীরা creative payloads খুঁজে পায় যা এখনও loose validation pass করে।
- একটি WAF পরিচিত আক্রমণ patterns ধরতে পারে, কিন্তু এটি একটি detection layer, underlying code এর fix নয়।
এই বাগের জন্য আপনার নিজের কোড পরীক্ষা করা
CI এর অংশ হিসাবে একটি static analysis tool এর মাধ্যমে আপনার query চালান (Python এর জন্য Bandit, SQL injection rulesets সহ Semgrep)। Manual testing এর জন্য, প্রতিটি input ফিল্ডে একটি একক quote (') inject করার চেষ্টা করুন এবং response এ SQL error message leaking দেখুন — যা প্রায়শই একটি query parameterized না হওয়ার প্রথম চিহ্ন।
যদি আপনি এটিতে গভীরভাবে যেতে চান, Korra Studio এর Web Security track XSS এবং auth bypass এর পাশাপাশি injection কভার করে, এবং Databases segments query design patterns এর মাধ্যমে হাঁটে যা এই class এর বাগ সম্পূর্ণভাবে এড়ায়।
AI সহায়তায় লেখা, পর্যালোচনা ও প্রকাশ করেছেন Michal Pilch (CISSP), Korra Studio।
এটি Korra Studio-র নলেজ বেস থেকে একটি নোট — প্ল্যাটফর্মটি প্রতিটি বিষয়কে ১-এর-সাথে-১ মেন্টরিংয়ের সাথে জুড়ে দেয়।
বিনামূল্যে শুরু করুনarrow_forward