in standard Ajax, where
and order by
SQL clauses are provided by the program (not user), eg
var url = ".select?dd=emp&where="+escape("emp_tp='abc' and hire_dt<current_date-'2 years' and super_emp_id is distinct from emp_id")
answered on the server by
$where = (isset($_GET['where'])) ? pureClause($_GET['where']) : null;
$order = (isset($_GET['order'])) ? pureClause($_GET['order']) : null;
...
$query = $query.(($where)?" where $where":'').(($order)?" order by $order":'');
the question is what should function pureClause
look like?
right now pureClause
simply raises error if any of the following exist:
; select insert update delete drop create truncate
if other injection causes query failure, that's fine, as long as data undamaged.
to me this seems adequate, but in my heart, I know I'm wrong.
Clarifications:
- prepared statements in Postgres, although very fast, are a pain to set up and maintain - they're ok for well used queries but not custom queries.
- creating a prepared statement for each transaction is a huge db hit. much preferred if security can be attained in at the app level.
Lastly, consider the where clause
emp_tp='abc' and hire_dt=current_dt-'2 years' and super_emp_id is distinct from emp_id
how many placeholders here? this needs to be parsed correctly before being fed into a prepared statement with placeholders, right? or am I completely missing the boat?
Primary facts:
- not practical to write a SQL clause parser for parameterized prepared statements
- not practical to write a SQL clause sanitizer that guarantees no harm
Solution:
for SELECTS, where the random SQL can be a problem: since it's too hard to protect the database, let the database protect itself! have different users have different roles / permissions. use a read-only user for selects. for normal SQL, this guarantees no DML from these statements.
best practices: four db user accesses
developer
, do everything (never use as connection in web app)dml
- can select / dml on almost everything (must use for dml)read
- can select (use for all selects, whether prepared or text)login
- can only execute login/password functions (used in login process)
password protection:
dml
andread
may not access password data, either through select or dmllogin
should access password data only through protected functions, eg,
function login( username, password ) - returns user_id function set_password( usr_id, password ) - sets password
- only
login
may run thelogin()
andset_password()
functions - depending on your database,
login
may need sql access to password columns - depending on your database, the
password
column may be protected itself; if not, then should be moved out of theuser
table into its own secure table
setting this up in mysql
, using the administrator tool, took about 30 minutes, including time to write the login functions and split out the password column.