নিরাপত্তা আগে

SQL ইনজেকশন প্রতিরোধ

ব্যবহারকারীর দেওয়া মান কখনো SQL টেক্সটে ইন্টারপোলেট করা হয় না — মানগুলো প্যারামিটার প্লেসহোল্ডারের মাধ্যমে বাইন্ড করা হয় (ডায়ালেক্ট-নির্দিষ্ট, যেমন $1 বা ?)

প্যারামিটার বাইন্ড
100%
ব্যবহারকারীর দেওয়া সব মান প্লেসহোল্ডার ব্যবহার করে
মান ইন্টারপোলেশন
শূন্য
ব্যবহারকারীর ডেটা কখনো SQL টেক্সটে আসে না
ক্রম সংরক্ষণ
নিশ্চিত
প্লেসহোল্ডার ও params 1:1 মেলে
নিরাপত্তা টেস্ট
বর্তমান
ইনজেকশন প্রচেষ্টার কভারেজ

SQL ইনজেকশন বোঝা

SQL ইনজেকশন এখনও ওয়েব অ্যাপ্লিকেশনের সবচেয়ে গুরুতর দুর্বলতাগুলোর একটি

অবিশ্বস্ত ডেটা নিরাপদ প্যারামিটার বাইন্ডিং ছাড়া SQL টেক্সটে যুক্ত হলে SQL ইনজেকশন ঘটে। আক্রমণকারী কুয়েরির অর্থ বদলে দিয়ে অননুমোদিত ডেটা অ্যাক্সেস করতে, রেকর্ড পরিবর্তন করতে বা অনাকাঙ্ক্ষিত অপারেশন চালাতে পারে।

সাধারণ আক্রমণের ধরন

অথেনটিকেশন বাইপাস

admin' OR '1'='1

WHERE শর্তকে সবসময় সত্য বানিয়ে লগইন এড়ানোর চেষ্টা

ডেটা চুরি

' UNION SELECT password FROM users--

অন্য টেবিল থেকে ডেটা বের করার চেষ্টায় UNION ব্যবহার

ধ্বংসাত্মক কমান্ড

'; DROP TABLE users; --

ডেটা মুছতে অতিরিক্ত স্টেটমেন্ট জুড়ে দেওয়ার চেষ্টা

প্যারামিটার ক্রমের বাগের অপব্যবহার

এক্সপ্লয়েট: প্লেসহোল্ডার ও params না মিললে অথরাইজেশন চেক ভুল মান ব্যবহার করতে পারে

উদাহরণ: WHERE userId=$1 AND isAdmin=$2, কিন্তু [123, true]-এর বদলে params=[true, 123] হলে ভুলভাবে অ্যাডমিন অ্যাক্সেস দেওয়া হতে পারে

বহুস্তরের সুরক্ষা কৌশল

প্রতিটি কুয়েরি একাধিক নিরাপত্তা যাচাই স্তরের মধ্য দিয়ে যায়

  1. স্তর 1: স্বয়ংক্রিয় প্যারামিটার বাইন্ডিং

    ব্যবহারকারীর দেওয়া সব মান বাইন্ড করা প্যারামিটারে রূপান্তরিত হয়। মানগুলো লিটারাল হিসেবে SQL টেক্সটে ঢোকে না।

    • ব্যবহারকারীর মান ডায়ালেক্ট-নির্দিষ্ট প্লেসহোল্ডার দিয়ে প্রকাশ করা হয় (যেমন $1 বা ?)
    • অ্যারে ইনপুট প্যারামিটারাইজ করা হয় (ডায়ালেক্ট/কৌশল অনুযায়ী বিস্তৃত প্লেসহোল্ডার বা অ্যারে প্যারামিটার)
    • NULL ও ঐচ্ছিক মান ব্যবহারকারীর ইনপুট SQL টেক্সটে জোড়া না দিয়েই সামলানো হয়
    • তারিখ/সময়ের মান প্যারামিটারাইজ করা হয়
    • JSON মান প্যারামিটারাইজ করা হয়
  2. স্তর 2: ফিল্ড নাম যাচাই

    কুয়েরি জেনারেশনের আগে প্রতিটি ফিল্ডের নাম Prisma স্কিমা মেটাডেটার সাথে মিলিয়ে যাচাই করা হয়।

    • শুধু স্কিমায় সংজ্ঞায়িত ফিল্ড অনুমোদিত
    • রিলেশন ফিল্টার স্কিমা মেটাডেটা দিয়ে যাচাই করা হয়
    • যথেচ্ছ কোনো ফিল্ড নাম গ্রহণ করা হয় না
    • প্রযোজ্য ক্ষেত্রে অসমর্থিত/কম্পিউটেড ফিল্ড প্রত্যাখ্যান করা হয়
    • প্রোটোটাইপ পলিউশন পেলোড প্রত্যাখ্যান করা হয় (যেমন __proto__, constructor)
  3. স্তর 3: আইডেন্টিফায়ার স্যানিটাইজেশন

    টেবিলের নাম, কলামের নাম ও অ্যালিয়াস যাচাই করা হয় এবং প্রয়োজন অনুযায়ী নিরাপদে কোট/এস্কেপ করা হয়।

    • কন্ট্রোল ক্যারেক্টার প্রত্যাখ্যান করা হয়
    • দরকার হলে সংরক্ষিত কীওয়ার্ড কোট করা হয়
    • যেখানে কনফিগার করা আছে সেখানে স্কিমা কোয়ালিফিকেশন সমর্থিত
    • আইডেন্টিফায়ারের জন্য ডাবল-কোট এস্কেপিং (বা ডায়ালেক্টের সমতুল্য)
    • প্রতি ডায়ালেক্টে আইডেন্টিফায়ারের সর্বোচ্চ দৈর্ঘ্য প্রয়োগ করা হয়
  4. স্তর 4: অপারেটর যাচাই

    প্রতিটি ফিল্ড টাইপের জন্য শুধু পরিচিত, নিরাপদ অপারেটর অনুমোদিত।

    • স্ট্রিং অপারেটর: contains, startsWith, endsWith
    • সংখ্যাসূচক অপারেটর: lt, lte, gt, gte
    • অ্যারে অপারেটর: টাইপ যাচাইসহ in, notIn
    • লজিক্যাল অপারেটর: AND, OR, NOT কাঠামোগতভাবে যাচাই করা হয়
    • অজানা অপারেটর সঙ্গে সঙ্গে প্রত্যাখ্যান করা হয়
  5. স্তর 5: প্যারামিটার ক্রমের নিশ্চয়তা

    কঠোর ক্রম নিশ্চিত করে যে প্রতিটি প্লেসহোল্ডার যোগ করার ক্রম অনুযায়ী ঠিক একটি প্যারামিটারের সাথে মেলে, ফলে অমিল হয় না।

    • ধারাবাহিক কাউন্টার ক্রম বদলানো প্রতিরোধ করে
    • SQL তৈরি ও param অ্যারে তৈরি একসাথে ধাপে ধাপে চলে
    • কোনো মধ্যবর্তী বাফার বা ক্রম বদলের অপারেশন নেই
    • টেস্টে যাচাই: জেনারেট হওয়া SQL ও params সবসময় মেলে
    • এক-এক সম্পর্ক: প্লেসহোল্ডার N → params[N-1] (বা ডায়ালেক্টের সমতুল্য ম্যাপিং)

আনুষ্ঠানিক নিরাপত্তা নিশ্চয়তা

উল্লিখিত ডিজাইন অনুমানের অধীনে (প্যারামিটার বাইন্ডিং, raw-SQL বাইপাস নেই, ড্রাইভারের সঠিক ব্যবহার) ইনজেকশন প্রতিরোধের প্রমাণের রূপরেখা

উপপাদ্য 1: মান বিচ্ছিন্নতা

ব্যবহারকারীর দেওয়া সব মান 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}`
}

উপপাদ্য 2: ফিল্ড নামের আবদ্ধতা

যেকোনো জেনারেট হওয়া কুয়েরিতে ফিল্ড নামের সেট 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)
}

উপপাদ্য 3: অপারেটর নিরাপত্তা

ফিল্ড 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')
}

উপপাদ্য 4: আইডেন্টিফায়ার নিরাপত্তা

সব 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
}

উপপাদ্য 5: প্যারামিটার ক্রমের সামঞ্জস্য

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 ইনজেকশন ভেক্টর ও এজ কেসের বিরুদ্ধে সুরক্ষা যাচাই করে

মান প্যারামিটারাইজেশন (basic.test.ts)

  • কোটসহ স্ট্রিং: user'with'quotes
  • সেমিকোলনসহ স্ট্রিং: user;extra
  • মান হিসেবে SQL কীওয়ার্ড: DROP TABLE users
  • জটিল ইনজেকশন: '; DROP TABLE users; --
  • Union আক্রমণ: ' UNION SELECT * FROM users--
  • Boolean আক্রমণ: admin' OR '1'='1
  • কমেন্ট ইনজেকশন: test@example.com' -- comment

ফিল্ড নাম যাচাই (identifiers.test.ts)

  • SELECT-এ ক্ষতিকর ফিল্ড নাম প্রত্যাখ্যান
  • WHERE-এ ক্ষতিকর ফিল্ড নাম প্রত্যাখ্যান
  • ORDER BY-তে ক্ষতিকর ফিল্ড নাম প্রত্যাখ্যান
  • ফিল্ড নামে SQL ইনজেকশন প্রত্যাখ্যান
  • অস্তিত্বহীন ফিল্ড প্রত্যাখ্যান
  • প্রোটোটাইপ পলিউশন প্রত্যাখ্যান: __proto__, constructor

LIKE প্যাটার্ন নিরাপত্তা (like-patterns.test.ts)

  • ওয়াইল্ডকার্ড ইনজেকশন: %' OR '1'='1
  • আন্ডারস্কোর ইনজেকশন: test_' OR '1'='1
  • ব্যাকস্ল্যাশ সামলানো: test\\'; DROP--
  • একাধিক ওয়াইল্ডকার্ড: %_%'; DROP--
  • কেস-ইনসেনসিটিভ ইনজেকশন: '; UNION SELECT--

অ্যারে অপারেটর নিরাপত্তা (array-operators.test.ts)

  • ক্ষতিকর অ্যারেসহ IN: ['; DROP--', 'UNION SELECT--']
  • ইনজেকশনসহ NOT IN: ['; TRUNCATE--', 'DELETE FROM--']
  • খালি অ্যারে সামলানো
  • বড় অ্যারে যাচাই (100+ আইটেম)
  • মিশ্র টাইপের অ্যারে সামলানো

এজ কেস (edge-cases.test.ts)

  • Unicode ইনজেকশন: \u0027 OR \u00271\u0027=\u00271
  • Hex-এনকোডেড ইনজেকশন: 0x31=0x31--
  • URL এনকোডেড: %27%3B%20DROP%20TABLE
  • স্ট্যাকড কুয়েরি: '; DROP TABLE users; SELECT
  • টাইম-ভিত্তিক ব্লাইন্ড (ডায়ালেক্ট-নির্দিষ্ট): WAITFOR DELAY '00:00:05'--
  • UNION-ভিত্তিক: UNION ALL SELECT null, password
  • সেকেন্ড-অর্ডার ইনজেকশনের চেষ্টা

প্যারামিটার ক্রম যাচাই (basic.test.ts)

  • ধারাবাহিক প্লেসহোল্ডার অবস্থান
  • প্যারামিটার অ্যারে প্লেসহোল্ডারের ক্রমের সাথে মেলে
  • জটিল কুয়েরিতে বিভিন্ন শর্ত জুড়ে ক্রম বজায় থাকে
  • নেস্টেড OR/AND শর্তে প্যারামিটারের ক্রম বজায় থাকে
  • রিলেশন ফিল্টারে সঠিক প্যারামিটার ম্যাপিং বজায় থাকে

নিরাপদ বনাম অনিরাপদ: কোডের তুলনা

❌ অনিরাপদ: স্ট্রিং জোড়া দেওয়া
unsafe.ts
const email = req.body.email
const sql = "SELECT * FROM users WHERE email = '" + email + "'"
✅ নিরাপদ: স্বয়ংক্রিয় প্যারামিটার বাইন্ডিং
safe.ts
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
}

নিরাপত্তার সেরা অনুশীলন

কখনো যাচাই বন্ধ করবেন না

স্কিমা যাচাই বা ফিল্ড চেক এড়িয়ে যাবেন না। এগুলো গুরুত্বপূর্ণ নিরাপত্তা স্তর।

করুন
পূর্ণ যাচাইসহ লাইব্রেরিটি যেভাবে ডিজাইন করা সেভাবেই ব্যবহার করুন
করবেন না
স্কিমা যাচাই এড়িয়ে যাওয়া বা কুয়েরি টেক্সটে raw SQL ঢোকানো

স্কিমা হালনাগাদ রাখুন

নিশ্চিত করুন, আপনার Prisma স্কিমা ডেটাবেসের কাঠামো সঠিকভাবে প্রতিফলিত করে।

করুন
স্কিমা পরিবর্তনের পর prisma db pull ও prisma generate চালান
করবেন না
পুরনো DMMF বা স্কিমা সংজ্ঞা ব্যবহার করা

ইনপুট টাইপ যাচাই করুন

TypeScript কম্পাইল-টাইম সেফটি দেয়, কিন্তু রানটাইম যাচাই সুরক্ষায় আরেকটি স্তর যোগ করে।

করুন
কুয়েরি আর্গুমেন্টের জন্য Prisma-এর জেনারেট হওয়া টাইপ ব্যবহার করুন
করবেন না
কুয়েরিতে পাঠানোর আগে ব্যবহারকারীর ইনপুটকে any-তে কাস্ট করা

কুয়েরি প্যাটার্ন মনিটর করুন

অস্বাভাবিকতা ও অপব্যবহার ধরতে প্রোডাকশনে জেনারেট হওয়া SQL ও প্যারামিটার লগ ও মনিটর করুন।

করুন
কুয়েরি লগিং চালু করুন এবং প্যাটার্ন পর্যালোচনা করুন
করবেন না
মনিটরিং ছাড়া প্রোডাকশনে চালানো

ডিজাইনেই নিরাপত্তা

এই লাইব্রেরির প্রতিটি স্তরে SQL ইনজেকশন সুরক্ষা তৈরি করা আছে। নিরাপত্তা টেস্ট ও বাস্তবায়নের বিস্তারিত সরাসরি পর্যালোচনা করুন।