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:
dmlandreadmay not access password data, either through select or dmlloginshould access password data only through protected functions, eg,
function login( username, password ) - returns user_id
function set_password( usr_id, password ) - sets password
- only
loginmay run thelogin()andset_password()functions - depending on your database,
loginmay need sql access to password columns - depending on your database, the
passwordcolumn may be protected itself; if not, then should be moved out of theusertable 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.