It has come to our attention that many applications which use libpq
are vulnerable to code insertion attacks in strings and identifiers
passed to these applications.  We have collected some evidence which
suggests that this is related to the fact that libpq does not provide
a function to escape strings and identifiers properly.  (Both the
Oracle and MySQL client libraries include such a function, and the
vast majority of applications we examined are not vulnerable to code
insertion attacks because they use this function.)

We therefore suggest that a string escaping function is included in a
future version of PostgreSQL and libpq.  A sample implementation is
provided below, along with documentation.

-- 
Florian Weimer                    [EMAIL PROTECTED]
University of Stuttgart           http://cert.uni-stuttgart.de/
RUS-CERT                          +49-711-685-5973/fax +49-711-685-5898
Index: libpq.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.66
diff -u -r1.66 libpq.sgml
--- libpq.sgml  2001/08/10 22:50:09     1.66
+++ libpq.sgml  2001/08/22 15:58:02
@@ -827,6 +827,42 @@
 </itemizedlist>
 </sect2>
 
+<sect2 id="libpq-exec-escape-string">
+  <title>Escaping strings for inclusion in SQL queries</title>
+<para>
+<function>PGescapeString</function>
+          Escapes a string for use within an SQL query.
+<synopsis>
+size_t PGescapeString (char *to, const char *from, size_t length);
+</synopsis>
+If you want to include strings or identifiers which have been received
+from a source which is not trustworthy (for example, because they were
+transmitted across a network), you cannot directly include them in SQL
+queries for security reasons.  Instead, you have to quote special
+characters which are otherwise interpreted by the SQL parser.
+</para>
+<para>
+<function>PGescapeString</> performs this operation.  The
+<parameter>from</> points to the first character of the string which
+is to be escaped, and the <parameter>length</> parameter counts the
+number of characters in this string (a terminating NUL character is
+neither necessary nor counted).  <parameter>to</> shall point to a
+buffer which is able to hold at least one more character than twice
+the value of <parameter>length</>, otherwise the behavior is
+undefined.  A call to <function>PGescapeString</> writes an escaped
+version of the <parameter>from</> string to the <parameter>to</>
+buffer, replacing special characters so that they cannot cause any
+harm, and adding a terminating NUL character.  The single or double
+quote characters which are required for strings and identifiers,
+respectively, are not added to the result string.
+</para>
+<para>
+<function>PGescapeString</> returns the number of characters written
+to <parameter>to</>, not including the terminating NUL character.
+Behavior is undefined when the <parameter>to</> and <parameter>from</>
+strings overlap.
+</para>
+
 <sect2 id="libpq-exec-select-info">
   <title>Retrieving SELECT Result Information</title>
 
Index: fe-exec.c
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.107
diff -u -r1.107 fe-exec.c
--- fe-exec.c   2001/08/17 15:11:15     1.107
+++ fe-exec.c   2001/08/22 15:58:40
@@ -56,6 +56,69 @@
 static int     getNotify(PGconn *conn);
 static int     getNotice(PGconn *conn);
 
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\", "\0" with "\\0", "'" with "\\'", and 
+ * "\"" with "\\\"".  length is the length of the buffer pointed to by
+ * from.  The buffer at to must be at least 2*length + 1 characters
+ * long.  A terminating NUL character is written.
+ * ---------------
+ */
+
+size_t
+PGescapeString (char *to, const char *from, size_t length)
+{
+       const char *source = from;
+       char *target = to;
+       unsigned int remaining = length;
+
+       while (remaining > 0) {
+               switch (*source) {
+               case '\0':
+                       *target = '\\';
+                       target++;
+                       *target = '0';
+                       /* target and remaining are updated below. */
+                       break;
+                       
+               case '\\':
+                       *target = '\\';
+                       target++;
+                       *target = '\\';
+                       /* target and remaining are updated below. */
+                       break;
+
+               case '\'':
+                       *target = '\\';
+                       target++;
+                       *target = '\'';
+                       /* target and remaining are updated below. */
+                       break;
+
+               case '"':
+                       *target = '\\';
+                       target++;
+                       *target = '"';
+                       /* target and remaining are updated below. */
+                       break;
+               
+               default:
+                       *target = *source;
+                       /* target and remaining are updated below. */
+               }
+               source++;
+               target++;
+               remaining--;
+       }
+
+       /* Write the terminating NUL character. */
+       *target = '\0';
+       
+       return target - to;
+}
+
+
 
 /* ----------------
  * Space management for PGresult.
Index: libpq-fe.h
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.71
diff -u -r1.71 libpq-fe.h
--- libpq-fe.h  2001/03/22 04:01:27     1.71
+++ libpq-fe.h  2001/08/22 15:59:02
@@ -240,6 +240,9 @@
 
 /* === in fe-exec.c === */
 
+       /* Quoting strings before inclusion in queries. */
+       extern size_t PGescapeString (char *to, const char *from, size_t length);
+
        /* Simple synchronous query */
        extern PGresult *PQexec(PGconn *conn, const char *query);
        extern PGnotify *PQnotifies(PGconn *conn);

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to