我尝试执行原始SQL查询并根据用户输入安全地通过/ asc / desc传递订单。这是分页数据网格的后端。我不能为我的生活弄清楚如何安全地做到这一点。参数将转换为字符串,以便Oracle无法执行查询。我无法在互联网上找到任何这方面的例子。安全完成此任务的最佳方法是什么? (我不使用ORM,必须是原始的sql)。
我的解决方法是将ASC / DESC设置为我设置的变量。这很好,很安全。但是,如何将列名绑定到ORDER BY?这甚至可能吗?我可以将一堆列列入白名单并执行类似于ASC / DESC的操作。我只是好奇是否有办法绑定它。感谢。
@default.route('/api/barcodes/<sort_by>/<sort_dir>', methods=['GET'])
@json_enc
def fetch_barcodes(sort_by, sort_dir):
#time.sleep(5)
# Can't use sort_dir as a parameter, so assign to variable to sanitize it
ord_dir = "DESC" if sort_dir.lower() == 'desc' else 'ASC'
records = []
stmt = text("SELECT bb_request_id,bb_barcode,bs_status, "
"TO_CHAR(bb_rec_cre_date, 'MM/DD/YYYY') AS bb_rec_cre_date "
"FROM bars_barcodes,bars_status "
"WHERE bs_status_id = bb_status_id "
"ORDER BY :ord_by :ord_dir ")
stmt = stmt.bindparams(ord_by=sort_by,ord_dir=ord_dir)
rs = db.session.execute(stmt)
records = [dict(zip(rs.keys(), row)) for row in rs]
DatabaseError:(cx_Oracle.DatabaseError)ORA-01036:非法变量名称/编号 [SQL:&#34; SELECT bb_request_id,bb_barcode,bs_status,TO_CHAR(bb_rec_cre_date,&#39; MM / DD / YYYY&#39;)AS bb_rec_cre_date FROM bars_barcodes,bars_status WHERE bs_status_id = bb_status_id ORDER BY:ord_by:ord_dir&#34 ;] [参数:{&#39; ord_by&#39;:你&#39; bb_rec_cre_date&#39;,&#39; ord_dir&#39;:&#39; ASC&#39;}}
根据接受的答案更新解决方案:
def fetch_barcodes(sort_by, sort_dir, page, rows_per_page):
ord_dir_func = desc if sort_dir.lower() == 'desc' else asc
query_limit = int(rows_per_page)
query_offset = (int(page) - 1) * query_limit
stmt = select([column('bb_request_id'),
column('bb_barcode'),
column('bs_status'),
func.to_char(column('bb_rec_cre_date'), 'MM/DD/YYYY').label('bb_rec_cre_date')]).\
select_from(table('bars_barcode')).\
select_from(table('bars_status')).\
where(column('bs_status_id') == column('bb_status_id')).\
order_by(ord_dir_func(column(sort_by))).\
limit(query_limit).offset(query_offset)
result = db.session.execute(stmt)
records = [dict(row) for row in result]
response = json_return()
response.addRecords(records)
#response.setTotal(len(records))
response.setTotal(1001)
response.setSuccess(True)
response.addMessage("Records retrieved successfully. Limit: " + str(query_limit) + ", Offset: " + str(query_offset) + " SQL: " + str(stmt))
return response
答案 0 :(得分:1)
您可以使用Core和[{3}}等table()
构造来代替原始SQL字符串。在这方面,这会让你的生活更轻松:
from sqlalchemy import select, table, column, asc, desc
ord_dir = desc if sort_dir.lower() == 'desc' else asc
stmt = select([column('bb_request_id'),
column('bb_barcode'),
column('bs_status'),
func.to_char(column('bb_rec_cre_date'),
'MM/DD/YYYY').label('bb_rec_cre_date')]).\
select_from(table('bars_barcodes')).\
select_from(table('bars_status')).\
where(column('bs_status_id') == column('bb_status_id')).\
order_by(ord_dir(column(sort_by)))
table()
和column()
代表完整版Table
对象Column
的语法部分,可以这种方式用于逃避目的:
假设
column()
处理的文本的处理方式与数据库列的名称相同;如果字符串包含混合大小写,特殊字符或匹配目标后端上的已知保留字,则列表达式将使用后端确定的引用行为进行呈现。
但是,白名单可能并不是一个坏主意。
请注意,您无需手动zip()
行代理即可生成字典。它们按原样用作映射,如果出于序列化原因需要dict()
,请执行dict(row)
。