অথেনটিকেশন বাইপাস
admin' OR '1'='1WHERE শর্তকে সবসময় সত্য বানিয়ে লগইন এড়ানোর চেষ্টা
ব্যবহারকারীর দেওয়া মান কখনো SQL টেক্সটে ইন্টারপোলেট করা হয় না — মানগুলো প্যারামিটার প্লেসহোল্ডারের মাধ্যমে বাইন্ড করা হয় (ডায়ালেক্ট-নির্দিষ্ট, যেমন $1 বা ?)
SQL ইনজেকশন এখনও ওয়েব অ্যাপ্লিকেশনের সবচেয়ে গুরুতর দুর্বলতাগুলোর একটি
অবিশ্বস্ত ডেটা নিরাপদ প্যারামিটার বাইন্ডিং ছাড়া SQL টেক্সটে যুক্ত হলে SQL ইনজেকশন ঘটে। আক্রমণকারী কুয়েরির অর্থ বদলে দিয়ে অননুমোদিত ডেটা অ্যাক্সেস করতে, রেকর্ড পরিবর্তন করতে বা অনাকাঙ্ক্ষিত অপারেশন চালাতে পারে।
admin' OR '1'='1WHERE শর্তকে সবসময় সত্য বানিয়ে লগইন এড়ানোর চেষ্টা
' UNION SELECT password FROM users--অন্য টেবিল থেকে ডেটা বের করার চেষ্টায় UNION ব্যবহার
'; DROP TABLE users; --ডেটা মুছতে অতিরিক্ত স্টেটমেন্ট জুড়ে দেওয়ার চেষ্টা
এক্সপ্লয়েট: প্লেসহোল্ডার ও params না মিললে অথরাইজেশন চেক ভুল মান ব্যবহার করতে পারেউদাহরণ: WHERE userId=$1 AND isAdmin=$2, কিন্তু [123, true]-এর বদলে params=[true, 123] হলে ভুলভাবে অ্যাডমিন অ্যাক্সেস দেওয়া হতে পারে
প্রতিটি কুয়েরি একাধিক নিরাপত্তা যাচাই স্তরের মধ্য দিয়ে যায়
ব্যবহারকারীর দেওয়া সব মান বাইন্ড করা প্যারামিটারে রূপান্তরিত হয়। মানগুলো লিটারাল হিসেবে SQL টেক্সটে ঢোকে না।
কুয়েরি জেনারেশনের আগে প্রতিটি ফিল্ডের নাম Prisma স্কিমা মেটাডেটার সাথে মিলিয়ে যাচাই করা হয়।
টেবিলের নাম, কলামের নাম ও অ্যালিয়াস যাচাই করা হয় এবং প্রয়োজন অনুযায়ী নিরাপদে কোট/এস্কেপ করা হয়।
প্রতিটি ফিল্ড টাইপের জন্য শুধু পরিচিত, নিরাপদ অপারেটর অনুমোদিত।
কঠোর ক্রম নিশ্চিত করে যে প্রতিটি প্লেসহোল্ডার যোগ করার ক্রম অনুযায়ী ঠিক একটি প্যারামিটারের সাথে মেলে, ফলে অমিল হয় না।
উল্লিখিত ডিজাইন অনুমানের অধীনে (প্যারামিটার বাইন্ডিং, raw-SQL বাইপাস নেই, ড্রাইভারের সঠিক ব্যবহার) ইনজেকশন প্রতিরোধের প্রমাণের রূপরেখা
ব্যবহারকারীর দেওয়া সব মান V-এর জন্য এমন কোনো এক্সিকিউশন পথ নেই, যেখানে V জেনারেট হওয়া কুয়েরি স্ট্রিংয়ে SQL টেক্সট হিসেবে আসে।
গঠন অনুযায়ী, ব্যবহারকারীর মান সামলায় এমন সব কোড পথ addParameter(value) কল করে, যা: 1. মানটি আলাদা একটি params অ্যারেতে রাখে 2. SQL টেক্সটের জন্য একটি প্লেসহোল্ডার টোকেন ফেরত দেয় 3. SQL স্ট্রিংয়ে শুধু প্লেসহোল্ডার টোকেন যোগ করা হয় 4. ডেটাবেস ড্রাইভার SQL টেক্সট ও প্যারামিটারের মান আলাদাভাবে পায় ∴ ব্যবহারকারীর মান SQL সিনট্যাক্স হিসেবে পার্স হয় না
const addParameter = (params: any[], value: any) => {
params.push(value)
const index = params.length
return `$${index}`
}যেকোনো জেনারেট হওয়া কুয়েরিতে ফিল্ড নামের সেট F স্কিমায় সংজ্ঞায়িত ফিল্ড S-এর উপসেট, অর্থাৎ F ⊆ S।
প্রতিটি ফিল্ড রেফারেন্সের জন্য: 1. কুয়েরি অবজেক্ট থেকে ফিল্ডের নাম বের করা হয় 2. স্কিমা মেটাডেটায় খোঁজা হয় 3. ফিল্ডটি স্কিমায় না থাকলে এরর থ্রো করা হয় 4. শুধু যাচাইকৃত ফিল্ড SQL জেনারেশনে পৌঁছায় ∴ যথেচ্ছ ফিল্ড নাম SQL-এ আসতে পারে না
const validateField = (field: string, model: Model) => {
if (!model.fields.has(field)) {
throw new Error(`Field ${field} does not exist`)
}
return model.fields.get(field)
}ফিল্ড F-এ প্রয়োগ করা যেকোনো অপারেটর O, F-এর টাইপ T-এর জন্য অনুমোদিত অপারেটরগুলোর একটি।
অপারেটর যাচাইয়ের অ্যালগরিদম: 1. স্কিমা থেকে ফিল্ড টাইপ T বের করা 2. allowed_ops(T) = { টাইপ T-এর বৈধ অপারেটর } নির্ধারণ করা 3. কুয়েরির অপারেটর O-এর জন্য: - O ∉ allowed_ops(T) হলে এরর থ্রো করা - অন্যথায় প্যারামিটার বাইন্ডিং দিয়ে অপারেটর প্রয়োগ করা ∴ শুধু টাইপ-উপযুক্ত অপারেটর ব্যবহার করা যায়
const ALLOWED_OPS: Record<string, string[]> = {
String: ['contains', 'startsWith', 'endsWith'],
Int: ['lt', 'lte', 'gt', 'gte']
}
if (!ALLOWED_OPS[fieldType]?.includes(operator)) {
throw new Error('Invalid operator')
}সব SQL আইডেন্টিফায়ার I যাচাই করা হয়, যাতে কন্ট্রোল ক্যারেক্টার আটকানো যায় এবং প্রতি ডায়ালেক্টে নিরাপদ কোটিং/এস্কেপিং নিশ্চিত হয়।
আইডেন্টিফায়ার প্রসেসিং: 1. কন্ট্রোল ক্যারেক্টার থাকলে প্রত্যাখ্যান 2. ডায়ালেক্টের নিয়ম অনুযায়ী ভেতরের কোট ক্যারেক্টার এস্কেপ 3. প্রয়োজনে আইডেন্টিফায়ার কোট করা (সংরক্ষিত কীওয়ার্ড বা বিশেষ ক্যারেক্টার) 4. প্রতি ডায়ালেক্টে আইডেন্টিফায়ারের দৈর্ঘ্যসীমা প্রয়োগ ∴ আইডেন্টিফায়ার SQL সিনট্যাক্স ভাঙতে বা ইনজেক্ট করা টোকেন ঢোকাতে পারে না
const quoteIdentifier = (id: string) => {
if (/[\x00-\x1F]/.test(id)) {
throw new Error('Invalid characters')
}
const escaped = id.replace(/"/g, '""')
const needsQuoting = /[^a-z0-9_]/i.test(id) || isReservedKeyword(id)
return needsQuoting ? `"${escaped}"` : id
}SQL টেক্সটে বসানো প্রতিটি প্লেসহোল্ডার অবস্থান N-এর জন্য params[N-1]-এ ঠিক সেই অবস্থানের জন্য নির্ধারিত মানটিই থাকে, কোনো ক্রম বদল বা অমিল ছাড়া।
প্যারামিটার ক্রমের নিশ্চয়তা: 1. একটিমাত্র ট্র্যাকার যোগ করার ক্রম বজায় রাখে 2. প্রতিটি add() কল মানটি params-এ যোগ করে এবং সঙ্গে সঙ্গে পরের প্লেসহোল্ডার টোকেন দেয় 3. SQL টেক্সট তৈরি ও params তৈরি একসাথে ধাপে ধাপে এগোয় 4. কোনো মধ্যবর্তী ক্রম বদলের অপারেশন ঘটে না ∴ পুরো প্রক্রিয়ায় এক-এক সম্পর্ক বজায় থাকে
class ParameterTracker {
private params: any[] = []
add(value: any): string {
this.params.push(value)
return `$${this.params.length}`
}
getParams(): any[] {
return this.params
}
}
const tracker = new ParameterTracker()
const sql = `WHERE email = ${tracker.add(email)} AND age > ${tracker.add(age)}`
const params = tracker.getParams()বর্তমান স্যুট সাধারণ SQL ইনজেকশন ভেক্টর ও এজ কেসের বিরুদ্ধে সুরক্ষা যাচাই করে
const email = req.body.email
const sql = "SELECT * FROM users WHERE email = '" + email + "'"const email = req.body.email
const { sql, params } = toSQL('User', 'findMany', {
where: { email }
})
const result = { sql, params }কেন্দ্রীয় প্যারামিটার ট্র্যাকিং নিশ্চিত করে যে ব্যবহারকারীর দেওয়া প্রতিটি মান প্যারামিটার হিসেবে বাইন্ড হয় এবং ক্রম স্থির থাকে
class ParameterTracker {
private params: any[] = []
add(value: any): string {
this.params.push(value)
return `$${this.params.length}`
}
getParams(): any[] {
return this.params
}
}স্কিমা-ভিত্তিক যাচাই যথেচ্ছ ফিল্ড অ্যাক্সেস প্রতিরোধ করে
function validateField(
fieldName: string,
model: ModelInfo
): FieldInfo {
const field = model.fields.get(fieldName)
if (!field) {
throw new Error(
`Field "${fieldName}" does not exist`
)
}
return field
}ডায়ালেক্ট-সচেতন আইডেন্টিফায়ার যাচাই ও কোটিং আইডেন্টিফায়ার-ভিত্তিক সিনট্যাক্স ইনজেকশন প্রতিরোধ করে
function quoteIdentifier(identifier: string): string {
if (/[\x00-\x1F]/.test(identifier)) {
throw new Error('Invalid control characters')
}
const escaped = identifier.replace(/"/g, '""')
const needsQuoting = /[^a-z0-9_]/i.test(identifier) || isReservedKeyword(identifier)
if (needsQuoting) {
return `"${escaped}"`
}
return identifier
}স্কিমা যাচাই বা ফিল্ড চেক এড়িয়ে যাবেন না। এগুলো গুরুত্বপূর্ণ নিরাপত্তা স্তর।
নিশ্চিত করুন, আপনার Prisma স্কিমা ডেটাবেসের কাঠামো সঠিকভাবে প্রতিফলিত করে।
TypeScript কম্পাইল-টাইম সেফটি দেয়, কিন্তু রানটাইম যাচাই সুরক্ষায় আরেকটি স্তর যোগ করে।
অস্বাভাবিকতা ও অপব্যবহার ধরতে প্রোডাকশনে জেনারেট হওয়া SQL ও প্যারামিটার লগ ও মনিটর করুন।
এই লাইব্রেরির প্রতিটি স্তরে SQL ইনজেকশন সুরক্ষা তৈরি করা আছে। নিরাপত্তা টেস্ট ও বাস্তবায়নের বিস্তারিত সরাসরি পর্যালোচনা করুন।