SQL 쿼리를 (수동으로) 매개변수화하지 마십시오.

지난 주에 저는 SafeQL이라는 플러그인(SQL 쿼리를 린트하는)에 대해 썼습니다. 그렇게 하면서 나는 다음과 같은 글을 썼다.

client.query(sql`SELECT * FROM comments WHERE id = ${id}`);


즉시 몇 군데에서 SQL 인젝션이 가능하기 때문에 parameterize my 응답을 받았습니다.

그들이 완전히 틀린 것은 아니지만 정확하지는 않습니다.

설명하겠습니다. 쿼리 문자열 내부에 변수를 쓰는 것은 실제로 SQL 인젝션에 대한 호출입니다.

// ⚠️ Open to SQL injection
client.query(`SELECT * FROM comments WHERE id = ${id}`)
// id = "0 OR (other statement)"


그것이 사실이지만 아래 코드에서 일어나는 일이 아닙니다.

client.query(sql`SELECT * FROM comments WHERE id = ${id}`);
          // ^^^ tagged templated literal


이 구문은 무엇입니까?



Tagged templates 이라고 하며 MDN을 인용합니다.

Tags allow you to parse template literals with a function. The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions.



내가 5살인 것처럼 설명해줘




// this line
client.query(sql`SELECT * FROM comments WHERE id = ${id}`)

// turns into this:
client.query({
  text: "SELECT * FROM comments WHERE id = $1",
  values: [id]
})

sql(태그가 있는 템플릿 리터럴)은 다음과 같은 함수입니다.

export function sql(
  template: TemplateStringsArray,
  ...values: unknown[]
);

// Typescript built-in type
interface TemplateStringsArray extends ReadonlyArray<string> {
    readonly raw: readonly string[];
}


다음 코드가 주어집니다.

sql`SELECT * FROM comments WHERE id = ${id}`
// template === ["SELECT * FROM comments WHERE id = "]
// values === [id]

template["SELECT * FROM comments WHERE id = "]와 같고 값은 [id]와 같습니다.

그러나 node-postgres 또는 Sequelize는 어떻게 처리합니까?



태그가 지정된 템플릿 리터럴이라는 용어를 이해한 후에 어떻게 우리에게 유리하게 사용할 수 있습니까?

node-postgres는 다음을 통해 SQL 쿼리를 작성할 수 있습니다.

// string only approach
client.query("select name FROM table_name WHERE id = $1", [name]);

// object approach
client.query({
  text: "select name FROM table_name WHERE id = $1",
  values: [name]
});


Sequelize를 사용하면 query 대신 text를 사용하여 객체 접근 방식으로 작성할 수 있습니다.

sequelize.query({
  query: "select name FROM table_name WHERE id = $1",
  values: [name]
});


알 수 있듯이 개체 구조는 sql 태그가 반환한 것과 동일한 구조와 유사합니다. 즉, sql 태그를 사용하면 매개변수화된 쿼리와 유사한 객체를 반환합니다.

간단히 말해서 SQL 구현




function sql(template: TemplateStringsArray, ...values: unknown[]) {
  let id = 0;
  let text = template[0];

  for (const value of values) {
    query += `$${++id}`;
    query += template[id];
  }

  // we pass both `query` and `text` so it would be compatible with both
  // node-postgres and Sequelize:
  return { text, query: text, values };
}


수동으로 매개변수화된 쿼리에 사용해야 하는 이유는 무엇입니까?



더 쉽고 안전하며 오류가 덜 발생하고 더 간단합니다. 대규모 쿼리는 최대 수십 개의 매개변수까지 올라갈 수 있습니다. 그들이 잘못 배치되는 것은 시간 문제입니다. 또한 $5 또는 $14가 무엇인지 파악하는 동안 쿼리를 읽는 것은 어려울 수 있습니다.

npm 설치



SQL 구현을 유지하는 대신 이미 수행한 몇 가지 패키지가 있습니다.

@ts-safeql/sql-tag
sql-template-strings
sql-template-tag


무엇 향후 계획?



TypeScript를 사용 중이고 유형 안전성에 관심이 있는 경우 SafeQL을 확인하십시오. 무료 오픈 소스 ESLint 플러그인으로 테이블, 열 또는 잘못된 구문의 철자를 절대 놓치지 않도록 합니다. 또한 작성하는 각 쿼리에 대해 TypeScript 유형을 자동으로 생성합니다.

좋은 웹페이지 즐겨찾기